From e75b0f91212349308912a5dcfd87252254da620b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Fri, 1 Mar 2019 11:43:12 +0100 Subject: [PATCH] Implement file actions menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) --- .eslintrc.js | 1 + package-lock.json | 19 ------ package.json | 3 +- src/main.js | 2 + src/services/FileList.js | 46 +++++++++++---- src/views/Viewer.vue | 121 ++++++++++++++++++++++++++++++--------- 6 files changed, 134 insertions(+), 58 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 188c60058..98f7dc5ab 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,6 +7,7 @@ module.exports = { jest: true }, globals: { + $: true, t: true, n: true, OC: true, diff --git a/package-lock.json b/package-lock.json index 162e70c75..dbe3cb5b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8877,11 +8877,6 @@ } } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", @@ -11292,20 +11287,6 @@ "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=", "dev": true }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 3c973928a..1dc4da8b0 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,7 @@ "mime-types": "^2.1.22", "nextcloud-server": "^0.15.9", "nextcloud-vue": "^0.8.0", - "vue": "^2.6.7", - "xml2js": "^0.4.19" + "vue": "^2.6.7" }, "browserslist": [ "last 2 versions", diff --git a/src/main.js b/src/main.js index 3c4369287..10955bbc1 100644 --- a/src/main.js +++ b/src/main.js @@ -25,6 +25,8 @@ import ViewerService from 'Services/Viewer' import { generateFilePath } from 'nextcloud-server/dist/router' +Vue.prototype.$ = $ + Vue.prototype.t = t Vue.prototype.n = n diff --git a/src/services/FileList.js b/src/services/FileList.js index 1860456be..6b004125d 100644 --- a/src/services/FileList.js +++ b/src/services/FileList.js @@ -21,7 +21,6 @@ */ import axios from 'axios' -import { parseString } from 'xml2js' /** * @@ -36,17 +35,42 @@ export default async function(user, path, mimes) { headers: { requesttoken: OC.requestToken, 'content-Type': 'text/xml' - } + }, + data: ` + + + + + + + + + + + + + + + + + + + + + + ` }) - let files = [] - await parseString(response.data, (error, data) => { - files = data['d:multistatus']['d:response'] - if (error) { - console.error(error) - } - }) - - return files.filter(file => file['d:propstat'][0]['d:prop'][0]['d:getcontenttype'] && mimes.indexOf(file['d:propstat'][0]['d:prop'][0]['d:getcontenttype'][0]) !== -1) + let files = OCA.Files.App.fileList.filesClient._client.parseMultiStatus(response.data) + return files + .map(file => { + const fileInfo = OCA.Files.App.fileList.filesClient._parseFileInfo(file) + fileInfo.href = file.href + return fileInfo + }) + .filter(file => file.mimetype && mimes.indexOf(file.mimetype) !== -1) } diff --git a/src/views/Viewer.vue b/src/views/Viewer.vue index 6d9ce06ca..4ccb06f46 100644 --- a/src/views/Viewer.vue +++ b/src/views/Viewer.vue @@ -132,14 +132,37 @@ export default { } return '' }, + /** + * Map the FIles action to a usable action vue component object + * + * @returns {Object[]} the list of actions + */ + actionsAll() { + return this.fileList[this.currentIndex] + ? Object.values(OCA.Files.App.fileList.fileActions.actions.all) + .filter(action => !action.render) + .map(action => { + return this.mapFileAction(action, this.fileList[this.currentIndex]) + }) + : [] + }, + actionsCurrent() { + const actions = OCA.Files.App.fileList.fileActions.actions[this.currentFile.mime] + return actions && this.fileList[this.currentIndex] + ? Object.values(actions) + .filter(action => !action.render) + .map(action => { + return this.mapFileAction(action, this.fileList[this.currentIndex]) + }) + : [] + }, actions() { - return [ - { - text: t('viewer', 'Share'), - icon: 'icon-share-white-forced', - action: this.showSharingSidebar - } - ] + const defaultAction = OCA.Files.App.fileList.fileActions.defaults[this.currentFile.mime] + return this.actionsAll.concat(this.actionsCurrent) + .filter(name => name.id !== defaultAction) + .sort((actionA, actionB) => { + return actionA.order - actionB.order + }) } }, @@ -158,6 +181,25 @@ export default { }) }) + const showAppsSidebar = OC.Apps.showAppSidebar + OC.Apps.showAppSidebar = () => { + showAppsSidebar() + const sidebar = document.getElementById('app-sidebar') + if (sidebar) { + sidebar.style.zIndex = 10001 + this.openedSidebar = true + } + } + + const hideAppSidebar = OC.Apps.hideAppSidebar + OC.Apps.hideAppSidebar = () => { + const sidebar = document.getElementById('app-sidebar') + if (sidebar) { + sidebar.style.zIndex = null + this.openedSidebar = false + } + hideAppSidebar() + } }, methods: { @@ -190,8 +232,10 @@ export default { // retrieve and store file List this.fileList = await FileList(OC.getCurrentUser().uid, fileInfo.dir, mimes) + console.debug(this.fileList) + // store current position - this.currentIndex = this.fileList.findIndex(file => decodeURI(file['d:href'][0]) === this.root + relativePath) + this.currentIndex = this.fileList.findIndex(file => decodeURI(file.href) === this.root + relativePath) this.updatePreviousNext() }, @@ -202,7 +246,7 @@ export default { * @param {Object} fileInfo the opened file info */ openFileFromList(fileInfo) { - const path = fileInfo['d:href'][0] + const path = fileInfo.href const mime = this.getMime(path) const modal = this.components[mime] @@ -214,9 +258,9 @@ export default { failed: false } - // if the sidebar is already opened, change the current file + // if the sidebar is already opened, hide it if (this.openedSidebar) { - OCA.Files.App.fileList.showDetailsView(this.currentFileName, 'shareTabView') + OCA.Apps.hideAppSidebar() } } @@ -232,7 +276,7 @@ export default { const next = this.fileList[this.currentIndex + 1] if (prev) { - const path = prev['d:href'][0] + const path = prev.href const mime = this.getMime(path) const modal = this.components[mime] @@ -250,7 +294,7 @@ export default { } if (next) { - const path = next['d:href'][0] + const path = next.href const mime = this.getMime(path) const modal = this.components[mime] @@ -413,23 +457,48 @@ export default { }, /** - * Show the sharing sidebar + * Map an OCA.Files.App action to a usable vue action object + * + * @param {Object} data the action object + * @param {String} data.action the action + * @param {String} data.altText altText property + * @param {String} data.displayName the displayName of the action + * @param {String} data.name the id of the action + * @param {String} data.iconClass the icon class + * @param {Int} data.order the order + * @param {OCA.Files.FileInfo} currentFileInfo the current file Infos + * @returns {Object} */ + mapFileAction({ action, altText, displayName, name, iconClass, order }, currentFileInfo) { + const fileInfoModel = new OCA.Files.FileInfoModel(currentFileInfo) + const $file = $(document.querySelector(`[data-file="${currentFileInfo.name}"]`)) + const context = { + fileActions: OCA.Files.App.fileList.fileActions, + fileList: OCA.Files.App.fileList, + $file, + fileInfoModel, + dir: fileInfoModel.get('path') + } - showSharingSidebar() { - // Open the sidebar sharing tab - OCA.Files.App.fileList.showDetailsView(this.currentFileName, 'shareTabView') + let text = typeof displayName === 'function' + ? displayName(context) + : displayName - const sidebar = document.getElementById('app-sidebar') - const closeButton = sidebar.querySelector('.icon-close') + if (!text) { + text = altText + } - // Sidebar above the modal - sidebar.style.zIndex = 10001 - this.openedSidebar = true - closeButton.addEventListener('click', e => { - sidebar.style.zIndex = null - this.openedSidebar = false - }) + return { + text, + id: name, + icon: typeof iconClass === 'function' + ? iconClass(currentFileInfo.name, context) + : iconClass, + action: function() { + OCA.Files.App.fileList.fileActions.triggerAction(name, fileInfoModel, OCA.Files.App.fileList) + }, + order + } } } }