Browse Source

Upgrade lifecycle and vue parent context

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Signed-off-by: npmbuildbot[bot] <npmbuildbot[bot]@users.noreply.github.com>
tags/v21.0.0beta1
John Molakvoæ (skjnldsv) 3 years ago
parent
commit
4de6e80771
No account linked to committer's email address

+ 1
- 1
apps/files/js/dist/files-app-settings.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files/js/dist/files-app-settings.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files/js/dist/personal-settings.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files/js/dist/personal-settings.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files/js/dist/sidebar.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files/js/dist/sidebar.js.map
File diff suppressed because it is too large
View File


+ 8
- 2
apps/files/js/filelist.js View File

@@ -3704,11 +3704,17 @@
id: tabView.id,
name: tabView.getLabel(),
icon: tabView.getIcon(),
render: function(el, fileInfo) {
mount: function(el, fileInfo) {
tabView.setFileInfo(fileInfo)
el.appendChild(tabView.el)
},
enabled,
update: function(fileInfo) {
tabView.setFileInfo(fileInfo)
},
destroy: function() {
tabView.el.remove()
},
enabled: enabled
}))
}
},

+ 44
- 12
apps/files/src/components/SidebarTab.vue View File

@@ -23,21 +23,28 @@
<template>
<AppSidebarTab
:id="id"
ref="tab"
:name="name"
:icon="icon">
<!-- Fallback loading -->
<EmptyContent v-if="loading" icon="icon-loading" />

<!-- Using a dummy div as Vue mount replace the element directly
It does NOT append to the content -->
<div ref="mount"></div>
<div ref="mount" />
</AppSidebarTab>
</template>

<script>
import AppSidebarTab from '@nextcloud/vue/dist/Components/AppSidebarTab'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'

