summaryrefslogtreecommitdiffstats
path: root/apps/files/src/views/Sidebar.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/views/Sidebar.vue')
-rw-r--r--apps/files/src/views/Sidebar.vue345
1 files changed, 345 insertions, 0 deletions
diff --git a/apps/files/src/views/Sidebar.vue b/apps/files/src/views/Sidebar.vue
new file mode 100644
index 00000000000..9a00df17377
--- /dev/null
+++ b/apps/files/src/views/Sidebar.vue
@@ -0,0 +1,345 @@
+<!--
+ - @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @author John Molakvoæ <skjnldsv@protonmail.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <AppSidebar
+ v-if="file"
+ ref="sidebar"
+ v-bind="appSidebar"
+ @close="onClose"
+ @update:starred="toggleStarred"
+ @[defaultActionListener].stop.prevent="onDefaultAction">
+ <!-- TODO: create a standard to allow multiple elements here? -->
+ <template v-if="fileInfo" #primary-actions>
+ <LegacyView v-for="view in views"
+ :key="view.cid"
+ :component="view"
+ :file-info="fileInfo" />
+ </template>
+
+ <!-- Error display -->
+ <div v-if="error" class="emptycontent">
+ <div class="icon-error" />
+ <h2>{{ error }}</h2>
+ </div>
+
+ <!-- If fileInfo fetch is complete, display tabs -->
+ <template v-for="tab in tabs" v-else-if="fileInfo">
+ <component
+ :is="tabComponent(tab).is"
+ v-if="canDisplay(tab)"
+ :key="tab.id"
+ :component="tabComponent(tab).component"
+ :name="tab.name"
+ :file-info="fileInfo" />
+ </template>
+ </AppSidebar>
+</template>
+<script>
+import $ from 'jquery'
+import axios from '@nextcloud/axios'
+import AppSidebar from 'nextcloud-vue/dist/Components/AppSidebar'
+import FileInfo from '../services/FileInfo'
+import LegacyTab from '../components/LegacyTab'
+import LegacyView from '../components/LegacyView'
+
+export default {
+ name: 'Sidebar',
+
+ components: {
+ AppSidebar,
+ LegacyView
+ },
+
+ data() {
+ return {
+ // reactive state
+ Sidebar: OCA.Files.Sidebar.state,
+ error: null,
+ fileInfo: null,
+ starLoading: false
+ }
+ },
+
+ computed: {
+ /**
+ * Current filename
+ * This is bound to the Sidebar service and
+ * is used to load a new file
+ * @returns {string}
+ */
+ file() {
+ return this.Sidebar.file
+ },
+
+ /**
+ * List of all the registered tabs
+ * @returns {Array}
+ */
+ tabs() {
+ return this.Sidebar.tabs
+ },
+
+ /**
+ * List of all the registered views
+ * @returns {Array}
+ */
+ views() {
+ return this.Sidebar.views
+ },
+
+ /**
+ * Current user dav root path
+ * @returns {string}
+ */
+ davPath() {
+ const user = OC.getCurrentUser().uid
+ return OC.linkToRemote(`dav/files/${user}${encodeURIComponent(this.file)}`)
+ },
+
+ /**
+ * Current active tab handler
+ * @param {string} id the tab id to set as active
+ * @returns {string} the current active tab
+ */
+ activeTab: {
+ get: function() {
+ return this.Sidebar.activeTab
+ },
+ set: function(id) {
+ OCA.Files.Sidebar.activeTab = id
+ }
+ },
+
+ /**
+ * Sidebar subtitle
+ * @returns {string}
+ */
+ subtitle() {
+ return `${this.size}, ${this.time}`
+ },
+
+ /**
+ * File last modified formatted string
+ * @returns {string}
+ */
+ time() {
+ return OC.Util.relativeModifiedDate(this.fileInfo.mtime)
+ },
+
+ /**
+ * File size formatted string
+ * @returns {string}
+ */
+ size() {
+ return OC.Util.humanFileSize(this.fileInfo.size)
+ },
+
+ /**
+ * File background/figure to illustrate the sidebar header
+ * @returns {string}
+ */
+ background() {
+ return this.getPreviewIfAny(this.fileInfo)
+ },
+
+ /**
+ * App sidebar v-binding object
+ *
+ * @returns {Object}
+ */
+ appSidebar() {
+ if (this.fileInfo) {
+ return {
+ background: this.background,
+ active: this.activeTab,
+ class: { 'has-preview': this.fileInfo.hasPreview },
+ compact: !this.fileInfo.hasPreview,
+ 'star-loading': this.starLoading,
+ starred: this.fileInfo.isFavourited,
+ subtitle: this.subtitle,
+ title: this.fileInfo.name
+ }
+ } else if (this.error) {
+ return {
+ key: 'error', // force key to re-render
+ subtitle: '',
+ title: ''
+ }
+ } else {
+ return {
+ class: 'icon-loading',
+ subtitle: '',
+ title: ''
+ }
+ }
+ },
+
+ /**
+ * Default action object for the current file
+ *
+ * @returns {Object}
+ */
+ defaultAction() {
+ return this.fileInfo
+ && OCA.Files && OCA.Files.App && OCA.Files.App.fileList
+ && OCA.Files.App.fileList
+ .fileActions.getDefaultFileAction(this.fileInfo.mimetype, this.fileInfo.type, OC.PERMISSION_READ)
+
+ },
+
+ /**
+ * Dynamic header click listener to ensure
+ * nothing is listening for a click if there
+ * is no default action
+ *
+ * @returns {string|null}
+ */
+ defaultActionListener() {
+ return this.defaultAction ? 'figure-click' : null
+ }
+ },
+
+ 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.sidebar) {
+ this.$refs.sidebar.updateTabs()
+ }
+ })
+ } catch (error) {
+ this.error = t('files', 'Error while loading the file data')
+ console.error('Error while loading the file data')
+ }
+ }
+ }
+ },
+
+ methods: {
+ /**
+ * Can this tab be displayed ?
+ *
+ * @param {Object} tab a registered tab
+ * @returns {boolean}
+ */
+ canDisplay(tab) {
+ if (tab.isLegacyTab) {
+ return this.fileInfo && tab.component.canDisplay && tab.component.canDisplay(this.fileInfo)
+ }
+ // if the tab does not have an enabled method, we assume it's always available
+ return tab.enabled ? tab.enabled(this.fileInfo) : true
+ },
+ onClose() {
+ this.resetData()
+ OCA.Files.Sidebar.file = ''
+ },
+ resetData() {
+ this.error = null
+ this.fileInfo = null
+ this.$nextTick(() => {
+ if (this.$refs.sidebar) {
+ this.$refs.sidebar.updateTabs()
+ }
+ })
+ },
+ getPreviewIfAny(fileInfo) {
+ if (fileInfo.hasPreview) {
+ return OC.generateUrl(`/core/preview?fileId=${fileInfo.id}&x=${screen.width}&y=${screen.height}&a=true`)
+ }
+ return OCA.Files.App.fileList._getIconUrl(fileInfo)
+ },
+
+ tabComponent(tab) {
+ if (tab.isLegacyTab) {
+ return {
+ is: LegacyTab,
+ component: tab.component
+ }
+ }
+ return {
+ is: tab.component
+ }
+ },
+
+ /**
+ * Toggle favourite state
+ * TODO: better implementation
+ *
+ * @param {Boolean} state favourited or not
+ */
+ async toggleStarred(state) {
+ try {
+ this.starLoading = true
+ await axios({
+ method: 'PROPPATCH',
+ url: this.davPath,
+ data: `<?xml version="1.0"?>
+ <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
+ ${state ? '<d:set>' : '<d:remove>'}
+ <d:prop>
+ <oc:favorite>1</oc:favorite>
+ </d:prop>
+ ${state ? '</d:set>' : '</d:remove>'}
+ </d:propertyupdate>`
+ })
+ } catch (error) {
+ OC.Notification.showTemporary(t('files', 'Unable to change the favourite state of the file'))
+ console.error('Unable to change favourite state', error)
+ }
+ this.starLoading = false
+ },
+
+ onDefaultAction() {
+ if (this.defaultAction) {
+ // generate fake context
+ this.defaultAction.action(this.fileInfo.name, {
+ fileInfo: this.fileInfo,
+ dir: this.fileInfo.dir,
+ fileList: OCA.Files.App.fileList,
+ $file: $('body')
+ })
+ }
+ }
+ }
+}
+</script>
+<style lang="scss" scoped>
+#app-sidebar {
+ &.has-preview::v-deep .app-sidebar-header__figure {
+ background-size: cover;
+ }
+}
+</style>