Skip to content

Commit 0f40e37

Browse files
authored
Merge pull request #53544 from nextcloud/backport/53140/stable30
2 parents 6442fb7 + fe5649f commit 0f40e37

25 files changed

+163
-93
lines changed

apps/files/src/services/WebdavClient.ts

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

7-
export const client = davGetClient()
8+
import { getClient, getDefaultPropfind, getRootPath, resultToNode } from '@nextcloud/files/dav'
9+
10+
export const client = getClient()
11+
12+
export const fetchNode = async (path: string): Promise<Node> => {
13+
const propfindPayload = getDefaultPropfind()
14+
const result = await client.stat(`${getRootPath()}${path}`, {
15+
details: true,
16+
data: propfindPayload,
17+
}) as ResponseDataDetailed<FileStat>
18+
return resultToNode(result.data)
19+
}

apps/systemtags/src/components/SystemTags.vue

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,51 +8,55 @@
88
<NcLoadingIcon v-if="loadingTags"
99
:name="t('systemtags', 'Loading collaborative tags …')"
1010
:size="32" />
11-
<template v-else>
12-
<NcSelectTags class="system-tags__select"
13-
:input-label="t('systemtags', 'Search or create collaborative tags')"
14-
:placeholder="t('systemtags', 'Collaborative tags …')"
15-
:options="sortedTags"
16-
:value="selectedTags"
17-
:create-option="createOption"
18-
:disabled="disabled"
19-
:taggable="true"
20-
:passthru="true"
21-
:fetch-tags="false"
22-
:loading="loading"
23-
@input="handleInput"
24-
@option:selected="handleSelect"
25-
@option:created="handleCreate"
26-
@option:deselected="handleDeselect">
27-
<template #no-options>
28-
{{ t('systemtags', 'No tags to select, type to create a new tag') }}
29-
</template>
30-
</NcSelectTags>
31-
</template>
11+
12+
<NcSelectTags v-show="!loadingTags"
13+
class="system-tags__select"
14+
:input-label="t('systemtags', 'Search or create collaborative tags')"
15+
:placeholder="t('systemtags', 'Collaborative tags …')"
16+
:options="sortedTags"
17+
:value="selectedTags"
18+
:create-option="createOption"
19+
:disabled="disabled"
20+
:taggable="true"
21+
:passthru="true"
22+
:fetch-tags="false"
23+
:loading="loading"
24+
@input="handleInput"
25+
@option:selected="handleSelect"
26+
@option:created="handleCreate"
27+
@option:deselected="handleDeselect">
28+
<template #no-options>
29+
{{ t('systemtags', 'No tags to select, type to create a new tag') }}
30+
</template>
31+
</NcSelectTags>
3232
</div>
3333
</template>
3434

