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>
This commit is contained in:
John Molakvoæ (skjnldsv) 2020-10-04 13:49:57 +02:00
parent 843d799a2e
commit 4de6e80771
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
25 changed files with 166 additions and 117 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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
}))
}
},

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>

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() {

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'

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
}
}
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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
},
}))
}

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()
}
},
},
beforeMount() {
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()
},
/**
* Get the existing shares infos
*/
@ -221,6 +214,7 @@ export default {
this.error = ''
this.sharedWithMe = {}
this.shares = []
this.linkShares = []
},
/**