diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-12-12 13:31:03 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-12 13:31:03 +0100 |
commit | 4e92de20caa1cf5559809afa4bde5335d42f3029 (patch) | |
tree | 94fe331edd91d039d5979322d2b6020e7de8441d /apps | |
parent | 3ea30b4e8cb74349f2e61f014ed855bb63c82e2d (diff) | |
parent | dc253433871064f596ec7c93476cc58d34bc9f24 (diff) | |
download | nextcloud-server-4e92de20caa1cf5559809afa4bde5335d42f3029.tar.gz nextcloud-server-4e92de20caa1cf5559809afa4bde5335d42f3029.zip |
Merge pull request #49610 from nextcloud/backport/49398/stable30
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/eventbus.d.ts | 6 | ||||
-rw-r--r-- | apps/files/src/services/Files.ts | 4 | ||||
-rw-r--r-- | apps/files/src/store/files.ts | 12 | ||||
-rw-r--r-- | apps/files/src/store/paths.spec.ts | 36 | ||||
-rw-r--r-- | apps/files/src/store/paths.ts | 121 |
5 files changed, 117 insertions, 62 deletions
diff --git a/apps/files/src/eventbus.d.ts b/apps/files/src/eventbus.d.ts index e1fd8c73b4b..3db03d453d7 100644 --- a/apps/files/src/eventbus.d.ts +++ b/apps/files/src/eventbus.d.ts @@ -9,14 +9,14 @@ declare module '@nextcloud/event-bus' { // mapping of 'event name' => 'event type' 'files:config:updated': { key: string, value: unknown } - 'files:favorites:removed': Node 'files:favorites:added': Node + 'files:favorites:removed': Node 'files:node:created': Node 'files:node:deleted': Node - 'files:node:updated': Node - 'files:node:renamed': Node 'files:node:moved': { node: Node, oldSource: string } + 'files:node:renamed': Node + 'files:node:updated': Node 'files:filter:added': IFileListFilter 'files:filter:removed': string diff --git a/apps/files/src/services/Files.ts b/apps/files/src/services/Files.ts index 944c2d30678..94739f76006 100644 --- a/apps/files/src/services/Files.ts +++ b/apps/files/src/services/Files.ts @@ -2,7 +2,7 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { ContentsWithRoot, File, Folder } from '@nextcloud/files' +import type { ContentsWithRoot, File, Folder, Node } from '@nextcloud/files' import type { FileStat, ResponseDataDetailed } from 'webdav' import { CancelablePromise } from 'cancelable-promise' @@ -14,7 +14,7 @@ import logger from '../logger.ts' * Slim wrapper over `@nextcloud/files` `davResultToNode` to allow using the function with `Array.map` * @param node The node returned by the webdav library */ -export const resultToNode = (node: FileStat): File | Folder => davResultToNode(node) +export const resultToNode = (node: FileStat): Node => davResultToNode(node) export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> => { const controller = new AbortController() diff --git a/apps/files/src/store/files.ts b/apps/files/src/store/files.ts index fecf9e32db4..b06baee5e9b 100644 --- a/apps/files/src/store/files.ts +++ b/apps/files/src/store/files.ts @@ -128,6 +128,17 @@ export const useFilesStore = function(...args) { this.updateNodes([node]) }, + onMovedNode({ node, oldSource }: { node: Node, oldSource: string }) { + if (!node.fileid) { + logger.error('Trying to update/set a node without fileid', { node }) + return + } + + // Update the path of the node + Vue.delete(this.files, oldSource) + this.updateNodes([node]) + }, + async onUpdatedNode(node: Node) { if (!node.fileid) { logger.error('Trying to update/set a node without fileid', { node }) @@ -160,6 +171,7 @@ export const useFilesStore = function(...args) { subscribe('files:node:created', fileStore.onCreatedNode) subscribe('files:node:deleted', fileStore.onDeletedNode) subscribe('files:node:updated', fileStore.onUpdatedNode) + subscribe('files:node:moved', fileStore.onMovedNode) fileStore._initialized = true } diff --git a/apps/files/src/store/paths.spec.ts b/apps/files/src/store/paths.spec.ts index fab621f2915..19b74673ea1 100644 --- a/apps/files/src/store/paths.spec.ts +++ b/apps/files/src/store/paths.spec.ts @@ -127,4 +127,40 @@ describe('Path store', () => { // See the child is removed expect(root._children).toEqual([]) }) + + test('Folder is moved', () => { + const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 }) + emit('files:node:created', node) + // see that the path is added and the children are set-up + expect(store.paths).toEqual({ files: { [node.path]: node.source } }) + expect(root._children).toEqual([node.source]) + + const renamedNode = node.clone() + renamedNode.rename('new-folder') + + expect(renamedNode.path).toBe('/new-folder') + expect(renamedNode.source).toBe('http://example.com/remote.php/dav/files/test/new-folder') + + emit('files:node:moved', { node: renamedNode, oldSource: node.source }) + // See the path is updated + expect(store.paths).toEqual({ files: { [renamedNode.path]: renamedNode.source } }) + // See the child is updated + expect(root._children).toEqual([renamedNode.source]) + }) + + test('File is moved', () => { + const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' }) + emit('files:node:created', node) + // see that the children are set-up + expect(root._children).toEqual([node.source]) + expect(store.paths).toEqual({}) + + const renamedNode = node.clone() + renamedNode.rename('new-file.txt') + + emit('files:node:moved', { node: renamedNode, oldSource: node.source }) + // See the child is updated + expect(root._children).toEqual([renamedNode.source]) + expect(store.paths).toEqual({}) + }) }) diff --git a/apps/files/src/store/paths.ts b/apps/files/src/store/paths.ts index bf197a0c6df..b988e2ca112 100644 --- a/apps/files/src/store/paths.ts +++ b/apps/files/src/store/paths.ts @@ -4,7 +4,8 @@ */ import type { FileSource, PathOptions, ServicesState, Service } from '../types' import { defineStore } from 'pinia' -import { FileType, Folder, Node, getNavigation } from '@nextcloud/files' +import { dirname } from '@nextcloud/paths' +import { type Node, File, FileType, Folder, getNavigation } from '@nextcloud/files' import { subscribe } from '@nextcloud/event-bus' import Vue from 'vue' import logger from '../logger' @@ -51,6 +52,27 @@ export const usePathsStore = function(...args) { Vue.delete(this.paths[service], path) }, + onCreatedNode(node: Node) { + const service = getNavigation()?.active?.id || 'files' + if (!node.fileid) { + logger.error('Node has no fileid', { node }) + return + } + + // Only add path if it's a folder + if (node.type === FileType.Folder) { + this.addPath({ + service, + path: node.path, + source: node.source, + }) + } + + // Update parent folder children if exists + // If the folder is the root, get it and update it + this.addNodeToParentChildren(node) + }, + onDeletedNode(node: Node) { const service = getNavigation()?.active?.id || 'files' @@ -62,46 +84,21 @@ export const usePathsStore = function(...args) { ) } - // Remove node from children - if (node.dirname === '/') { - const root = files.getRoot(service) as Folder & { _children?: string[] } - // ensure sources are unique - const children = new Set(root._children ?? []) - children.delete(node.source) - Vue.set(root, '_children', [...children.values()]) - return - } - - if (this.paths[service][node.dirname]) { - const parentSource = this.paths[service][node.dirname] - const parentFolder = files.getNode(parentSource) as Folder & { _children?: string[] } - - if (!parentFolder) { - logger.error('Parent folder not found', { parentSource }) - return - } - - logger.debug('Path exists, removing from children', { parentFolder, node }) - - // ensure sources are unique - const children = new Set(parentFolder._children ?? []) - children.delete(node.source) - Vue.set(parentFolder, '_children', [...children.values()]) - return - } - - logger.debug('Parent path does not exists, skipping children update', { node }) + this.deleteNodeFromParentChildren(node) }, - onCreatedNode(node: Node) { + onMovedNode({ node, oldSource }: { node: Node, oldSource: string }) { const service = getNavigation()?.active?.id || 'files' - if (!node.fileid) { - logger.error('Node has no fileid', { node }) - return - } - // Only add path if it's a folder + // Update the path of the node if (node.type === FileType.Folder) { + // Delete the old path if it exists + const oldPath = Object.entries(this.paths[service]).find(([, source]) => source === oldSource) + if (oldPath?.[0]) { + this.deletePath(service, oldPath[0]) + } + + // Add the new path this.addPath({ service, path: node.path, @@ -109,48 +106,58 @@ export const usePathsStore = function(...args) { }) } - // Update parent folder children if exists - // If the folder is the root, get it and update it - if (node.dirname === '/') { - const root = files.getRoot(service) as Folder & { _children?: string[] } + // Dummy simple clone of the renamed node from a previous state + const oldNode = new File({ source: oldSource, owner: node.owner, mime: node.mime }) + + this.deleteNodeFromParentChildren(oldNode) + this.addNodeToParentChildren(node) + }, + + deleteNodeFromParentChildren(node: Node) { + const service = getNavigation()?.active?.id || 'files' + + // Update children of a root folder + const parentSource = dirname(node.source) + const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource)) as Folder & { _children?: string[] } + if (folder) { // ensure sources are unique - const children = new Set(root._children ?? []) - children.add(node.source) - Vue.set(root, '_children', [...children.values()]) + const children = new Set(folder._children ?? []) + children.delete(node.source) + Vue.set(folder, '_children', [...children.values()]) + logger.debug('Children updated', { parent: folder, node, children: folder._children }) return } - // If the folder doesn't exists yet, it will be - // fetched later and its children updated anyway. - if (this.paths[service][node.dirname]) { - const parentSource = this.paths[service][node.dirname] - const parentFolder = files.getNode(parentSource) as Folder & { _children?: string[] } - logger.debug('Path already exists, updating children', { parentFolder, node }) + logger.debug('Parent path does not exists, skipping children update', { node }) + }, - if (!parentFolder) { - logger.error('Parent folder not found', { parentSource }) - return - } + addNodeToParentChildren(node: Node) { + const service = getNavigation()?.active?.id || 'files' + // Update children of a root folder + const parentSource = dirname(node.source) + const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource)) as Folder & { _children?: string[] } + if (folder) { // ensure sources are unique - const children = new Set(parentFolder._children ?? []) + const children = new Set(folder._children ?? []) children.add(node.source) - Vue.set(parentFolder, '_children', [...children.values()]) + Vue.set(folder, '_children', [...children.values()]) + logger.debug('Children updated', { parent: folder, node, children: folder._children }) return } logger.debug('Parent path does not exists, skipping children update', { node }) }, + }, }) const pathsStore = store(...args) // Make sure we only register the listeners once if (!pathsStore._initialized) { - // TODO: watch folders to update paths? subscribe('files:node:created', pathsStore.onCreatedNode) subscribe('files:node:deleted', pathsStore.onDeletedNode) - // subscribe('files:node:moved', pathsStore.onMovedNode) + subscribe('files:node:moved', pathsStore.onMovedNode) pathsStore._initialized = true } |