Skip to content

Commit 8a4f610

Browse files
committed
fix(files): Ensure favorites set in sidebar work
When marking a file as favorite from within the sidebar make sure it really works, this fixes two issues: 1. The source needs to be the plain source not URL encoded, as otherwise the source of the node would be encoded twice (and show with encoding in the navigation) 2. The store should also listen for the update events as the sidebar has no access to the real node to update it, instead the store should - as long as we only have the legacy sidebar - update the node when added or removed as favorite. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 3dec77b commit 8a4f610

File tree

4 files changed

+169
-10
lines changed

4 files changed

+169
-10
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: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
})

0 commit comments

Comments
 (0)