Skip to content

Commit 70ac798

Browse files
authored
Merge pull request #50300 from nextcloud/backport/50220/stable30
[stable30] fix(files): Ensure favorites set in sidebar work
2 parents 290d630 + 1589663 commit 70ac798

File tree

9 files changed

+177
-18
lines changed

9 files changed

+177
-18
lines changed

apps/files/src/store/files.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ export const useFilesStore = function(...args) {
162162
// Otherwise, it means we receive an event for a node that is not in the store
163163
fetchNode(node).then(n => this.updateNodes([n]))
164164
},
165+
166+
// Handlers for legacy sidebar (no real nodes support)
167+
onAddFavorite(node: Node) {
168+
const ourNode = this.getNode(node.source)
169+
if (ourNode) {
170+
Vue.set(ourNode.attributes, 'favorite', 1)
171+
}
172+
},
173+
174+
onRemoveFavorite(node: Node) {
175+
const ourNode = this.getNode(node.source)
176+
if (ourNode) {
177+
Vue.set(ourNode.attributes, 'favorite', 0)
178+
}
179+
},
165180
},
166181
})
167182

@@ -172,6 +187,9 @@ export const useFilesStore = function(...args) {
172187
subscribe('files:node:deleted', fileStore.onDeletedNode)
173188
subscribe('files:node:updated', fileStore.onUpdatedNode)
174189
subscribe('files:node:moved', fileStore.onMovedNode)
190+
// legacy sidebar
191+
subscribe('files:favorites:added', fileStore.onAddFavorite)
192+
subscribe('files:favorites:removed', fileStore.onRemoveFavorite)
175193

176194
fileStore._initialized = true
177195
}

