2019-05-23 18:03:04 +03:00
|
|
|
<!--
|
|
|
|
- @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"
|
2019-10-23 19:40:48 +03:00
|
|
|
@update:active="setActiveTab"
|
2019-05-23 18:03:04 +03:00
|
|
|
@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"
|
2019-10-23 19:40:48 +03:00
|
|
|
:dav-path="davPath"
|
2019-05-23 18:03:04 +03:00
|
|
|
: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
|
2019-10-09 08:53:30 +03:00
|
|
|
return OC.linkToRemote(`dav/files/${user}${this.file}`)
|
2019-05-23 18:03:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current active tab handler
|
|
|
|
* @param {string} id the tab id to set as active
|
|
|
|
* @returns {string} the current active tab
|
|
|
|
*/
|
2019-10-23 19:40:48 +03:00
|
|
|
activeTab() {
|
|
|
|
return this.Sidebar.activeTab
|
2019-05-23 18:03:04 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2019-10-23 19:40:48 +03:00
|
|
|
/**
|
|
|
|
* Set current active tab
|
|
|
|
*
|
|
|
|
* @param {string} id tab unique id
|
|
|
|
*/
|
|
|
|
setActiveTab(id) {
|
|
|
|
OCA.Files.Sidebar.activeTab = id
|
|
|
|
},
|
|
|
|
|
2019-05-23 18:03:04 +03:00
|
|
|
/**
|
|
|
|
* 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>`
|
|
|
|
})
|
2019-10-23 12:41:47 +03:00
|
|
|
|
|
|
|
// TODO: Obliterate as soon as possible and use events with new files app
|
|
|
|
// Terrible fallback for legacy files: toggle filelist as well
|
|
|
|
if (OCA.Files && OCA.Files.App && OCA.Files.App.fileList && OCA.Files.App.fileList.fileActions) {
|
|
|
|
OCA.Files.App.fileList.fileActions.triggerAction('Favorite', OCA.Files.App.fileList.getModelForFile(this.fileInfo.name), OCA.Files.App.fileList)
|
|
|
|
}
|
|
|
|
|
2019-05-23 18:03:04 +03:00
|
|
|
} 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>
|