Move Files Sidebar to proper javascript standard

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ (skjnldsv) 2020-10-04 00:30:50 +02:00
parent 678ef8466d
commit 843d799a2e
No known key found for this signature in database
GPG Key ID: 60C25B8C072916CF
7 changed files with 125 additions and 118 deletions

View File

@ -3700,7 +3700,16 @@
console.warn('registerTabView is deprecated! It will be removed in nextcloud 20.'); console.warn('registerTabView is deprecated! It will be removed in nextcloud 20.');
const enabled = tabView.canDisplay || undefined const enabled = tabView.canDisplay || undefined
if (tabView.id) { if (tabView.id) {
OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab(tabView.id, tabView, enabled, true)) OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
id: tabView.id,
name: tabView.getLabel(),
icon: tabView.getIcon(),
render: function(el, fileInfo) {
tabView.setFileInfo(fileInfo)
el.appendChild(tabView.el)
},
enabled,
}))
} }
}, },

View File

@ -1,3 +1,4 @@
<!-- <!--
- @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com> - @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
- -
@ -19,74 +20,74 @@
- along with this program. If not, see <http://www.gnu.org/licenses/>. - along with this program. If not, see <http://www.gnu.org/licenses/>.
- -
--> -->
<template> <template>
<AppSidebarTab <AppSidebarTab
:id="id" :id="id"
:icon="icon"
:name="name" :name="name"
:active-tab="activeTab" /> :icon="icon">
<!-- Using a dummy div as Vue mount replace the element directly
It does NOT append to the content -->
<div ref="mount"></div>
</AppSidebarTab>
</template> </template>
<script> <script>
import AppSidebarTab from '@nextcloud/vue/dist/Components/AppSidebarTab' import AppSidebarTab from '@nextcloud/vue/dist/Components/AppSidebarTab'
export default { export default {
name: 'LegacyTab', name: 'SidebarTab',
components: { components: {
AppSidebarTab, AppSidebarTab,
}, },
props: { props: {
component: { fileInfo: {
type: Object, type: Object,
default: () => {},
required: true, required: true,
}, },
id: { id: {
type: String, type: String,
required: true, required: true,
}, },
fileInfo: { name: {
type: Object, type: String,
default: () => {}, required: true,
},
icon: {
type: String,
required: true,
},
render: {
type: Function,
required: true, required: true,
}, },
}, },
computed: { computed: {
icon() { // TODO: implement a better way to force pass a prop fromm Sidebar
return this.component.getIcon()
},
name() {
return this.component.getLabel()
},
order() {
return this.component.order
? this.component.order
: 0
},
// needed because AppSidebarTab also uses $parent.activeTab
activeTab() { activeTab() {
return this.$parent.activeTab return this.$parent.activeTab
}, },
}, },
watch: { watch: {
fileInfo(fileInfo) { fileInfo(newFile, oldFile) {
if (fileInfo) { if (newFile.id !== oldFile.id) {
this.setFileInfo(fileInfo) this.mountTab()
} }
}, },
}, },
mounted() { mounted() {
// append the backbone element and set the FileInfo this.mountTab()
this.component.$el.appendTo(this.$el)
},
beforeDestroy() {
this.component.remove()
}, },
methods: { methods: {
setFileInfo(fileInfo) { mountTab() {
this.component.setFileInfo(new OCA.Files.FileInfoModel(fileInfo)) // Mount the tab into this component
this.render(this.$refs.mount, this.fileInfo)
}, },
}, },
} }
</script> </script>
<style>
</style>

View File

