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