From e5188f619ac9e1c8f10c3f3f5b5359aee67cc0e6 Mon Sep 17 00:00:00 2001 From: Andras Timar Date: Fri, 13 Feb 2026 21:36:57 +0100 Subject: [PATCH] fix: Register default file action to prevent download instead of open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Viewer app's 'view' file action checks window.OCA.Viewer.mimetypes to decide if it can handle a file. However, those mimetypes are only populated on DOMContentLoaded when handlers are bridged from the @nextcloud/viewer npm package to the ViewerService. If the Files app renders the file list before that event fires, the 'view' action's enabled() returns false and the 'download' action (order 30) becomes the default — causing document files to be downloaded instead of opened in Collabora Online. Register a richdocuments-specific default file action that checks against the OCS capability mimetype list (baked into the page HTML via initial state, always available immediately) instead of the ViewerService's runtime mimetypes. When the Viewer's 'view' action works correctly both actions are enabled but 'view' wins (registered first). When 'view' is disabled due to the timing race, the new action takes over. Execution delegates to the Viewer's own 'view' action for proper URL history and prev/next navigation support. Signed-off-by: Andras Timar --- src/file-actions.js | 58 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/file-actions.js b/src/file-actions.js index 2686352d94..4fb5407c1d 100644 --- a/src/file-actions.js +++ b/src/file-actions.js @@ -2,13 +2,69 @@ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { registerFileAction } from '@nextcloud/files' +import { DefaultType, Permission, getFileActions, registerFileAction } from '@nextcloud/files' import { getCapabilities } from './services/capabilities.ts' import { translate as t } from '@nextcloud/l10n' // eslint-disable-next-line import/no-unresolved import appIcon from '../img/app.svg?raw' +const supportedMimes = getCapabilities()?.mimetypes ?? [] + +// Default file action for document types handled by richdocuments. +// The Viewer app registers a generic 'view' file action that checks +// window.OCA.Viewer.mimetypes, but those mimetypes are only populated +// on DOMContentLoaded which can race with the file list rendering. +// This action uses richdocuments' own capability list (available +// immediately from OCS initial state) so it is not affected by timing. +const openDocument = { + id: 'richdocuments-default', + + displayName: () => { + return t('richdocuments', + 'Open in {productName}', + { productName: getCapabilities()?.productName ?? 'Nextcloud Office' }) + }, + + iconSvgInline: () => { + return appIcon.replaceAll(/#(fff|0{6})/g, 'currentColor') + }, + + default: DefaultType.DEFAULT, + + enabled: ({ nodes }) => { + if (nodes.length !== 1) { + return false + } + const node = nodes[0] + if (!node.isDavResource || !node.root?.startsWith('/files')) { + return false + } + if (!(node.permissions & Permission.READ)) { + return false + } + return supportedMimes.includes(node.mime) + }, + + exec: async ({ nodes, view, folder }) => { + // Delegate to the Viewer's own 'view' action when available + // so that URL history and prev/next navigation work correctly. + const viewAction = getFileActions().find(a => a.id === 'view') + if (viewAction?.exec) { + return viewAction.exec({ nodes, view, folder }) + } + + // Fallback: open directly through the Viewer API + if (window.OCA?.Viewer) { + window.OCA.Viewer.open({ path: nodes[0].path }) + return null + } + return false + }, +} + +registerFileAction(openDocument) + const openPdf = { id: 'office-open-pdf',