@ -22,32 +22,49 @@
export default class Tab { export default class Tab {
#component
#legacy
#id #id
#name
#icon
#render
#enabled #enabled
/** /**
* Create a new tab instance * Create a new tab instance
* *
* @param {string} id the unique id of this tab * @param {Object} options destructuring object
* @param {Object} component the vue component * @param {string} options.id the unique id of this tab
* @param {Function} [enabled] function that returns if the tab should be shown or not * @param {string} options.name the translated tab name
* @param {boolean} [legacy] is this a legacy tab * @param {string} options.icon the vue component
* @param {Function} options.render function to render the tab
* @param {Function} [options.enabled] define conditions whether this tab is active. Must returns a boolean
*/ */
constructor(id, component, enabled = () => true, legacy) { constructor({ id, name, icon, render, enabled }) {
if (enabled === undefined) {
enabled = () => true
}
// Sanity checks
if (typeof id !== 'string' || id.trim() === '') {
throw new Error('The id argument is not a valid string')
}
if (typeof name !== 'string' || name.trim() === '') {
throw new Error('The name argument is not a valid string')
}
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 enabled !== 'function') { if (typeof enabled !== 'function') {
throw new Error('The enabled argument should be a function') throw new Error('The enabled argument should be a function')
} }
this.#id = id this.#id = id
this.#component = component this.#name = name
this.#icon = icon
this.#render = render
this.#enabled = enabled this.#enabled = enabled
this.#legacy = legacy === true
if (this.#legacy) {
console.warn('Legacy tabs are deprecated! They will be removed in nextcloud 20.')
}
} }
@ -55,16 +72,20 @@ export default class Tab {
return this.#id return this.#id
} }
get component() { get name() {
return this.#component return this.#name
} }
get isEnabled() { get icon() {
return this.#icon
}
get render() {
return this.#render
}
get enabled() {
return this.#enabled return this.#enabled
} }
get isLegacyTab() {
return this.#legacy === true
}
} }

View File

@ -24,9 +24,6 @@ import Vue from 'vue'
import SidebarView from './views/Sidebar.vue' import SidebarView from './views/Sidebar.vue'
import Sidebar from './services/Sidebar' import Sidebar from './services/Sidebar'
import Tab from './models/Tab' import Tab from './models/Tab'
import VueClipboard from 'vue-clipboard2'
Vue.use(VueClipboard)
Vue.prototype.t = t Vue.prototype.t = t

View File

