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>
pull/23164/head
John Molakvoæ (skjnldsv) 5 years ago
parent
commit
4de6e80771
No known key found for this signature in database GPG Key ID: 60C25B8C072916CF
  1. 2
      apps/files/js/dist/files-app-settings.js
  2. 2
      apps/files/js/dist/files-app-settings.js.map
  3. 2
      apps/files/js/dist/personal-settings.js
  4. 2
      apps/files/js/dist/personal-settings.js.map
  5. 2
      apps/files/js/dist/sidebar.js
  6. 2
      apps/files/js/dist/sidebar.js.map
  7. 10
      apps/files/js/filelist.js
  8. 56
      apps/files/src/components/SidebarTab.vue
  9. 36
      apps/files/src/models/Tab.js
  10. 2
      apps/files/src/sidebar.js
  11. 81
      apps/files/src/views/Sidebar.vue
  12. 2
      apps/files_sharing/js/dist/additionalScripts.js
  13. 2
      apps/files_sharing/js/dist/additionalScripts.js.map
  14. 2
      apps/files_sharing/js/dist/collaboration.js
  15. 2
      apps/files_sharing/js/dist/collaboration.js.map
  16. 2
      apps/files_sharing/js/dist/files_sharing.js
  17. 2
      apps/files_sharing/js/dist/files_sharing.js.map
  18. 2
      apps/files_sharing/js/dist/files_sharing_tab.js
  19. 2
      apps/files_sharing/js/dist/files_sharing_tab.js.map
  20. 2
      apps/files_sharing/js/dist/main.js
  21. 2
      apps/files_sharing/js/dist/main.js.map
  22. 2
      apps/files_sharing/js/dist/personal-settings.js
  23. 2
      apps/files_sharing/js/dist/personal-settings.js.map
  24. 26
      apps/files_sharing/src/files_sharing_tab.js
  25. 34
      apps/files_sharing/src/views/SharingTab.vue

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

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

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

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

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

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

10
apps/files/js/filelist.js

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

56
apps/files/src/components/SidebarTab.vue

@ -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>

36
apps/files/src/models/Tab.js

@ -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
apps/files/src/sidebar.js

@ -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'

81
apps/files/src/views/Sidebar.vue

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

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

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

2
apps/files_sharing/js/dist/collaboration.js

@ -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

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

2
apps/files_sharing/js/dist/files_sharing.js
File diff suppressed because it is too large
View File

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

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

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

2
apps/files_sharing/js/dist/main.js

@ -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

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

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

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

26
apps/files_sharing/src/files_sharing_tab.js

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

34
apps/files_sharing/src/views/SharingTab.vue

@ -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