diff --git a/package-lock.json b/package-lock.json index 26d3e2499e..6930b7dc17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@nextcloud/capabilities": "^1.2.1", "@nextcloud/dialogs": "^6.4.1", "@nextcloud/event-bus": "^3.3.3", - "@nextcloud/files": "^3.12.0", + "@nextcloud/files": "^4.0.0-beta.8", "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/logger": "^3.0.2", @@ -2452,6 +2452,28 @@ "vue": "^2.7.16" } }, + "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/files": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", + "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "cancelable-promise": "^4.3.1", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.1", + "webdav": "^5.8.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/initial-state": { "version": "3.0.0", "license": "GPL-3.0-or-later", @@ -2459,6 +2481,15 @@ "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } }, + "node_modules/@nextcloud/dialogs/node_modules/@nextcloud/paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-3.0.0.tgz", + "integrity": "sha512-+sTfTkIbVUa2Ue3bkz3R7F1mhddvHPOWUxkSNg7Q5dAsimVFBaTRgiBAJmsAag3JPsxyuS8kUgeb0zdEssRdTA==", + "license": "GPL-3.0-or-later", + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, "node_modules/@nextcloud/eslint-config": { "version": "8.4.2", "dev": true, @@ -2536,31 +2567,31 @@ } }, "node_modules/@nextcloud/files": { - "version": "3.12.0", + "version": "4.0.0-beta.8", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-4.0.0-beta.8.tgz", + "integrity": "sha512-uHKPDAJ1jXnkeFcFEtCbn5lstmZ4zV/SQ9GUypT+FP8j+j51dggGO/tMiPxvc1GbBB1HTbKltaBtv83O3eX5UQ==", "license": "AGPL-3.0-or-later", "dependencies": { - "@nextcloud/auth": "^2.5.1", - "@nextcloud/capabilities": "^1.2.0", - "@nextcloud/l10n": "^3.3.0", - "@nextcloud/logger": "^3.0.2", - "@nextcloud/paths": "^2.2.1", - "@nextcloud/router": "^3.0.1", - "@nextcloud/sharing": "^0.2.4", - "cancelable-promise": "^4.3.1", - "is-svg": "^6.0.0", + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "is-svg": "^6.1.0", "typescript-event-target": "^1.1.1", "webdav": "^5.8.0" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + "node": "^24.0.0" } }, - "node_modules/@nextcloud/files/node_modules/@nextcloud/sharing": { - "version": "0.2.5", + "node_modules/@nextcloud/files/node_modules/@nextcloud/paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-3.0.0.tgz", + "integrity": "sha512-+sTfTkIbVUa2Ue3bkz3R7F1mhddvHPOWUxkSNg7Q5dAsimVFBaTRgiBAJmsAag3JPsxyuS8kUgeb0zdEssRdTA==", "license": "GPL-3.0-or-later", - "dependencies": { - "@nextcloud/initial-state": "^2.2.0" - }, "engines": { "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } @@ -2588,14 +2619,15 @@ } }, "node_modules/@nextcloud/logger": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nextcloud/logger/-/logger-3.0.3.tgz", + "integrity": "sha512-TcbVRL4/O5ffI1RXFmQAFD3gwwT15AAdr1770x+RNqVvfBdoGVyhzOwCIyA5Vfc3fA1iJXFa+rE6buJZSoqlcw==", "license": "GPL-3.0-or-later", "dependencies": { - "@nextcloud/auth": "^2.3.0" + "@nextcloud/auth": "^2.5.3" }, "engines": { - "node": "^20.0.0", - "npm": "^10.0.0" + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } }, "node_modules/@nextcloud/moment": { @@ -2637,14 +2669,15 @@ } }, "node_modules/@nextcloud/router": { - "version": "3.0.1", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nextcloud/router/-/router-3.1.0.tgz", + "integrity": "sha512-e4dkIaxRSwdZJlZFpn9x03QgBn/Sa2hN1hp/BA7+AbzykmSAlKuWfdmX8j/8ewrLpQwYmZR23IZO9XwpJXq2Uw==", "license": "GPL-3.0-or-later", "dependencies": { - "@nextcloud/typings": "^1.7.0" + "@nextcloud/typings": "^1.10.0" }, "engines": { - "node": "^20.0.0", - "npm": "^10.0.0" + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } }, "node_modules/@nextcloud/sharing": { @@ -2661,6 +2694,29 @@ "@nextcloud/files": "^3.12.0" } }, + "node_modules/@nextcloud/sharing/node_modules/@nextcloud/files": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", + "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", + "license": "AGPL-3.0-or-later", + "optional": true, + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "cancelable-promise": "^4.3.1", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.1", + "webdav": "^5.8.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, "node_modules/@nextcloud/sharing/node_modules/@nextcloud/initial-state": { "version": "3.0.0", "license": "GPL-3.0-or-later", @@ -2668,6 +2724,16 @@ "node": "^20.0.0 || ^22.0.0 || ^24.0.0" } }, + "node_modules/@nextcloud/sharing/node_modules/@nextcloud/paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-3.0.0.tgz", + "integrity": "sha512-+sTfTkIbVUa2Ue3bkz3R7F1mhddvHPOWUxkSNg7Q5dAsimVFBaTRgiBAJmsAag3JPsxyuS8kUgeb0zdEssRdTA==", + "license": "GPL-3.0-or-later", + "optional": true, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, "node_modules/@nextcloud/stylelint-config": { "version": "3.1.1", "dev": true, @@ -2733,6 +2799,62 @@ "vue": "^2.7.16" } }, + "node_modules/@nextcloud/upload/node_modules/@nextcloud/files": { + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/@nextcloud/files/-/files-3.12.2.tgz", + "integrity": "sha512-vBo8tf3Xh6efiF8CrEo3pKj9AtvAF6RdDGO1XKL65IxV8+UUd9Uxl2lUExHlzoDRRczCqfGfaWfRRaFhYqce5Q==", + "license": "AGPL-3.0-or-later", + "dependencies": { + "@nextcloud/auth": "^2.5.3", + "@nextcloud/capabilities": "^1.2.1", + "@nextcloud/l10n": "^3.4.1", + "@nextcloud/logger": "^3.0.3", + "@nextcloud/paths": "^3.0.0", + "@nextcloud/router": "^3.1.0", + "@nextcloud/sharing": "^0.3.0", + "cancelable-promise": "^4.3.1", + "is-svg": "^6.1.0", + "typescript-event-target": "^1.1.1", + "webdav": "^5.8.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, + "node_modules/@nextcloud/upload/node_modules/@nextcloud/files/node_modules/@nextcloud/initial-state": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/initial-state/-/initial-state-3.0.0.tgz", + "integrity": "sha512-cV+HBdkQJGm8FxkBI5rFT/FbMNWNBvpbj6OPrg4Ae4YOOsQ15CL8InPOAw1t4XkOkQK2NEdUGQLVUz/19wXbdQ==", + "license": "GPL-3.0-or-later", + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, + "node_modules/@nextcloud/upload/node_modules/@nextcloud/files/node_modules/@nextcloud/paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nextcloud/paths/-/paths-3.0.0.tgz", + "integrity": "sha512-+sTfTkIbVUa2Ue3bkz3R7F1mhddvHPOWUxkSNg7Q5dAsimVFBaTRgiBAJmsAag3JPsxyuS8kUgeb0zdEssRdTA==", + "license": "GPL-3.0-or-later", + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + } + }, + "node_modules/@nextcloud/upload/node_modules/@nextcloud/files/node_modules/@nextcloud/sharing": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@nextcloud/sharing/-/sharing-0.3.0.tgz", + "integrity": "sha512-kV7qeUZvd1fTKeFyH+W5Qq5rNOqG9rLATZM3U9MBxWXHJs3OxMqYQb8UQ3NYONzsX3zDGJmdQECIGHm1ei2sCA==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@nextcloud/initial-state": "^3.0.0", + "is-svg": "^6.1.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || ^24.0.0" + }, + "optionalDependencies": { + "@nextcloud/files": "^3.12.0" + } + }, "node_modules/@nextcloud/upload/node_modules/@nextcloud/sharing": { "version": "0.2.5", "license": "GPL-3.0-or-later", diff --git a/package.json b/package.json index 1e4b70e83d..ba4eb91765 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@nextcloud/capabilities": "^1.2.1", "@nextcloud/dialogs": "^6.4.1", "@nextcloud/event-bus": "^3.3.3", - "@nextcloud/files": "^3.12.0", + "@nextcloud/files": "^4.0.0-beta.8", "@nextcloud/initial-state": "^2.2.0", "@nextcloud/l10n": "^3.4.1", "@nextcloud/logger": "^3.0.2", diff --git a/src/actions/openInLibreSignAction.js b/src/actions/openInLibreSignAction.js index 4b34849772..aff1150789 100644 --- a/src/actions/openInLibreSignAction.js +++ b/src/actions/openInLibreSignAction.js @@ -2,7 +2,7 @@ * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { registerFileAction, FileAction } from '@nextcloud/files' +import { registerFileAction, FileAction, getSidebar } from '@nextcloud/files' import { getCapabilities } from '@nextcloud/capabilities' import { loadState } from '@nextcloud/initial-state' import { translate as t } from '@nextcloud/l10n' @@ -71,10 +71,9 @@ export const action = new FileAction({ * Single file: open in sidebar */ async exec({ nodes }) { - window.OCA.Files.Sidebar.close() const node = nodes[0] - await window.OCA.Files.Sidebar.open(node.path) - window.OCA.Files.Sidebar.setActiveTab('libresign') + await sidebar.open(node, 'libresign') + sidebar.setActiveTab('libresign') return null }, @@ -103,16 +102,15 @@ export const action = new FileAction({ settings: { path: envelopePath, }, - }).then((response) => { + }).then(async (response) => { const envelopeData = response.data?.ocs?.data window.OCA.Libresign.pendingEnvelope = envelopeData - window.OCA.Files.Sidebar.close() - - window.OCA.Files.Sidebar.setActiveTab('libresign') + const sidebar = getSidebar() const firstNode = nodes[0] - window.OCA.Files.Sidebar.open(firstNode.path) + await sidebar.open(firstNode, 'libresign') + sidebar.setActiveTab('libresign') return new Array(nodes.length).fill(null) }).catch((error) => { diff --git a/src/actions/showStatusInlineAction.js b/src/actions/showStatusInlineAction.js index 650ec25a85..ccf606525f 100644 --- a/src/actions/showStatusInlineAction.js +++ b/src/actions/showStatusInlineAction.js @@ -2,7 +2,7 @@ * SPDX-FileCopyrightText: 2025 LibreCode coop and contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { FileAction, registerFileAction } from '@nextcloud/files' +import { FileAction, registerFileAction, getSidebar } from '@nextcloud/files' import { loadState } from '@nextcloud/initial-state' import { t } from '@nextcloud/l10n' @@ -26,8 +26,8 @@ const action = new FileAction({ }, exec: async ({ nodes }) => { const node = nodes[0] - await window.OCA.Files.Sidebar.open(node.path) - window.OCA.Files.Sidebar.setActiveTab('libresign') + sidebar.open(node, 'libresign') + sidebar.setActiveTab('libresign') return null }, iconSvgInline: ({ nodes }) => { @@ -48,7 +48,7 @@ const action = new FileAction({ const certificateOk = loadState('libresign', 'certificate_ok') const allPdf = nodes?.length > 0 && nodes.every(node => node.mime === 'application/pdf') const allHaveStatus = nodes?.every(node => node.attributes['libresign-signature-status']) - + return certificateOk && allPdf && allHaveStatus }, order: -1, diff --git a/src/init.js b/src/init.js index b1067381ee..51e676ccf3 100644 --- a/src/init.js +++ b/src/init.js @@ -6,7 +6,7 @@ import Vue from 'vue' import axios from '@nextcloud/axios' -import { addNewFileMenuEntry, Permission } from '@nextcloud/files' +import { addNewFileMenuEntry, Permission, getSidebar } from '@nextcloud/files' import { registerDavProperty } from '@nextcloud/files/dav' import { translate, translatePlural } from '@nextcloud/l10n' import { generateOcsUrl } from '@nextcloud/router' @@ -34,7 +34,7 @@ addNewFileMenuEntry({ enabled() { return Permission.CREATE !== 0 }, - async handler(context, content) { + async handler(context, content) {s const input = document.createElement('input') input.accept = 'application/pdf' input.type = 'file' @@ -54,9 +54,10 @@ addNewFileMenuEntry({ name: upload.file.name, }) .then(async ({ data }) => { - await window.OCA.Files.Sidebar.open(path) - OCA.Files.Sidebar.setActiveTab('libresign') + sidebar.open({ path }, 'libresign') + sidebar.setActiveTab('libresign') }) + .catch((error) => logger.error('Error uploading file:', error)) }) this.uploadManager .upload(file.name, file) diff --git a/src/tab.js b/src/tab.js index 37825f7190..5bd1562c00 100644 --- a/src/tab.js +++ b/src/tab.js @@ -7,7 +7,10 @@ import { createPinia, PiniaVuePlugin } from 'pinia' import Vue from 'vue' import { loadState } from '@nextcloud/initial-state' -import { translate, translatePlural } from '@nextcloud/l10n' +import { translate as t, translatePlural } from '@nextcloud/l10n' +import { FileType, registerSidebarTab } from '@nextcloud/files' + +import LibreSignLogoDarkSvg from '../img/app-dark.svg?raw' import AppFilesTab from './Components/RightSidebar/AppFilesTab.vue' @@ -17,7 +20,7 @@ import './plugins/vuelidate.js' import './style/icons.scss' -Vue.prototype.t = translate +Vue.prototype.t = t Vue.prototype.n = translatePlural if (!window.OCA.Libresign) { @@ -28,58 +31,121 @@ Vue.use(PiniaVuePlugin) const pinia = createPinia() -const isEnabled = function(fileInfo) { - if (fileInfo?.isDirectory() || !loadState('libresign', 'certificate_ok')) { - return false +const tagName = 'libresign-files-sidebar-tab' +const View = Vue.extend(AppFilesTab) + +function mapNodeToFileInfo(node = {}) { + const name = node.basename || node.displayname || node.name || '' + const dirname = node.dirname || (node.path ? node.path.substring(0, node.path.lastIndexOf('/')) : '') + return { + id: node.fileid || node.id, + name, + path: dirname, + isDirectory() { + return node.type === FileType.Folder || node.type === FileType.Collection || node.type === 'folder' + }, + get(key) { + if (key === 'mimetype') { + return node.mime || node.mimetype + } + return undefined + }, } +} - window.OCA.Libresign.fileInfo = fileInfo +function setupCustomElement() { + if (window.customElements.get(tagName)) { + return + } - const mimetype = fileInfo.get('mimetype') || '' - if (mimetype === 'application/pdf') { - return true + class LibreSignSidebarTab extends HTMLElement { + connectedCallback() { + this.mountVue() + this.updateFromNode() + } + + disconnectedCallback() { + this.destroyVue() + } + + set node(value) { + this._node = value + this.updateFromNode() + } + + get node() { + return this._node + } + + async setActive(active) { + this._active = active + if (active) { + this.updateFromNode() + } + return Promise.resolve() + } + + mountVue() { + if (this._vueInstance) { + return + } + + const instance = new View({ pinia }) + instance.$mount() + this._vueInstance = instance + this.appendChild(instance.$el) + } + + destroyVue() { + if (this._vueInstance) { + this._vueInstance.$destroy() + this._vueInstance.$el.remove() + this._vueInstance = null + } + } + + updateFromNode() { + if (!this._vueInstance || !this._node) { + return + } + const fileInfo = mapNodeToFileInfo(this._node) + this._vueInstance.update?.(fileInfo) + } } - return false + window.customElements.define(tagName, LibreSignSidebarTab) } -const View = Vue.extend(AppFilesTab) -let TabInstance = null +function isEnabled(context) { + if (!context?.node) { + return false + } -window.addEventListener('DOMContentLoaded', () => { - /** - * Register a new tab in the sidebar - */ - if (OCA.Files && OCA.Files.Sidebar) { - OCA.Files.Sidebar.registerTab(new OCA.Files.Sidebar.Tab({ - id: 'libresign', - name: t('libresign', 'LibreSign'), - icon: 'icon-rename', - enabled: isEnabled, - - async mount(el, fileInfo, context) { - if (TabInstance) { - TabInstance.$destroy() - } - - TabInstance = new View({ - // Better integration with vue parent component - parent: context, - pinia, - }) - - // Only mount after we hahve all theh info we need - await TabInstance.update(fileInfo) - - TabInstance.$mount(el) - }, - update(fileInfo) { - TabInstance.update(fileInfo) - }, - destroy() { - TabInstance.$destroy() - TabInstance = null - }, - })) + if (!loadState('libresign', 'certificate_ok')) { + return false + } + + const node = context.node + const mimetype = node.mime || node.mimetype || '' + const isFolder = node.type === FileType.Folder || node.type === FileType.Collection || node.type === 'folder' + + if (isFolder) { + return false } + + window.OCA.Libresign.fileInfo = mapNodeToFileInfo(node) + + return mimetype === 'application/pdf' +} + +window.addEventListener('DOMContentLoaded', () => { + setupCustomElement() + registerSidebarTab({ + id: 'libresign', + order: 95, + displayName: t('libresign', 'LibreSign'), + iconSvgInline: LibreSignLogoDarkSvg, + enabled: isEnabled, + tagName, + }) })