Skip to content

Commit 4e92de2

Browse files
authored
Merge pull request #49610 from nextcloud/backport/49398/stable30
2 parents 3ea30b4 + dc25343 commit 4e92de2

128 files changed

Lines changed: 320 additions & 307 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

__tests__/FixJSDOMEnvironment.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
import JSDOMEnvironment from 'jest-environment-jsdom'
6+
7+
// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string
8+
export default class FixJSDOMEnvironment extends JSDOMEnvironment {
9+
10+
constructor(...args: ConstructorParameters<typeof JSDOMEnvironment>) {
11+
super(...args)
12+
13+
// https://github.com/jsdom/jsdom/issues/3363
14+
// 31 ad above switched to vitest and don't have that issue
15+
this.global.structuredClone = structuredClone
16+
}
17+
18+
}

apps/files/src/eventbus.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ declare module '@nextcloud/event-bus' {
99
// mapping of 'event name' => 'event type'
1010
'files:config:updated': { key: string, value: unknown }
1111

12-
'files:favorites:removed': Node
1312
'files:favorites:added': Node
13+
'files:favorites:removed': Node
1414

1515
'files:node:created': Node
1616
'files:node:deleted': Node
17-
'files:node:updated': Node
18-
'files:node:renamed': Node
1917
'files:node:moved': { node: Node, oldSource: string }
18+
'files:node:renamed': Node
19+
'files:node:updated': Node
2020

2121
'files:filter:added': IFileListFilter
2222
'files:filter:removed': string

apps/files/src/services/Files.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
import type { ContentsWithRoot, File, Folder } from '@nextcloud/files'
5+
import type { ContentsWithRoot, File, Folder, Node } from '@nextcloud/files'
66
import type { FileStat, ResponseDataDetailed } from 'webdav'
77

88
import { CancelablePromise } from 'cancelable-promise'
@@ -14,7 +14,7 @@ import logger from '../logger.ts'
1414
* Slim wrapper over `@nextcloud/files` `davResultToNode` to allow using the function with `Array.map`
1515
* @param node The node returned by the webdav library
1616
*/
17-
export const resultToNode = (node: FileStat): File | Folder => davResultToNode(node)
17+
export const resultToNode = (node: FileStat): Node => davResultToNode(node)
1818

1919
export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> => {
2020
const controller = new AbortController()

apps/files/src/store/files.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ export const useFilesStore = function(...args) {
128128
this.updateNodes([node])
129129
},
130130

131+
onMovedNode({ node, oldSource }: { node: Node, oldSource: string }) {
132+
if (!node.fileid) {
133+
logger.error('Trying to update/set a node without fileid', { node })
134+
return
135+
}
136+
137+
// Update the path of the node
138+
Vue.delete(this.files, oldSource)
139+
this.updateNodes([node])
140+
},
141+
131142
async onUpdatedNode(node: Node) {
132143
if (!node.fileid) {
133144
logger.error('Trying to update/set a node without fileid', { node })
@@ -160,6 +171,7 @@ export const useFilesStore = function(...args) {
160171
subscribe('files:node:created', fileStore.onCreatedNode)
161172
subscribe('files:node:deleted', fileStore.onDeletedNode)
162173
subscribe('files:node:updated', fileStore.onUpdatedNode)
174+
subscribe('files:node:moved', fileStore.onMovedNode)
163175

164176
fileStore._initialized = true
165177
}

apps/files/src/store/paths.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,40 @@ describe('Path store', () => {
127127
// See the child is removed
128128
expect(root._children).toEqual([])
129129
})
130+
131+
test('Folder is moved', () => {
132+
const node = new Folder({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/folder', id: 2 })
133+
emit('files:node:created', node)
134+
// see that the path is added and the children are set-up
135+
expect(store.paths).toEqual({ files: { [node.path]: node.source } })
136+
expect(root._children).toEqual([node.source])
137+
138+
const renamedNode = node.clone()
139+
renamedNode.rename('new-folder')
140+
141+
expect(renamedNode.path).toBe('/new-folder')
142+
expect(renamedNode.source).toBe('http://example.com/remote.php/dav/files/test/new-folder')
143+
144+
emit('files:node:moved', { node: renamedNode, oldSource: node.source })
145+
// See the path is updated
146+
expect(store.paths).toEqual({ files: { [renamedNode.path]: renamedNode.source } })
147+
// See the child is updated
148+
expect(root._children).toEqual([renamedNode.source])
149+
})
150+
151+
test('File is moved', () => {
152+
const node = new File({ owner: 'test', source: 'http://example.com/remote.php/dav/files/test/file.txt', id: 2, mime: 'text/plain' })
153+
emit('files:node:created', node)
154+
// see that the children are set-up
155+
expect(root._children).toEqual([node.source])
156+
expect(store.paths).toEqual({})
157+
158+
const renamedNode = node.clone()
159+
renamedNode.rename('new-file.txt')
160+
161+
emit('files:node:moved', { node: renamedNode, oldSource: node.source })
162+
// See the child is updated
163+
expect(root._children).toEqual([renamedNode.source])
164+
expect(store.paths).toEqual({})
165+
})
130166
})

apps/files/src/store/paths.ts

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
*/
55
import type { FileSource, PathOptions, ServicesState, Service } from '../types'
66
import { defineStore } from 'pinia'
7-
import { FileType, Folder, Node, getNavigation } from '@nextcloud/files'
7+
import { dirname } from '@nextcloud/paths'
8+
import { type Node, File, FileType, Folder, getNavigation } from '@nextcloud/files'
89
import { subscribe } from '@nextcloud/event-bus'
910
import Vue from 'vue'
1011
import logger from '../logger'
@@ -51,6 +52,27 @@ export const usePathsStore = function(...args) {
5152
Vue.delete(this.paths[service], path)
5253
},
5354

55+
onCreatedNode(node: Node) {
56+
const service = getNavigation()?.active?.id || 'files'
57+
if (!node.fileid) {
58+
logger.error('Node has no fileid', { node })
59+
return
60+
}
61+
62+
// Only add path if it's a folder
63+
if (node.type === FileType.Folder) {
64+
this.addPath({
65+
service,
66+
path: node.path,
67+
source: node.source,
68+
})
69+
}
70+
71+
// Update parent folder children if exists
72+
// If the folder is the root, get it and update it
73+
this.addNodeToParentChildren(node)
74+
},
75+
5476
onDeletedNode(node: Node) {
5577
const service = getNavigation()?.active?.id || 'files'
5678

@@ -62,95 +84,80 @@ export const usePathsStore = function(...args) {
6284
)
6385
}
6486

65-
// Remove node from children
66-
if (node.dirname === '/') {
67-
const root = files.getRoot(service) as Folder & { _children?: string[] }
68-
// ensure sources are unique
69-
const children = new Set(root._children ?? [])
70-
children.delete(node.source)
71-
Vue.set(root, '_children', [...children.values()])
72-
return
73-
}
74-
75-
if (this.paths[service][node.dirname]) {
76-
const parentSource = this.paths[service][node.dirname]
77-
const parentFolder = files.getNode(parentSource) as Folder & { _children?: string[] }
78-
79-
if (!parentFolder) {
80-
logger.error('Parent folder not found', { parentSource })
81-
return
82-
}
83-
84-
logger.debug('Path exists, removing from children', { parentFolder, node })
85-
86-
// ensure sources are unique
87-
const children = new Set(parentFolder._children ?? [])
88-
children.delete(node.source)
89-
Vue.set(parentFolder, '_children', [...children.values()])
90-
return
91-
}
92-
93-
logger.debug('Parent path does not exists, skipping children update', { node })
87+
this.deleteNodeFromParentChildren(node)
9488
},
9589

96-
onCreatedNode(node: Node) {
90+
onMovedNode({ node, oldSource }: { node: Node, oldSource: string }) {
9791
const service = getNavigation()?.active?.id || 'files'
98-
if (!node.fileid) {
99-
logger.error('Node has no fileid', { node })
100-
return
101-
}
10292

103-
// Only add path if it's a folder
93+
// Update the path of the node
10494
if (node.type === FileType.Folder) {
95+
// Delete the old path if it exists
96+
const oldPath = Object.entries(this.paths[service]).find(([, source]) => source === oldSource)
97+
if (oldPath?.[0]) {
98+
this.deletePath(service, oldPath[0])
99+
}
100+
101+
// Add the new path
105102
this.addPath({
106103
service,
107104
path: node.path,
108105
source: node.source,
109106
})
110107
}
111108

112-
// Update parent folder children if exists
113-
// If the folder is the root, get it and update it
114-
if (node.dirname === '/') {
115-
const root = files.getRoot(service) as Folder & { _children?: string[] }
109+
// Dummy simple clone of the renamed node from a previous state
110+
const oldNode = new File({ source: oldSource, owner: node.owner, mime: node.mime })
111+
112+
this.deleteNodeFromParentChildren(oldNode)
113+
this.addNodeToParentChildren(node)
114+
},
115+
116+
deleteNodeFromParentChildren(node: Node) {
117+
const service = getNavigation()?.active?.id || 'files'
118+
119+
// Update children of a root folder
120+
const parentSource = dirname(node.source)
121+
const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource)) as Folder & { _children?: string[] }
122+
if (folder) {
116123
// ensure sources are unique
117-
const children = new Set(root._children ?? [])
118-
children.add(node.source)
119-
Vue.set(root, '_children', [...children.values()])
124+
const children = new Set(folder._children ?? [])
125+
children.delete(node.source)
126+
Vue.set(folder, '_children', [...children.values()])
127+
logger.debug('Children updated', { parent: folder, node, children: folder._children })
120128
return
121129
}
122130

123-
// If the folder doesn't exists yet, it will be
124-
// fetched later and its children updated anyway.
125-
if (this.paths[service][node.dirname]) {
126-
const parentSource = this.paths[service][node.dirname]
127-
const parentFolder = files.getNode(parentSource) as Folder & { _children?: string[] }
128-
logger.debug('Path already exists, updating children', { parentFolder, node })
131+
logger.debug('Parent path does not exists, skipping children update', { node })
132+
},
129133

130-
if (!parentFolder) {
131-
logger.error('Parent folder not found', { parentSource })
132-
return
133-
}
134+
addNodeToParentChildren(node: Node) {
135+
const service = getNavigation()?.active?.id || 'files'
134136

137+
// Update children of a root folder
138+
const parentSource = dirname(node.source)
139+
const folder = (node.dirname === '/' ? files.getRoot(service) : files.getNode(parentSource)) as Folder & { _children?: string[] }
140+
if (folder) {
135141
// ensure sources are unique
136-
const children = new Set(parentFolder._children ?? [])
142+
const children = new Set(folder._children ?? [])
137143
children.add(node.source)
138-
Vue.set(parentFolder, '_children', [...children.values()])
144+
Vue.set(folder, '_children', [...children.values()])
145+
logger.debug('Children updated', { parent: folder, node, children: folder._children })
139146
return
140147
}
141148

142149
logger.debug('Parent path does not exists, skipping children update', { node })
143150
},
151+
144152
},
145153
})
146154

147155
const pathsStore = store(...args)
148156
// Make sure we only register the listeners once
149157
if (!pathsStore._initialized) {
150-
// TODO: watch folders to update paths?
151158
subscribe('files:node:created', pathsStore.onCreatedNode)
152159
subscribe('files:node:deleted', pathsStore.onDeletedNode)
153-
// subscribe('files:node:moved', pathsStore.onMovedNode)
160+
subscribe('files:node:moved', pathsStore.onMovedNode)
154161

155162
pathsStore._initialized = true
156163
}

dist/5828-5828.js.license

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ SPDX-FileCopyrightText: Sindre Sorhus
1515
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
1616
SPDX-FileCopyrightText: Roeland Jago Douma
1717
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
18-
SPDX-FileCopyrightText: Perry Mitchell <perry@perrymitchell.net>
1918
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
2019
SPDX-FileCopyrightText: Matt Zabriskie
2120
SPDX-FileCopyrightText: Joyent
@@ -63,7 +62,7 @@ This file is generated from multiple sources. Included packages:
6362
- version: 3.3.1
6463
- license: GPL-3.0-or-later
6564
- @nextcloud/files
66-
- version: 3.9.2
65+
- version: 3.10.0
6766
- license: AGPL-3.0-or-later
6867
- @nextcloud/initial-state
6968
- version: 2.2.0
@@ -254,9 +253,6 @@ This file is generated from multiple sources. Included packages:
254253
- vue
255254
- version: 2.7.16
256255
- license: MIT
257-
- webdav
258-
- version: 5.7.1
259-
- license: MIT
260256
- which-typed-array
261257
- version: 1.1.15
262258
- license: MIT

dist/6127-6127.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/6127-6127.js.license

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ SPDX-FileCopyrightText: Roeland Jago Douma
2929
SPDX-FileCopyrightText: Richie Bendall
3030
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
3131
SPDX-FileCopyrightText: Philipp Kewisch
32-
SPDX-FileCopyrightText: Perry Mitchell <perry@perrymitchell.net>
3332
SPDX-FileCopyrightText: Paul Vorbach <paul@vorba.ch> (http://paul.vorba.ch)
3433
SPDX-FileCopyrightText: Paul Vorbach <paul@vorb.de> (http://vorb.de)
3534
SPDX-FileCopyrightText: OpenJS Foundation and other contributors
@@ -131,7 +130,7 @@ This file is generated from multiple sources. Included packages:
131130
- version: 3.3.1
132131
- license: GPL-3.0-or-later
133132
- @nextcloud/files
134-
- version: 3.9.2
133+
- version: 3.10.0
135134
- license: AGPL-3.0-or-later
136135
- @nextcloud/initial-state
137136
- version: 2.2.0
@@ -592,9 +591,6 @@ This file is generated from multiple sources. Included packages:
592591
- web-namespaces
593592
- version: 2.0.1
594593
- license: MIT
595-
- webdav
596-
- version: 5.7.1
597-
- license: MIT
598594
- which-typed-array
599595
- version: 1.1.15
600596
- license: MIT

dist/6127-6127.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)