apps/files/src/views/Sidebar.vue

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ import { getCurrentUser } from '@nextcloud/auth'
9191
import { getCapabilities } from '@nextcloud/capabilities'
9292
import { showError } from '@nextcloud/dialogs'
9393
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
94-
import { File, Folder, formatFileSize } from '@nextcloud/files'
94+
import { davRemoteURL, davRootPath, File, Folder, formatFileSize } from '@nextcloud/files'
9595
import { encodePath } from '@nextcloud/paths'
9696
import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
9797
import { ShareType } from '@nextcloud/sharing'
@@ -375,10 +375,10 @@ export default {
375375
},
376376
377377
/**
378-
* Toggle favourite state
378+
* Toggle favorite state
379379
* TODO: better implementation
380380
*
381-
* @param {boolean} state favourited or not
381+
* @param {boolean} state is favorite or not
382382
*/
383383
async toggleStarred(state) {
384384
try {
@@ -401,17 +401,21 @@ export default {
401401
*/
402402
const isDir = this.fileInfo.type === 'dir'
403403
const Node = isDir ? Folder : File
404-
emit(state ? 'files:favorites:added' : 'files:favorites:removed', new Node({
404+
const node = new Node({
405405
fileid: this.fileInfo.id,
406-
source: this.davPath,
407-
root: `/files/${getCurrentUser().uid}`,
406+
source: `${davRemoteURL}${davRootPath}${this.file}`,
407+
root: davRootPath,
408408
mime: isDir ? undefined : this.fileInfo.mimetype,
409-
}))
409+
attributes: {
410+
favorite: 1,
411+
},
412+
})
413+
emit(state ? 'files:favorites:added' : 'files:favorites:removed', node)
410414
411415
this.fileInfo.isFavourited = state
412416
} catch (error) {
413-
showError(t('files', 'Unable to change the favourite state of the file'))
414-
logger.error('Unable to change favourite state', { error })
417+
showError(t('files', 'Unable to change the favorite state of the file'))
418+
logger.error('Unable to change favorite state', { error })
415419
}
416420
},
417421

apps/files/src/views/favorites.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const registerFavoritesView = async () => {
6565
favoriteFoldersViews.forEach(view => Navigation.register(view))
6666

6767
/**
68-
* Update favourites navigation when a new folder is added
68+
* Update favorites navigation when a new folder is added
6969
*/
7070
subscribe('files:favorites:added', (node: Node) => {
7171
if (node.type !== FileType.Folder) {
@@ -99,7 +99,7 @@ export const registerFavoritesView = async () => {
9999
})
100100

101101
/**
102-
* Update favourites navigation when a folder is renamed
102+
* Update favorites navigation when a folder is renamed
103103
*/
104104
subscribe('files:node:renamed', (node: Node) => {
105105
if (node.type !== FileType.Folder) {

cypress/e2e/files/favorites.cy.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import type { User } from '@nextcloud/cypress'
7+
import { getActionButtonForFile, getRowForFile, triggerActionForFile } from './FilesUtils'
8+
9+
describe('files: Favorites', { testIsolation: true }, () => {
10+
let user: User
11+
12+
beforeEach(() => {
13+
cy.createRandomUser().then(($user) => {
14+
user = $user
15+
cy.uploadContent(user, new Blob([]), 'text/plain', '/file.txt')
16+
cy.mkdir(user, '/new folder')
17+
cy.login(user)
18+
cy.visit('/apps/files')
19+
})
20+
})
21+
22+
it('Mark file as favorite', () => {
23+
// See file exists
24+
getRowForFile('file.txt')
25+
.should('exist')
26+
27+
cy.intercept('POST', '**/apps/files/api/v1/files/file.txt').as('addToFavorites')
28+
// Click actions
29+
getActionButtonForFile('file.txt').click({ force: true })
30+
// See action is called 'Add to favorites'
31+
cy.get('[data-cy-files-list-row-action="favorite"] > button').last()
32+
.should('exist')
33+
.and('have.text', 'Add to favorites')
34+
.click({ force: true })
35+
cy.wait('@addToFavorites')
36+
// See favorites star
37+
getRowForFile('file.txt')
38+
.findByRole('img', { name: 'Favorite' })
39+
.should('exist')
40+
})
41+
42+
it('Un-mark file as favorite', () => {
43+
// See file exists
44+
getRowForFile('file.txt')
45+
.should('exist')
46+
47+
cy.intercept('POST', '**/apps/files/api/v1/files/file.txt').as('addToFavorites')
48+
// toggle favorite
49+
triggerActionForFile('file.txt', 'favorite')
50+
cy.wait('@addToFavorites')
51+
52+
// See favorites star
53+
getRowForFile('file.txt')
54+
.findByRole('img', { name: 'Favorite' })
55+
.should('be.visible')
56+
57+
// Remove favorite
58+
// click action button
59+
getActionButtonForFile('file.txt').click({ force: true })
60+
// See action is called 'Remove from favorites'
61+
cy.get('[data-cy-files-list-row-action="favorite"] > button').last()
62+
.should('exist')
63+
.and('have.text', 'Remove from favorites')
64+
.click({ force: true })
65+
cy.wait('@addToFavorites')
66+
// See no favorites star anymore
67+
getRowForFile('file.txt')
68+
.findByRole('img', { name: 'Favorite' })
69+
.should('not.exist')
70+
})
71+
72+
it('See favorite folders in navigation', () => {
73+
cy.intercept('POST', '**/apps/files/api/v1/files/new%20folder').as('addToFavorites')
74+
75+
// see navigation has no entry
76+
cy.get('[data-cy-files-navigation-item="favorites"]')
77+
.should('be.visible')
78+
.contains('new folder')
79+
.should('not.exist')
80+
81+
// toggle favorite
82+
triggerActionForFile('new folder', 'favorite')
83+
cy.wait('@addToFavorites')
84+
85+
// See in navigation
86+
cy.get('[data-cy-files-navigation-item="favorites"]')
87+
.should('be.visible')
88+
.contains('new folder')
89+
.should('exist')
90+
91+
// toggle favorite
92+
triggerActionForFile('new folder', 'favorite')
93+
cy.wait('@addToFavorites')
94+
95+
// See no longer in navigation
96+
cy.get('[data-cy-files-navigation-item="favorites"]')
97+
.should('be.visible')
98+
.contains('new folder')
99+
.should('not.exist')
100+
})
101+
102+
it('Mark file as favorite using the sidebar', () => {
103+
// See file exists
104+
getRowForFile('new folder')
105+
.should('exist')
106+
// see navigation has no entry
107+
cy.get('[data-cy-files-navigation-item="favorites"]')
108+
.should('be.visible')
109+
.contains('new folder')
110+
.should('not.exist')
111+
112+
cy.intercept('PROPPATCH', '**/remote.php/dav/files/*/new%20folder').as('addToFavorites')
113+
// open sidebar
114+
triggerActionForFile('new folder', 'details')
115+
// open actions
116+
cy.get('[data-cy-sidebar]')
117+
.findByRole('button', { name: 'Actions' })
118+
.click()
119+
// trigger menu button
120+
cy.findAllByRole('menu')
121+
.findByRole('menuitem', { name: 'Add to favorites' })
122+
.should('be.visible')
123+
.click()
124+
cy.wait('@addToFavorites')
125+
126+
// See favorites star
127+
getRowForFile('new folder')
128+
.findByRole('img', { name: 'Favorite' })
129+
.should('be.visible')
130+
131+
// See folder in navigation
132+
cy.get('[data-cy-files-navigation-item="favorites"]')
133+
.should('be.visible')
134+
.contains('new folder')
135+
.should('exist')
136+
})
137+
})

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.

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