export default {
name: 'SidebarTab',

components: {
AppSidebarTab,
EmptyContent,
},

props: {
@@ -58,36 +65,61 @@ export default {
type: String,
required: true,
},
render: {

/**
* Lifecycle methods.
* They are prefixed with `on` to avoid conflict with Vue
* methods like this.destroy
*/
onMount: {
type: Function,
required: true,
},
onUpdate: {
type: Function,
required: true,
},
onDestroy: {
type: Function,
required: true,
},
},

data() {
return {
loading: true,
}
},

computed: {
// TODO: implement a better way to force pass a prop fromm Sidebar
// TODO: implement a better way to force pass a prop from Sidebar
activeTab() {
return this.$parent.activeTab
},
},

watch: {
fileInfo(newFile, oldFile) {
async fileInfo(newFile, oldFile) {
// Update fileInfo on change
if (newFile.id !== oldFile.id) {
this.mountTab()
this.loading = true
await this.onUpdate(this.fileInfo)
this.loading = false
}
},
},

mounted() {
this.mountTab()
async mounted() {
this.loading = true
// Mount the tab: mounting point, fileInfo, vue context
await this.onMount(this.$refs.mount, this.fileInfo, this.$refs.tab)
this.loading = false
},

methods: {
mountTab() {
// Mount the tab into this component
this.render(this.$refs.mount, this.fileInfo)
},
async beforeDestroy() {
// unmount the tab
await this.onDestroy()
},

}
</script>

+ 28
- 8
apps/files/src/models/Tab.js View File

@@ -25,7 +25,9 @@ export default class Tab {
#id
#name
#icon
#render
#mount
#update
#destroy
#enabled

/**
@@ -35,10 +37,12 @@ export default class Tab {
* @param {string} options.id the unique id of this tab
* @param {string} options.name the translated tab name
* @param {string} options.icon the vue component
* @param {Function} options.render function to render the tab
* @param {Function} options.mount function to mount the tab
* @param {Function} options.update function to update the tab
* @param {Function} options.destroy function to destroy the tab
* @param {Function} [options.enabled] define conditions whether this tab is active. Must returns a boolean
*/
constructor({ id, name, icon, render, enabled }) {
constructor({ id, name, icon, mount, update, destroy, enabled } = {}) {
if (enabled === undefined) {
enabled = () => true
}
@@ -53,8 +57,14 @@ export default class Tab {
if (typeof icon !== 'string' || icon.trim() === '') {
throw new Error('The icon argument is not a valid string')
}
if (typeof render !== 'function') {
throw new Error('The render argument should be a function')
if (typeof mount !== 'function') {
throw new Error('The mount argument should be a function')
}
if (typeof update !== 'function') {
throw new Error('The update argument should be a function')
}
if (typeof destroy !== 'function') {
throw new Error('The destroy argument should be a function')
}
if (typeof enabled !== 'function') {
throw new Error('The enabled argument should be a function')
@@ -63,7 +73,9 @@ export default class Tab {
this.#id = id
this.#name = name
this.#icon = icon
this.#render = render
this.#mount = mount
this.#update = update
this.#destroy = destroy
this.#enabled = enabled

}
@@ -80,8 +92,16 @@ export default class Tab {
return this.#icon
}

get render() {
return this.#render
get mount() {
return this.#mount
}

get update() {
return this.#update
}

get destroy() {
return this.#destroy
}

get enabled() {

+ 2
- 0
apps/files/src/sidebar.js View File

@@ -21,6 +21,8 @@
*/

import Vue from 'vue'
import { translate as t } from '@nextcloud/l10n'

import SidebarView from './views/Sidebar.vue'
import Sidebar from './services/Sidebar'
import Tab from './models/Tab'

+ 32
- 49
apps/files/src/views/Sidebar.vue View File

@@ -52,33 +52,38 @@
</template>

<!-- Error display -->
<div v-if="error" class="emptycontent">
<div class="icon-error" />
<h2>{{ error }}</h2>
</div>
<EmptyContent v-if="error" icon="icon-error">
{{ error }}
</EmptyContent>

<!-- If fileInfo fetch is complete, display tabs -->
<template v-else-if="fileInfo" v-for="tab in tabs">
<!-- If fileInfo fetch is complete, render tabs -->
<template v-for="tab in tabs" v-else-if="fileInfo">
<!-- Hide them if we're loading another file but keep them mounted -->
<SidebarTab
v-if="tab.enabled(fileInfo)"
v-show="!loading"
:id="tab.id"
:key="tab.id"
:name="tab.name"
:icon="tab.icon"
:render="tab.render"
:on-mount="tab.mount"
:on-update="tab.update"
:on-destroy="tab.destroy"
:file-info="fileInfo" />
</template>
</AppSidebar>
</template>
<script>
import { encodePath } from '@nextcloud/paths'
import $ from 'jquery'
import axios from '@nextcloud/axios'
import AppSidebar from '@nextcloud/vue/dist/Components/AppSidebar'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'

import FileInfo from '../services/FileInfo'
import SidebarTab from '../components/SidebarTab'
import LegacyView from '../components/LegacyView'
import { encodePath } from '@nextcloud/paths'

export default {
name: 'Sidebar',
@@ -86,8 +91,9 @@ export default {
components: {
ActionButton,
AppSidebar,
SidebarTab,
EmptyContent,
LegacyView,
SidebarTab,
},

data() {
@@ -95,6 +101,7 @@ export default {
// reactive state
Sidebar: OCA.Files.Sidebar.state,
error: null,
loading: true,
fileInfo: null,
starLoading: false,
}
@@ -185,15 +192,16 @@ export default {
appSidebar() {
if (this.fileInfo) {
return {
background: this.background,
'data-mimetype': this.fileInfo.mimetype,
'star-loading': this.starLoading,
active: this.activeTab,
background: this.background,
class: { 'has-preview': this.fileInfo.hasPreview },
compact: !this.fileInfo.hasPreview,
'star-loading': this.starLoading,
loading: this.loading,
starred: this.fileInfo.isFavourited,
subtitle: this.subtitle,
title: this.fileInfo.name,
'data-mimetype': this.fileInfo.mimetype,
}
} else if (this.error) {
return {
@@ -201,12 +209,12 @@ export default {
subtitle: '',
title: '',
}
} else {
return {
class: 'icon-loading',
subtitle: '',
title: '',
}
}
// no fileInfo yet, showing empty data
return {
loading: this.loading,
subtitle: '',
title: '',
}
},

@@ -241,35 +249,6 @@ export default {
},
},

watch: {
// update the sidebar data
async file(curr, prev) {
this.resetData()
if (curr && curr.trim() !== '') {
try {
this.fileInfo = await FileInfo(this.davPath)
// adding this as fallback because other apps expect it
this.fileInfo.dir = this.file.split('/').slice(0, -1).join('/')

// DEPRECATED legacy views
// TODO: remove
this.views.forEach(view => {
view.setFileInfo(this.fileInfo)
})

this.$nextTick(() => {
if (this.$refs.tabs) {
this.$refs.tabs.updateTabs()
}
})
} catch (error) {
this.error = t('files', 'Error while loading the file data')
console.error('Error while loading the file data', error)
}
}
},
},

methods: {
/**
* Can this tab be displayed ?
@@ -403,9 +382,11 @@ export default {
// update current opened file
this.Sidebar.file = path

// reset previous data
this.resetData()
if (path && path.trim() !== '') {
// reset data, keep old fileInfo to not reload all tabs and just hide them
this.error = null
this.loading = true

try {
this.fileInfo = await FileInfo(this.davPath)
// adding this as fallback because other apps expect it
@@ -427,6 +408,8 @@ export default {
console.error('Error while loading the file data', error)

throw new Error(error)
} finally {
this.loading = false
}
}
},

+ 1
- 1
apps/files_sharing/js/dist/additionalScripts.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/additionalScripts.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/collaboration.js View File

@@ -1,2 +1,2 @@
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=152)}({152:function(e,n,r){r.p=OC.linkTo("files_sharing","js/dist/"),r.nc=btoa(OC.requestToken),window.OCP.Collaboration.registerType("file",{action:function(){return new Promise((function(e,n){OC.dialogs.filepicker(t("files_sharing","Link to a file"),(function(t){OC.Files.getClient().getFileInfo(t).then((function(n,t){e(t.id)})).fail((function(){n(new Error("Cannot get fileinfo"))}))}),!1,null,!1,OC.dialogs.FILEPICKER_TYPE_CHOOSE,"",{allowDirectoryChooser:!0})}))},typeString:t("files_sharing","Link to a file"),typeIconClass:"icon-files-dark"})}});
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="/js/",t(t.s=153)}({153:function(e,n,r){r.p=OC.linkTo("files_sharing","js/dist/"),r.nc=btoa(OC.requestToken),window.OCP.Collaboration.registerType("file",{action:function(){return new Promise((function(e,n){OC.dialogs.filepicker(t("files_sharing","Link to a file"),(function(t){OC.Files.getClient().getFileInfo(t).then((function(n,t){e(t.id)})).fail((function(){n(new Error("Cannot get fileinfo"))}))}),!1,null,!1,OC.dialogs.FILEPICKER_TYPE_CHOOSE,"",{allowDirectoryChooser:!0})}))},typeString:t("files_sharing","Link to a file"),typeIconClass:"icon-files-dark"})}});
//# sourceMappingURL=collaboration.js.map

+ 1
- 1
apps/files_sharing/js/dist/collaboration.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/files_sharing.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/files_sharing.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/files_sharing_tab.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/files_sharing_tab.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/main.js View File

@@ -1,2 +1,2 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/js/",r(r.s=277)}({277:function(e,t){Object.assign(OC,{Share:{SHARE_TYPE_USER:0,SHARE_TYPE_GROUP:1,SHARE_TYPE_LINK:3,SHARE_TYPE_EMAIL:4,SHARE_TYPE_REMOTE:6,SHARE_TYPE_CIRCLE:7,SHARE_TYPE_GUEST:8,SHARE_TYPE_REMOTE_GROUP:9,SHARE_TYPE_ROOM:10}})}});
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/js/",r(r.s=278)}({278:function(e,t){Object.assign(OC,{Share:{SHARE_TYPE_USER:0,SHARE_TYPE_GROUP:1,SHARE_TYPE_LINK:3,SHARE_TYPE_EMAIL:4,SHARE_TYPE_REMOTE:6,SHARE_TYPE_CIRCLE:7,SHARE_TYPE_GUEST:8,SHARE_TYPE_REMOTE_GROUP:9,SHARE_TYPE_ROOM:10}})}});
//# sourceMappingURL=main.js.map

+ 1
- 1
apps/files_sharing/js/dist/main.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/personal-settings.js
File diff suppressed because it is too large
View File


+ 1
- 1
apps/files_sharing/js/dist/personal-settings.js.map
File diff suppressed because it is too large
View File


+ 19
- 7
apps/files_sharing/src/files_sharing_tab.js View File

@@ -42,6 +42,7 @@ Vue.use(VueClipboard)

// Init Sharing tab component
const View = Vue.extend(SharingTab)
let TabInstance = null

window.addEventListener('DOMContentLoaded', function() {
if (OCA.Files && OCA.Files.Sidebar) {
@@ -50,13 +51,24 @@ window.addEventListener('DOMContentLoaded', function() {
name: t('files_sharing', 'Sharing'),
icon: 'icon-share',

render: (el, fileInfo) => {
new View({
propsData: {
fileInfo,
},
}).$mount(el)
console.info(el)
async mount(el, fileInfo, context) {
if (TabInstance) {
TabInstance.$destroy()
}
TabInstance = new View({
// Better integration with vue parent component
parent: context,
})
// Only mount after we have all the info we need
await TabInstance.update(fileInfo)
TabInstance.$mount(el)
},
update(fileInfo) {
TabInstance.update(fileInfo)
},
destroy() {
TabInstance.$destroy()
TabInstance = null
},
}))
}

+ 14
- 20
apps/files_sharing/src/views/SharingTab.vue View File

@@ -117,24 +117,20 @@ export default {

mixins: [ShareTypes],

props: {
fileInfo: {
type: Object,
default: () => {},
required: true,
},
},

data() {
return {
error: '',
expirationInterval: null,
loading: true,

fileInfo: null,

// reshare Share object
reshare: null,
sharedWithMe: {},
shares: [],
linkShares: [],

sections: OCA.Sharing.ShareTabSections.getSections(),
}
},
@@ -155,20 +151,17 @@ export default {
},
},

watch: {
fileInfo(newFile, oldFile) {
if (newFile.id !== oldFile.id) {
this.resetState()
this.getShares()
}
methods: {
/**
* Update current fileInfo and fetch new data
* @param {Object} fileInfo the current file FileInfo
*/
async update(fileInfo) {
this.fileInfo = fileInfo
this.resetState()
this.getShares()
},
},

beforeMount() {
this.getShares()
},

methods: {
/**
* Get the existing shares infos
*/
@@ -221,6 +214,7 @@ export default {
this.error = ''
this.sharedWithMe = {}
this.shares = []
this.linkShares = []
},

/**

Loading…
Cancel
Save