3535
<script lang="ts">
3636
// FIXME Vue TypeScript ESLint errors
3737
/* eslint-disable */
38+
import type { Node } from '@nextcloud/files'
39+
import type { Tag, TagWithId } from '../types.js'
40+
3841
import Vue from 'vue'
3942
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
4043
import NcSelectTags from '@nextcloud/vue/dist/Components/NcSelectTags.js'
4144
42-
import { translate as t } from '@nextcloud/l10n'
45+
import { emit, subscribe } from '@nextcloud/event-bus'
4346
import { showError } from '@nextcloud/dialogs'
47+
import { t } from '@nextcloud/l10n'
4448
4549
import { defaultBaseTag } from '../utils.js'
4650
import { fetchLastUsedTagIds, fetchTags } from '../services/api.js'
51+
import { fetchNode } from '../../../files/src/services/WebdavClient.js'
52+
import { logger } from '../logger.ts'
4753
import {
4854
createTagForFile,
4955
deleteTagForFile,
5056
fetchTagsForFile,
5157
setTagForFile,
5258
} from '../services/files.js'
5359
54-
import type { Tag, TagWithId } from '../types.js'
55-
5660
export default Vue.extend({
5761
name: 'SystemTags',
5862
@@ -123,6 +127,10 @@ export default Vue.extend({
123127
},
124128
},
125129
130+
mounted() {
131+
subscribe('systemtags:node:updated', this.onTagUpdated)
132+
},
133+
126134
methods: {
127135
t,
128136
@@ -177,6 +185,8 @@ export default Vue.extend({
177185
showError(t('systemtags', 'Failed to select tag'))
178186
}
179187
this.loading = false
188+
189+
this.updateAndDispatchNodeTagsEvent(this.fileId)
180190
},
181191
182192
async handleCreate(tag: Tag) {
@@ -190,6 +200,8 @@ export default Vue.extend({
190200
showError(t('systemtags', 'Failed to create tag'))
191201
}
192202
this.loading = false
203+
204+
this.updateAndDispatchNodeTagsEvent(this.fileId)
193205
},
194206
195207
async handleDeselect(tag: TagWithId) {
@@ -200,6 +212,35 @@ export default Vue.extend({
200212
showError(t('systemtags', 'Failed to delete tag'))
201213
}
202214
this.loading = false
215+
216+
this.updateAndDispatchNodeTagsEvent(this.fileId)
217+
},
218+
219+
async onTagUpdated(node: Node) {
220+
if (node.fileid !== this.fileId) {
221+
return
222+
}
223+
224+
this.loadingTags = true
225+
try {
226+
this.selectedTags = await fetchTagsForFile(this.fileId)
227+
} catch (error) {
228+
showError(t('systemtags', 'Failed to load selected tags'))
229+
}
230+
231+
this.loadingTags = false
232+
},
233+
234+
async updateAndDispatchNodeTagsEvent(fileId: number) {
235+
const path = window.OCA?.Files?.Sidebar?.file || ''
236+
try {
237+
const node = await fetchNode(path)
238+
if (node) {
239+
emit('systemtags:node:updated', node)
240+
}
241+
} catch (error) {
242+
logger.error('Failed to fetch node for system tags update', { error, fileId })
243+
}
203244
},
204245
},
205246
})

cypress/e2e/systemtags/files-inline-action.cy.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
2222
it('See first assigned tag in the file list', () => {
2323
const tag = randomBytes(8).toString('base64')
2424

25+
cy.intercept('PROPFIND', `**/remote.php/dav/files/${user.userId}/file.txt`).as('getNode')
2526
getRowForFile('file.txt').should('be.visible')
2627
triggerActionForFile('file.txt', 'details')
28+
cy.wait('@getNode')
2729

2830
cy.get('[data-cy-sidebar]')
2931
.should('be.visible')
@@ -36,13 +38,14 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
3638
.click()
3739

3840
cy.intercept('PUT', '**/remote.php/dav/systemtags-relations/files/**').as('assignTag')
39-
cy.get('[data-cy-sidebar]')
40-
.findByRole('combobox', { name: /collaborative tags/i })
41-
.should('be.visible')
42-
.type(`${tag}{enter}`)
41+
42+
getCollaborativeTagsInput()
43+
.type(`{selectAll}${tag}{enter}`)
4344
cy.wait('@assignTag')
44-
closeSidebar()
45+
cy.wait('@getNode')
4546

47+
// Close the sidebar and reload to check the file list
48+
closeSidebar()
4649
cy.reload()
4750

4851
getRowForFile('file.txt')
@@ -56,8 +59,10 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
5659
const tag1 = randomBytes(5).toString('base64')
5760
const tag2 = randomBytes(5).toString('base64')
5861

62+
cy.intercept('PROPFIND', `**/remote.php/dav/files/${user.userId}/file.txt`).as('getNode')
5963
getRowForFile('file.txt').should('be.visible')
6064
triggerActionForFile('file.txt', 'details')
65+
cy.wait('@getNode')
6166

6267
cy.get('[data-cy-sidebar]')
6368
.should('be.visible')
@@ -70,17 +75,20 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
7075
.click()
7176

7277
cy.intercept('PUT', '**/remote.php/dav/systemtags-relations/files/**').as('assignTag')
73-
cy.get('[data-cy-sidebar]').within(() => {
74-
cy.findByRole('combobox', { name: /collaborative tags/i })
75-
.should('be.visible')
76-
.type(`${tag1}{enter}`)
77-
cy.wait('@assignTag')
78-
cy.findByRole('combobox', { name: /collaborative tags/i })
79-
.should('be.visible')
80-
.type(`${tag2}{enter}`)
81-
cy.wait('@assignTag')
82-
})
8378

79+
// Assign first tag
80+
getCollaborativeTagsInput()
81+
.type(`{selectAll}${tag1}{enter}`)
82+
cy.wait('@assignTag')
83+
cy.wait('@getNode')
84+
85+
// Assign second tag
86+
getCollaborativeTagsInput()
87+
.type(`{selectAll}${tag2}{enter}`)
88+
cy.wait('@assignTag')
89+
cy.wait('@getNode')
90+
91+
// Close the sidebar and reload to check the file list
8492
closeSidebar()
8593
cy.reload()
8694

@@ -97,11 +105,10 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
97105
const tag2 = randomBytes(4).toString('base64')
98106
const tag3 = randomBytes(4).toString('base64')
99107

108+
cy.intercept('PROPFIND', `**/remote.php/dav/files/${user.userId}/file.txt`).as('getNode')
100109
getRowForFile('file.txt').should('be.visible')
101-
102-
cy.intercept('PROPFIND', '**/remote.php/dav/**').as('sidebarLoaded')
103110
triggerActionForFile('file.txt', 'details')
104-
cy.wait('@sidebarLoaded')
111+
cy.wait('@getNode')
105112

106113
cy.get('[data-cy-sidebar]')
107114
.should('be.visible')
@@ -114,23 +121,26 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
114121
.click()
115122

116123
cy.intercept('PUT', '**/remote.php/dav/systemtags-relations/files/**').as('assignTag')
117-
cy.get('[data-cy-sidebar]').within(() => {
118-
cy.findByRole('combobox', { name: /collaborative tags/i })
119-
.should('be.visible')
120-
.type(`${tag1}{enter}`)
121-
cy.wait('@assignTag')
122-
123-
cy.findByRole('combobox', { name: /collaborative tags/i })
124-
.should('be.visible')
125-
.type(`${tag2}{enter}`)
126-
cy.wait('@assignTag')
127-
128-
cy.findByRole('combobox', { name: /collaborative tags/i })
129-
.should('be.visible')
130-
.type(`${tag3}{enter}`)
131-
cy.wait('@assignTag')
132-
})
133124

125+
// Assign first tag
126+
getCollaborativeTagsInput()
127+
.type(`{selectAll}${tag1}{enter}`)
128+
cy.wait('@assignTag')
129+
cy.wait('@getNode')
130+
131+
// Assign second tag
132+
getCollaborativeTagsInput()
133+
.type(`{selectAll}${tag2}{enter}`)
134+
cy.wait('@assignTag')
135+
cy.wait('@getNode')
136+
137+
// Assign third tag
138+
getCollaborativeTagsInput()
139+
.type(`{selectAll}${tag3}{enter}`)
140+
cy.wait('@assignTag')
141+
cy.wait('@getNode')
142+
143+
// Close the sidebar and reload to check the file list
134144
closeSidebar()
135145
cy.reload()
136146

@@ -153,3 +163,10 @@ describe('Systemtags: Files integration', { testIsolation: true }, () => {
153163
})
154164
})
155165
})
166+
167+
function getCollaborativeTagsInput(): Cypress.Chainable<JQuery<HTMLElement>> {
168+
return cy.get('[data-cy-sidebar]')
169+
.findByRole('combobox', { name: /collaborative tags/i })
170+
.should('be.visible')
171+
.should('not.have.attr', 'disabled', { timeout: 5000 })
172+
}

dist/core-common.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/core-common.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.

dist/federatedfilesharing-vue-settings-admin.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/federatedfilesharing-vue-settings-admin.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.

dist/files-init.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/files-init.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.

dist/files-main.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.

0 commit comments

Comments
 (0)