@ -58,15 +58,14 @@
</div> </div>
<!-- If fileInfo fetch is complete, display tabs --> <!-- If fileInfo fetch is complete, display tabs -->
<template v-for="tab in tabs" v-else-if="fileInfo"> <template v-else-if="fileInfo" v-for="tab in tabs">
<component <SidebarTab
:is="tabComponent(tab).is" v-if="tab.enabled(fileInfo)"
v-if="canDisplay(tab)"
:id="tab.id" :id="tab.id"
:key="tab.id" :key="tab.id"
:component="tabComponent(tab).component"
:name="tab.name" :name="tab.name"
:dav-path="davPath" :icon="tab.icon"
:render="tab.render"
:file-info="fileInfo" /> :file-info="fileInfo" />
</template> </template>
</AppSidebar> </AppSidebar>
@ -77,7 +76,7 @@ import axios from '@nextcloud/axios'
import AppSidebar from '@nextcloud/vue/dist/Components/AppSidebar' import AppSidebar from '@nextcloud/vue/dist/Components/AppSidebar'
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
import FileInfo from '../services/FileInfo' import FileInfo from '../services/FileInfo'
import LegacyTab from '../components/LegacyTab' import SidebarTab from '../components/SidebarTab'
import LegacyView from '../components/LegacyView' import LegacyView from '../components/LegacyView'
import { encodePath } from '@nextcloud/paths' import { encodePath } from '@nextcloud/paths'
@ -87,6 +86,7 @@ export default {
components: { components: {
ActionButton, ActionButton,
AppSidebar, AppSidebar,
SidebarTab,
LegacyView, LegacyView,
}, },
@ -258,8 +258,8 @@ export default {
}) })
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.sidebar) { if (this.$refs.tabs) {
this.$refs.sidebar.updateTabs() this.$refs.tabs.updateTabs()
} }
}) })
} catch (error) { } catch (error) {
@ -278,14 +278,14 @@ export default {
* @returns {boolean} * @returns {boolean}
*/ */
canDisplay(tab) { canDisplay(tab) {
return tab.isEnabled(this.fileInfo) return tab.enabled(this.fileInfo)
}, },
resetData() { resetData() {
this.error = null this.error = null
this.fileInfo = null this.fileInfo = null
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.sidebar) { if (this.$refs.tabs) {
this.$refs.sidebar.updateTabs() this.$refs.tabs.updateTabs()
} }
}) })
}, },
@ -327,18 +327,6 @@ export default {
return OC.MimeType.getIconUrl(mimeType) return OC.MimeType.getIconUrl(mimeType)
}, },
tabComponent(tab) {
if (tab.isLegacyTab) {
return {
is: LegacyTab,
component: tab.component,
}
}
return {
is: tab.component,
}
},
/** /**
* Set current active tab * Set current active tab
* *
@ -430,8 +418,8 @@ export default {
}) })
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.sidebar) { if (this.$refs.tabs) {
this.$refs.sidebar.updateTabs() this.$refs.tabs.updateTabs()
} }
}) })
} catch (error) { } catch (error) {

View File

@ -19,11 +19,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
import Vue from 'vue'
import VueClipboard from 'vue-clipboard2'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import SharingTab from './views/SharingTab' import SharingTab from './views/SharingTab'
import ShareSearch from './services/ShareSearch' import ShareSearch from './services/ShareSearch'
import ExternalLinkActions from './services/ExternalLinkActions' import ExternalLinkActions from './services/ExternalLinkActions'
import TabSections from './services/TabSections' import TabSections from './services/TabSections'
// Init Sharing Tab Service // Init Sharing Tab Service
@ -34,8 +36,28 @@ Object.assign(window.OCA.Sharing, { ShareSearch: new ShareSearch() })
Object.assign(window.OCA.Sharing, { ExternalLinkActions: new ExternalLinkActions() }) Object.assign(window.OCA.Sharing, { ExternalLinkActions: new ExternalLinkActions() })
Object.assign(window.OCA.Sharing, { ShareTabSections: new TabSections() }) Object.assign(window.OCA.Sharing, { ShareTabSections: new TabSections() })
Vue.prototype.t = t
Vue.prototype.n = n
Vue.use(VueClipboard)
// Init Sharing tab component
const View = Vue.extend(SharingTab)
window.addEventListener('DOMContentLoaded', function() { window.addEventListener('DOMContentLoaded', function() {
if (OCA.Files && OCA.Files.Sidebar) { if (OCA.Files && OCA.Files.Sidebar) {
OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab('sharing', SharingTab)) OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({
id: 'sharing',
name: t('files_sharing', 'Sharing'),
icon: 'icon-share',
render: (el, fileInfo) => {
new View({
propsData: {
fileInfo,
},
}).$mount(el)
console.info(el)
},
}))
} }
}) })

View File

@ -21,10 +21,7 @@
--> -->
<template> <template>
<Tab :id="id" <div :class="{ 'icon-loading': loading }">
:icon="icon"
:name="name"
:class="{ 'icon-loading': loading }">
<!-- error message --> <!-- error message -->
<div v-if="error" class="emptycontent"> <div v-if="error" class="emptycontent">
<div class="icon icon-error" /> <div class="icon icon-error" />
@ -84,7 +81,7 @@
<component :is="section($refs['section-'+index], fileInfo)" :file-info="fileInfo" /> <component :is="section($refs['section-'+index], fileInfo)" :file-info="fileInfo" />
</div> </div>
</template> </template>
</Tab> </div>
</template> </template>
<script> <script>
@ -92,7 +89,6 @@ import { CollectionList } from 'nextcloud-vue-collections'
import { generateOcsUrl } from '@nextcloud/router' import { generateOcsUrl } from '@nextcloud/router'
import Avatar from '@nextcloud/vue/dist/Components/Avatar' import Avatar from '@nextcloud/vue/dist/Components/Avatar'
import axios from '@nextcloud/axios' import axios from '@nextcloud/axios'
import Tab from '@nextcloud/vue/dist/Components/AppSidebarTab'
import { shareWithTitle } from '../utils/SharedWithMe' import { shareWithTitle } from '../utils/SharedWithMe'
import Share from '../models/Share' import Share from '../models/Share'
@ -117,7 +113,6 @@ export default {
SharingInput, SharingInput,
SharingLinkList, SharingLinkList,
SharingList, SharingList,
Tab,
}, },
mixins: [ShareTypes], mixins: [ShareTypes],
@ -134,9 +129,7 @@ export default {
return { return {
error: '', error: '',
expirationInterval: null, expirationInterval: null,
icon: 'icon-share',
loading: true, loading: true,
name: t('files_sharing', 'Sharing'),
// reshare Share object // reshare Share object
reshare: null, reshare: null,
sharedWithMe: {}, sharedWithMe: {},
@ -147,26 +140,6 @@ export default {
}, },
computed: { computed: {
/**
* Needed to differenciate the tabs
* pulled from the AppSidebarTab component
*
* @returns {string}
*/
id() {
return 'sharing'
},
/**
* Returns the current active tab
* needed because AppSidebarTab also uses $parent.activeTab
*
* @returns {string}
*/
activeTab() {
return this.$parent.activeTab
},
/** /**
* Is this share shared with me? * Is this share shared with me?
* *
@ -341,7 +314,3 @@ export default {
}, },
} }
</script> </script>
<style lang="scss" scoped>
</style>