From 4a9cdeb01f44773508fd4f13e00c33757367812e Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Sun, 28 Dec 2025 17:24:58 +0100 Subject: [PATCH] refactor!(files): migrate sidebar API to use Node API Signed-off-by: Ferdinand Thiessen --- apps/files/src/FilesApp.vue | 26 +- apps/files/src/actions/sidebarAction.spec.ts | 89 +-- apps/files/src/actions/sidebarAction.ts | 54 +- .../components/FileEntry/FileEntryPreview.vue | 78 +- .../files/src/components/FilesListVirtual.vue | 41 +- .../FilesSidebar/FilesSidebarSubname.vue | 65 ++ .../FilesSidebar/FilesSidebarTab.vue | 69 ++ apps/files/src/components/LegacyView.vue | 44 -- apps/files/src/components/SidebarTab.vue | 119 --- apps/files/src/composables/useHotKeys.spec.ts | 2 +- apps/files/src/composables/usePreviewImage.ts | 86 +++ apps/files/src/eventbus.d.ts | 25 +- apps/files/src/global.d.ts | 29 + apps/files/src/models/Tab.js | 125 ---- apps/files/src/router/router.ts | 32 +- apps/files/src/services/RouterService.ts | 23 +- apps/files/src/services/Sidebar.js | 80 -- apps/files/src/sidebar.ts | 56 +- apps/files/src/store/active.ts | 12 +- apps/files/src/store/index.ts | 2 +- apps/files/src/store/sidebar.ts | 193 +++++ apps/files/src/views/FilesList.vue | 48 +- apps/files/src/views/FilesNavigation.vue | 6 +- apps/files/src/views/FilesSidebar.vue | 707 +++--------------- apps/systemtags/src/components/SystemTags.vue | 8 +- build/frontend-legacy/webpack.common.cjs | 26 +- cypress/e2e/files/FilesUtils.ts | 3 +- cypress/e2e/files/files-sidebar.cy.ts | 2 +- 28 files changed, 729 insertions(+), 1321 deletions(-) create mode 100644 apps/files/src/components/FilesSidebar/FilesSidebarSubname.vue create mode 100644 apps/files/src/components/FilesSidebar/FilesSidebarTab.vue delete mode 100644 apps/files/src/components/LegacyView.vue delete mode 100644 apps/files/src/components/SidebarTab.vue create mode 100644 apps/files/src/composables/usePreviewImage.ts create mode 100644 apps/files/src/global.d.ts delete mode 100644 apps/files/src/models/Tab.js delete mode 100644 apps/files/src/services/Sidebar.js create mode 100644 apps/files/src/store/sidebar.ts diff --git a/apps/files/src/FilesApp.vue b/apps/files/src/FilesApp.vue index 622b2e84ca0..e83d4999c24 100644 --- a/apps/files/src/FilesApp.vue +++ b/apps/files/src/FilesApp.vue @@ -6,35 +6,19 @@ + - diff --git a/apps/files/src/actions/sidebarAction.spec.ts b/apps/files/src/actions/sidebarAction.spec.ts index bc89297734b..5ed51feabd5 100644 --- a/apps/files/src/actions/sidebarAction.spec.ts +++ b/apps/files/src/actions/sidebarAction.spec.ts @@ -3,17 +3,32 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { View } from '@nextcloud/files' +import type { IView } from '@nextcloud/files' import { File, FileAction, Folder, Permission } from '@nextcloud/files' -import { describe, expect, test, vi } from 'vitest' +import { beforeEach, describe, expect, test, vi } from 'vitest' import logger from '../logger.ts' import { action } from './sidebarAction.ts' +const sidebar = vi.hoisted(() => ({ + available: true, + open: vi.fn(), +})) + +vi.mock('@nextcloud/files', async (original) => ({ + ...(await original()), + getSidebar: () => sidebar, +})) + const view = { id: 'files', name: 'Files', -} as View +} as IView + +beforeEach(() => { + sidebar.available = true + vi.clearAllMocks() +}) describe('Open sidebar action conditions tests', () => { test('Default values', () => { @@ -38,9 +53,6 @@ describe('Open sidebar action conditions tests', () => { describe('Open sidebar action enabled tests', () => { test('Enabled for ressources within user root folder', () => { - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: {} } } - const file = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', @@ -60,9 +72,6 @@ describe('Open sidebar action enabled tests', () => { }) test('Disabled without permissions', () => { - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: {} } } - const file = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', @@ -82,9 +91,6 @@ describe('Open sidebar action enabled tests', () => { }) test('Disabled if more than one node', () => { - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: {} } } - const file1 = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foo.txt', @@ -110,8 +116,7 @@ describe('Open sidebar action enabled tests', () => { }) test('Disabled if no Sidebar', () => { - // @ts-expect-error mocking for tests - window.OCA = {} + sidebar.available = false const file = new File({ id: 1, @@ -131,9 +136,6 @@ describe('Open sidebar action enabled tests', () => { }) test('Disabled for non-dav ressources', () => { - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: {} } } - const file = new File({ id: 1, source: 'https://domain.com/documents/admin/foobar.txt', @@ -154,14 +156,7 @@ describe('Open sidebar action enabled tests', () => { describe('Open sidebar action exec tests', () => { test('Open sidebar', async () => { - const openMock = vi.fn() - const defaultTabMock = vi.fn() - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: { open: openMock, setActiveTab: defaultTabMock } } } - - const goToRouteMock = vi.fn() - window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } } - + sidebar.available = true const file = new File({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', @@ -177,33 +172,17 @@ describe('Open sidebar action exec tests', () => { root: '/files/admin', }) - const exec = await action.exec({ + // Silent action + expect(await action.exec({ nodes: [file], view, folder, contents: [], - }) - // Silent action - expect(exec).toBe(null) - expect(openMock).toBeCalledWith('/foobar.txt') - expect(defaultTabMock).toBeCalledWith('sharing') - expect(goToRouteMock).toBeCalledWith( - null, - { view: view.id, fileid: '1' }, - { dir: '/', opendetails: 'true' }, - true, - ) + })).toBeNull() + expect(sidebar.open).toBeCalledWith(file, 'sharing') }) test('Open sidebar for folder', async () => { - const openMock = vi.fn() - const defaultTabMock = vi.fn() - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: { open: openMock, setActiveTab: defaultTabMock } } } - - const goToRouteMock = vi.fn() - window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } } - const file = new Folder({ id: 1, source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar', @@ -227,23 +206,13 @@ describe('Open sidebar action exec tests', () => { }) // Silent action expect(exec).toBe(null) - expect(openMock).toBeCalledWith('/foobar') - expect(defaultTabMock).toBeCalledWith('sharing') - expect(goToRouteMock).toBeCalledWith( - null, - { view: view.id, fileid: '1' }, - { dir: '/', opendetails: 'true' }, - true, - ) + expect(sidebar.open).toBeCalledWith(file, 'sharing') }) test('Open sidebar fails', async () => { - const openMock = vi.fn(() => { - throw new Error('Mock error') + sidebar.open.mockImplementationOnce(() => { + throw new Error('Sidebar error') }) - const defaultTabMock = vi.fn() - // @ts-expect-error mocking for tests - window.OCA = { Files: { Sidebar: { open: openMock, setActiveTab: defaultTabMock } } } vi.spyOn(logger, 'error').mockImplementation(() => vi.fn()) const file = new File({ @@ -261,7 +230,7 @@ describe('Open sidebar action exec tests', () => { contents: [], }) expect(exec).toBe(false) - expect(openMock).toBeCalledTimes(1) - expect(logger.error).toBeCalledTimes(1) + expect(sidebar.open).toHaveBeenCalledOnce() + expect(logger.error).toHaveBeenCalledOnce() }) }) diff --git a/apps/files/src/actions/sidebarAction.ts b/apps/files/src/actions/sidebarAction.ts index d967c2434ef..da16ea1d449 100644 --- a/apps/files/src/actions/sidebarAction.ts +++ b/apps/files/src/actions/sidebarAction.ts @@ -1,10 +1,11 @@ -/** +/*! * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ + import InformationSvg from '@mdi/svg/svg/information-outline.svg?raw' -import { FileAction, Permission } from '@nextcloud/files' -import { translate as t } from '@nextcloud/l10n' +import { FileAction, getSidebar, Permission } from '@nextcloud/files' +import { t } from '@nextcloud/l10n' import { isPublicShare } from '@nextcloud/sharing/public' import logger from '../logger.ts' @@ -17,49 +18,34 @@ export const action = new FileAction({ // Sidebar currently supports user folder only, /files/USER enabled: ({ nodes }) => { + const node = nodes[0] + if (nodes.length !== 1 || !node) { + return false + } + + const sidebar = getSidebar() + if (!sidebar.available) { + return false + } + if (isPublicShare()) { return false } - // Only works on single node - if (nodes.length !== 1) { - return false - } - - if (!nodes[0]) { - return false - } - - // Only work if the sidebar is available - if (!window?.OCA?.Files?.Sidebar) { - return false - } - - return (nodes[0].root?.startsWith('/files/') && nodes[0].permissions !== Permission.NONE) ?? false + return node.root.startsWith('/files/') && node.permissions !== Permission.NONE }, - async exec({ nodes, view, folder }) { - const node = nodes[0] + async exec({ nodes }) { + const sidebar = getSidebar() + const [node] = nodes try { // If the sidebar is already open for the current file, do nothing - if (window.OCA.Files?.Sidebar?.file === node.path) { + if (sidebar.node?.source === node.source) { logger.debug('Sidebar already open for this file', { node }) return null } - // Open sidebar and set active tab to sharing by default - window.OCA.Files?.Sidebar?.setActiveTab('sharing') - - // TODO: migrate Sidebar to use a Node instead - await window.OCA.Files?.Sidebar?.open(node.path) - - // Silently update current fileid - window.OCP?.Files?.Router?.goToRoute( - null, - { view: view.id, fileid: String(node.fileid) }, - { ...window.OCP.Files.Router.query, dir: folder.path, opendetails: 'true' }, - true, - ) + sidebar.open(node, 'sharing') return null } catch (error) { logger.error('Error while opening sidebar', { error }) diff --git a/apps/files/src/components/FileEntry/FileEntryPreview.vue b/apps/files/src/components/FileEntry/FileEntryPreview.vue index 77b9fb1eadf..2696c2c00d7 100644 --- a/apps/files/src/components/FileEntry/FileEntryPreview.vue +++ b/apps/files/src/components/FileEntry/FileEntryPreview.vue @@ -8,7 +8,7 @@