summaryrefslogtreecommitdiffstats
path: root/apps/files/src
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src')
-rw-r--r--apps/files/src/components/LegacyTab.vue89
-rw-r--r--apps/files/src/components/LegacyView.vue59
-rw-r--r--apps/files/src/models/Tab.js59
-rw-r--r--apps/files/src/services/FileInfo.js67
-rw-r--r--apps/files/src/services/Sidebar.js109
-rw-r--r--apps/files/src/sidebar.js59
-rw-r--r--apps/files/src/views/Sidebar.vue345
7 files changed, 787 insertions, 0 deletions
diff --git a/apps/files/src/components/LegacyTab.vue b/apps/files/src/components/LegacyTab.vue
new file mode 100644
index 00000000000..9a85ee7f073
--- /dev/null
+++ b/apps/files/src/components/LegacyTab.vue
@@ -0,0 +1,89 @@
+<!--
+ - @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>
+ <AppSidebarTab :icon="icon"
+ :name="name"
+ :active-tab="activeTab" />
+</template>
+<script>
+import AppSidebarTab from 'nextcloud-vue/dist/Components/AppSidebarTab'
+
+export default {
+ name: 'LegacyTab',
+ components: {
+ AppSidebarTab: AppSidebarTab
+ },
+ props: {
+ component: {
+ type: Object,
+ required: true
+ },
+ name: {
+ type: String,
+ default: '',
+ required: true
+ },
+ fileInfo: {
+ type: Object,
+ default: () => {},
+ required: true
+ }
+ },
+ computed: {
+ icon() {
+ return this.component.getIcon()
+ },
+ id() {
+ // copied from AppSidebarTab
+ return this.name.toLowerCase().replace(/ /g, '-')
+ },
+ order() {
+ return this.component.order
+ ? this.component.order
+ : 0
+ },
+ // needed because AppSidebarTab also uses $parent.activeTab
+ activeTab() {
+ return this.$parent.activeTab
+ }
+ },
+ watch: {
+ activeTab(activeTab) {
+ if (activeTab === this.id && this.fileInfo) {
+ this.setFileInfo(this.fileInfo)
+ }
+ }
+ },
+ mounted() {
+ // append the backbone element and set the FileInfo
+ this.component.$el.appendTo(this.$el)
+ },
+ methods: {
+ setFileInfo(fileInfo) {
+ this.component.setFileInfo(new OCA.Files.FileInfoModel(fileInfo))
+ }
+ }
+}
+</script>
+<style>
+</style>
diff --git a/apps/files/src/components/LegacyView.vue b/apps/files/src/components/LegacyView.vue
new file mode 100644
index 00000000000..e4a07ac3e5e
--- /dev/null
+++ b/apps/files/src/components/LegacyView.vue
@@ -0,0 +1,59 @@
+<!--
+ - @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>
+ <div />
+</template>
+<script>
+export default {
+ name: 'LegacyView',
+ props: {
+ component: {
+ type: Object,
+ required: true
+ },
+ fileInfo: {
+ type: Object,
+ default: () => {},
+ required: true
+ }
+ },
+ watch: {
+ fileInfo(fileInfo) {
+ // update the backbone model FileInfo
+ this.setFileInfo(fileInfo)
+ }
+ },
+ mounted() {
+ // append the backbone element and set the FileInfo
+ this.component.$el.replaceAll(this.$el)
+ this.setFileInfo(this.fileInfo)
+ },
+ methods: {
+ setFileInfo(fileInfo) {
+ this.component.setFileInfo(new OCA.Files.FileInfoModel(fileInfo))
+ }
+ }
+}
+</script>
+<style>
+</style>
diff --git a/apps/files/src/models/Tab.js b/apps/files/src/models/Tab.js
new file mode 100644
index 00000000000..28902b0e754
--- /dev/null
+++ b/apps/files/src/models/Tab.js
@@ -0,0 +1,59 @@
+/**
+ * @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/>.
+ *
+ */
+
+export default class Tab {
+
+ #component;
+ #legacy;
+ #name;
+
+ /**
+ * Create a new tab instance
+ *
+ * @param {string} name the name of this tab
+ * @param {Object} component the vue component
+ * @param {boolean} [legacy] is this a legacy tab
+ */
+ constructor(name, component, legacy) {
+ this.#name = name
+ this.#component = component
+ this.#legacy = legacy === true
+
+ if (this.#legacy) {
+ console.warn('Legacy tabs are deprecated! They will be removed in nextcloud 20.')
+ }
+
+ }
+
+ get name() {
+ return this.#name
+ }
+
+ get component() {
+ return this.#component
+ }
+
+ get isLegacyTab() {
+ return this.#legacy === true
+ }
+
+}
diff --git a/apps/files/src/services/FileInfo.js b/apps/files/src/services/FileInfo.js
new file mode 100644
index 00000000000..aa026df1445
--- /dev/null
+++ b/apps/files/src/services/FileInfo.js
@@ -0,0 +1,67 @@
+/**
+ * @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/>.
+ *
+ */
+
+import axios from '@nextcloud/axios'
+
+export default async function(url) {
+ const response = await axios({
+ method: 'PROPFIND',
+ url,
+ data: `<?xml version="1.0"?>
+ <d:propfind xmlns:d="DAV:"
+ xmlns:oc="http://owncloud.org/ns"
+ xmlns:nc="http://nextcloud.org/ns"
+ xmlns:ocs="http://open-collaboration-services.org/ns">
+ <d:prop>
+ <d:getlastmodified />
+ <d:getetag />
+ <d:getcontenttype />
+ <d:resourcetype />
+ <oc:fileid />
+ <oc:permissions />
+ <oc:size />
+ <d:getcontentlength />
+ <nc:has-preview />
+ <nc:mount-type />
+ <nc:is-encrypted />
+ <ocs:share-permissions />
+ <oc:tags />
+ <oc:favorite />
+ <oc:comments-unread />
+ <oc:owner-id />
+ <oc:owner-display-name />
+ <oc:share-types />
+ </d:prop>
+ </d:propfind>`
+ })
+
+ // TODO: create new parser or use cdav-lib when available
+ const file = OCA.Files.App.fileList.filesClient._client.parseMultiStatus(response.data)
+ // TODO: create new parser or use cdav-lib when available
+ const fileInfo = OCA.Files.App.fileList.filesClient._parseFileInfo(file[0])
+
+ // TODO remove when no more legacy backbone is used
+ fileInfo.get = (key) => fileInfo[key]
+ fileInfo.isDirectory = () => fileInfo.mimetype === 'httpd/unix-directory'
+
+ return fileInfo
+}
diff --git a/apps/files/src/services/Sidebar.js b/apps/files/src/services/Sidebar.js
new file mode 100644
index 00000000000..8f02a1b51ab
--- /dev/null
+++ b/apps/files/src/services/Sidebar.js
@@ -0,0 +1,109 @@
+/**
+ * @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/>.
+ *
+ */
+
+export default class Sidebar {
+
+ #state;
+ #view;
+
+ constructor() {
+ // init empty state
+ this.#state = {}
+
+ // init default values
+ this.#state.tabs = []
+ this.#state.views = []
+ this.#state.file = ''
+ this.#state.activeTab = ''
+ console.debug('OCA.Files.Sidebar initialized')
+ }
+
+ /**
+ * Get the sidebar state
+ *
+ * @readonly
+ * @memberof Sidebar
+ * @returns {Object} the data state
+ */
+ get state() {
+ return this.#state
+ }
+
+ /**
+ * @memberof Sidebar
+ * Register a new tab view
+ *
+ * @param {Object} tab a new unregistered tab
+ * @memberof Sidebar
+ * @returns {Boolean}
+ */
+ registerTab(tab) {
+ const hasDuplicate = this.#state.tabs.findIndex(check => check.name === tab.name) > -1
+ if (!hasDuplicate) {
+ this.#state.tabs.push(tab)
+ return true
+ }
+ console.error(`An tab with the same name ${tab.name} already exists`, tab)
+ return false
+ }
+
+ registerSecondaryView(view) {
+ const hasDuplicate = this.#state.views.findIndex(check => check.cid === view.cid) > -1
+ if (!hasDuplicate) {
+ this.#state.views.push(view)
+ return true
+ }
+ console.error(`A similar view already exists`, view)
+ return false
+ }
+
+ /**
+ * Set the current sidebar file data
+ *
+ * @param {string} path the file path to load
+ * @memberof Sidebar
+ */
+ set file(path) {
+ this.#state.file = path
+ }
+
+ /**
+ * Set the current sidebar file data
+ *
+ * @returns {String} the current opened file
+ * @memberof Sidebar
+ */
+ get file() {
+ return this.#state.file
+ }
+
+ /**
+ * Set the current sidebar tab
+ *
+ * @param {string} id the tab unique id
+ * @memberof Sidebar
+ */
+ set activeTab(id) {
+ this.#state.activeTab = id
+ }
+
+}
diff --git a/apps/files/src/sidebar.js b/apps/files/src/sidebar.js
new file mode 100644
index 00000000000..b508e8aee20
--- /dev/null
+++ b/apps/files/src/sidebar.js
@@ -0,0 +1,59 @@
+/**
+ * @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/>.
+ *
+ */
+
+import Vue from 'vue'
+import SidebarView from './views/Sidebar.vue'
+import Sidebar from './services/Sidebar'
+import Tab from './models/Tab'
+import VueClipboard from 'vue-clipboard2'
+
+Vue.use(VueClipboard)
+
+Vue.prototype.t = t
+
+window.addEventListener('DOMContentLoaded', () => {
+ // Init Sidebar Service
+ if (window.OCA && window.OCA.Files) {
+ Object.assign(window.OCA.Files, { Sidebar: new Sidebar() })
+ Object.assign(window.OCA.Files.Sidebar, { Tab })
+ }
+
+ // Make sure we have a proper layout
+ if (document.getElementById('content')) {
+
+ // Make sure we have a mountpoint
+ if (!document.getElementById('app-sidebar')) {
+ var contentElement = document.getElementById('content')
+ var sidebarElement = document.createElement('div')
+ sidebarElement.id = 'app-sidebar'
+ contentElement.appendChild(sidebarElement)
+ }
+ }
+
+ // Init vue app
+ const AppSidebar = new Vue({
+ // eslint-disable-next-line vue/match-component-file-name
+ name: 'SidebarRoot',
+ render: h => h(SidebarView)
+ })
+ AppSidebar.$mount('#app-sidebar')
+})
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>