diff --git a/apps/files/src/store/active.spec.ts b/apps/files/src/store/active.spec.ts new file mode 100644 index 0000000000000..70c005ed2b77e --- /dev/null +++ b/apps/files/src/store/active.spec.ts @@ -0,0 +1,56 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { Folder } from '@nextcloud/files' +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, test, vi } from 'vitest' +import { nextTick } from 'vue' +import { useActiveStore } from './active.ts' + +function buildFolder(id: number, name: string) { + return new Folder({ + id, + owner: 'test', + source: `http://example.com/remote.php/dav/files/test/${name}`, + root: '/files/test', + }) +} + +describe('Active store syncs the route fileid with the active node', () => { + let goToRoute: ReturnType + + beforeEach(() => { + setActivePinia(createPinia()) + goToRoute = vi.fn() + window.OCP = { Files: { Router: { goToRoute, params: {}, query: {} } } } as unknown as typeof window.OCP + }) + + test('rewrites a stale child fileid when the current folder becomes active', async () => { + // The route still deep-links a child (79) left over from a previous + // location, while the sidebar is being opened for the current folder (78). + window.OCP.Files.Router.params = { fileid: '79' } + + const store = useActiveStore() + const folder = buildFolder(78, 'parent') + store.activeFolder = folder + store.activeNode = folder + await nextTick() + + expect(goToRoute).toHaveBeenCalledTimes(1) + expect(goToRoute.mock.calls[0][1]).toMatchObject({ fileid: '78' }) + }) + + test('does not touch the route when it already points at the active node', async () => { + window.OCP.Files.Router.params = { fileid: '78' } + + const store = useActiveStore() + const folder = buildFolder(78, 'parent') + store.activeFolder = folder + store.activeNode = folder + await nextTick() + + expect(goToRoute).not.toHaveBeenCalled() + }) +}) diff --git a/apps/files/src/store/active.ts b/apps/files/src/store/active.ts index 935eee52bf544..12bd3f5136e96 100644 --- a/apps/files/src/store/active.ts +++ b/apps/files/src/store/active.ts @@ -47,14 +47,21 @@ export const useActiveStore = defineStore('active', () => { // Set the active node on the router params watch(activeNode, () => { - if (typeof activeNode.value?.fileid !== 'number' || activeNode.value.fileid === activeFolder.value?.fileid) { + if (!activeNode.value?.id) { return } - logger.debug('Updating active fileid in URL query', { fileid: activeNode.value.fileid }) + // Sync even when the active node is the current folder: skipping that case + // leaves a stale child fileid in the route, which then gets restored into + // the sidebar when it reopens. + if (activeNode.value.id === String(window.OCP.Files.Router.params?.fileid ?? '')) { + return + } + + logger.debug('Updating active fileid in URL query', { fileid: activeNode.value.id }) window.OCP.Files.Router.goToRoute( null, - { ...window.OCP.Files.Router.params, fileid: String(activeNode.value.fileid) }, + { ...window.OCP.Files.Router.params, fileid: activeNode.value.id }, { ...window.OCP.Files.Router.query }, true, )