mirror of
https://github.com/nextcloud/server.git
synced 2026-02-19 02:38:40 -05:00
refactor(files): migrate favorite sidebar action to new Sidebar API
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
7077685bf8
commit
f9a137ea87
10 changed files with 107 additions and 30 deletions
|
|
@ -217,7 +217,7 @@ describe('Favorite action execute tests', () => {
|
|||
|
||||
// Check node change propagation
|
||||
expect(file.attributes.favorite).toBe(1)
|
||||
expect(eventBus.emit).toBeCalledTimes(1)
|
||||
expect(eventBus.emit).toHaveBeenCalled()
|
||||
expect(eventBus.emit).toBeCalledWith('files:favorites:added', file)
|
||||
})
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ describe('Favorite action execute tests', () => {
|
|||
|
||||
// Check node change propagation
|
||||
expect(file.attributes.favorite).toBe(0)
|
||||
expect(eventBus.emit).toBeCalledTimes(1)
|
||||
expect(eventBus.emit).toHaveBeenCalled()
|
||||
expect(eventBus.emit).toBeCalledWith('files:favorites:removed', file)
|
||||
})
|
||||
|
||||
|
|
@ -285,9 +285,9 @@ describe('Favorite action execute tests', () => {
|
|||
|
||||
// Check node change propagation
|
||||
expect(file.attributes.favorite).toBe(0)
|
||||
expect(eventBus.emit).toBeCalledTimes(2)
|
||||
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:node:deleted', file)
|
||||
expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:favorites:removed', file)
|
||||
expect(eventBus.emit).toHaveBeenCalled()
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:node:deleted', file)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:removed', file)
|
||||
})
|
||||
|
||||
test('Favorite does NOT triggers node removal if favorite view but NOT root dir', async () => {
|
||||
|
|
@ -320,7 +320,7 @@ describe('Favorite action execute tests', () => {
|
|||
|
||||
// Check node change propagation
|
||||
expect(file.attributes.favorite).toBe(0)
|
||||
expect(eventBus.emit).toBeCalledTimes(1)
|
||||
expect(eventBus.emit).toHaveBeenCalled()
|
||||
expect(eventBus.emit).toBeCalledWith('files:favorites:removed', file)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
/**
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { Node, View } from '@nextcloud/files'
|
||||
|
||||
import type { INode, IView } from '@nextcloud/files'
|
||||
|
||||
import StarOutlineSvg from '@mdi/svg/svg/star-outline.svg?raw'
|
||||
import StarSvg from '@mdi/svg/svg/star.svg?raw'
|
||||
|
|
@ -26,17 +27,18 @@ const queue = new PQueue({ concurrency: 5 })
|
|||
*
|
||||
* @param nodes - The nodes to check
|
||||
*/
|
||||
function shouldFavorite(nodes: Node[]): boolean {
|
||||
function shouldFavorite(nodes: INode[]): boolean {
|
||||
return nodes.some((node) => node.attributes.favorite !== 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Favorite or unfavorite a node
|
||||
*
|
||||
* @param node
|
||||
* @param view
|
||||
* @param willFavorite
|
||||
* @param node - The node to favorite/unfavorite
|
||||
* @param view - The current view
|
||||
* @param willFavorite - Whether to favorite or unfavorite the node
|
||||
*/
|
||||
export async function favoriteNode(node: Node, view: View, willFavorite: boolean): Promise<boolean> {
|
||||
export async function favoriteNode(node: INode, view: IView, willFavorite: boolean): Promise<boolean> {
|
||||
try {
|
||||
// TODO: migrate to webdav tags plugin
|
||||
const url = generateUrl('/apps/files/api/v1/files') + encodePath(node.path)
|
||||
|
|
@ -55,6 +57,7 @@ export async function favoriteNode(node: Node, view: View, willFavorite: boolean
|
|||
|
||||
// Update the node webdav attribute
|
||||
Vue.set(node.attributes, 'favorite', willFavorite ? 1 : 0)
|
||||
emit('files:node:updated', node)
|
||||
|
||||
// Dispatch event to whoever is interested
|
||||
if (willFavorite) {
|
||||
|
|
|
|||
42
apps/files/src/actions/sidebarFavoriteAction.ts
Normal file
42
apps/files/src/actions/sidebarFavoriteAction.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import starOutlineSvg from '@mdi/svg/svg/star-outline.svg?raw'
|
||||
import starSvg from '@mdi/svg/svg/star.svg?raw'
|
||||
import { registerSidebarAction } from '@nextcloud/files'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { favoriteNode } from './favoriteAction.ts'
|
||||
|
||||
/**
|
||||
* Register the favorite/unfavorite action in the sidebar
|
||||
*/
|
||||
export function registerSidebarFavoriteAction() {
|
||||
registerSidebarAction({
|
||||
id: 'files-favorite',
|
||||
order: 0,
|
||||
|
||||
enabled({ node }) {
|
||||
return node.isDavResource && node.root.startsWith('/files/')
|
||||
},
|
||||
|
||||
displayName({ node }) {
|
||||
if (node.attributes.favorite) {
|
||||
return t('files', 'Unfavorite')
|
||||
}
|
||||
return t('files', 'Favorite')
|
||||
},
|
||||
|
||||
iconSvgInline({ node }) {
|
||||
if (node.attributes.favorite) {
|
||||
return starSvg
|
||||
}
|
||||
return starOutlineSvg
|
||||
},
|
||||
|
||||
onClick({ node, view }) {
|
||||
favoriteNode(node, view, !node.attributes.favorite)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
import type { View } from '@nextcloud/files'
|
||||
import type { Mock } from 'vitest'
|
||||
import type { Location } from 'vue-router'
|
||||
|
||||
import axios from '@nextcloud/axios'
|
||||
|
|
@ -143,6 +144,9 @@ describe('HotKeysService testing', () => {
|
|||
})
|
||||
|
||||
it('Pressing s should toggle favorite', () => {
|
||||
(favoriteAction.enabled as Mock).mockReturnValue(true);
|
||||
(favoriteAction.exec as Mock).mockImplementationOnce(() => Promise.resolve(null))
|
||||
|
||||
vi.spyOn(axios, 'post').mockImplementationOnce(() => Promise.resolve())
|
||||
dispatchEvent({ key: 's', code: 'KeyS' })
|
||||
|
||||
|
|
@ -152,7 +156,6 @@ describe('HotKeysService testing', () => {
|
|||
dispatchEvent({ key: 's', code: 'KeyS', shiftKey: true })
|
||||
dispatchEvent({ key: 's', code: 'KeyS', metaKey: true })
|
||||
|
||||
expect(favoriteAction.enabled).toHaveReturnedWith(true)
|
||||
expect(favoriteAction.exec).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { action as openInFilesAction } from './actions/openInFilesAction.ts'
|
|||
import { action as editLocallyAction } from './actions/openLocallyAction.ts'
|
||||
import { action as renameAction } from './actions/renameAction.ts'
|
||||
import { action as sidebarAction } from './actions/sidebarAction.ts'
|
||||
import { registerSidebarFavoriteAction } from './actions/sidebarFavoriteAction.ts'
|
||||
import { action as viewInFolderAction } from './actions/viewInFolderAction.ts'
|
||||
import { registerFilenameFilter } from './filters/FilenameFilter.ts'
|
||||
import { registerHiddenFilesFilter } from './filters/HiddenFilesFilter.ts'
|
||||
|
|
@ -69,6 +70,9 @@ registerModifiedFilter()
|
|||
registerFilenameFilter()
|
||||
registerFilterToSearchToggle()
|
||||
|
||||
// Register sidebar action
|
||||
registerSidebarFavoriteAction()
|
||||
|
||||
// Register preview service worker
|
||||
registerPreviewServiceWorker()
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ import { getClient, getDefaultPropfind, getRootPath, resultToNode } from '@nextc
|
|||
export const client = getClient()
|
||||
|
||||
/**
|
||||
* Fetches a node from the given path
|
||||
*
|
||||
* @param path
|
||||
* @param path - The path to fetch the node from
|
||||
*/
|
||||
export async function fetchNode(path: string): Promise<Node> {
|
||||
const propfindPayload = getDefaultPropfind()
|
||||
|
|
|
|||
|
|
@ -130,9 +130,9 @@ describe('Favorites view definition', () => {
|
|||
|
||||
describe('Dynamic update of favorite folders', () => {
|
||||
let Navigation
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
|
||||
delete window._nc_navigation
|
||||
Navigation = getNavigation()
|
||||
})
|
||||
|
|
@ -167,8 +167,9 @@ describe('Dynamic update of favorite folders', () => {
|
|||
contents: [],
|
||||
})
|
||||
|
||||
expect(eventBus.emit).toHaveBeenCalledTimes(1)
|
||||
expect(eventBus.emit).toHaveBeenCalledTimes(2)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:added', folder)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:node:updated', folder)
|
||||
})
|
||||
|
||||
test('Remove a favorite folder remove the entry from the navigation column', async () => {
|
||||
|
|
@ -213,8 +214,9 @@ describe('Dynamic update of favorite folders', () => {
|
|||
contents: [],
|
||||
})
|
||||
|
||||
expect(eventBus.emit).toHaveBeenCalledTimes(1)
|
||||
expect(eventBus.emit).toHaveBeenCalledTimes(2)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:removed', folder)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:node:updated', folder)
|
||||
expect(fo).toHaveBeenCalled()
|
||||
|
||||
favoritesView = Navigation.views.find((view) => view.id === 'favorites')
|
||||
|
|
@ -257,7 +259,8 @@ describe('Dynamic update of favorite folders', () => {
|
|||
folder: {} as NcFolder,
|
||||
contents: [],
|
||||
})
|
||||
expect(eventBus.emit).toHaveBeenNthCalledWith(1, 'files:favorites:added', folder)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:favorites:added', folder)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:node:updated', folder)
|
||||
|
||||
// Create a folder with the same id but renamed
|
||||
const renamedFolder = new Folder({
|
||||
|
|
@ -269,6 +272,6 @@ describe('Dynamic update of favorite folders', () => {
|
|||
|
||||
// Exec the rename action
|
||||
eventBus.emit('files:node:renamed', renamedFolder)
|
||||
expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:node:renamed', renamedFolder)
|
||||
expect(eventBus.emit).toHaveBeenCalledWith('files:node:renamed', renamedFolder)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { IFolder, INode, IView } from '@nextcloud/files'
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ export function getActionEntryForFileId(fileid: number, actionId: string) {
|
|||
* @param actionId
|
||||
*/
|
||||
export function getActionEntryForFile(file: string, actionId: string) {
|
||||
return getActionButtonForFile(file)
|
||||
getActionButtonForFile(file)
|
||||
.should('have.attr', 'aria-controls')
|
||||
.then((menuId) => cy.get(`#${menuId}`).find(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`))
|
||||
return cy.findByRole('menu')
|
||||
.find(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import type { User } from '@nextcloud/e2e-test-server/cypress'
|
||||
|
||||
import { getActionButtonForFile, getRowForFile, triggerActionForFile } from './FilesUtils.ts'
|
||||
import { closeSidebar, getActionButtonForFile, getRowForFile, triggerActionForFile } from './FilesUtils.ts'
|
||||
|
||||
describe('files: Favorites', { testIsolation: true }, () => {
|
||||
let user: User
|
||||
|
|
@ -110,29 +110,44 @@ describe('files: Favorites', { testIsolation: true }, () => {
|
|||
.contains('new folder')
|
||||
.should('not.exist')
|
||||
|
||||
cy.intercept('PROPPATCH', '**/remote.php/dav/files/*/new%20folder').as('addToFavorites')
|
||||
cy.intercept('POST', '**/apps/files/api/v1/files/new%20folder').as('addToFavorites')
|
||||
// open sidebar
|
||||
triggerActionForFile('new folder', 'details')
|
||||
// open actions
|
||||
cy.get('[data-cy-sidebar]')
|
||||
.findByRole('button', { name: 'Actions' })
|
||||
.click()
|
||||
// trigger menu button
|
||||
cy.findAllByRole('menu')
|
||||
.findByRole('menuitem', { name: 'Add to favorites' })
|
||||
.findByRole('menuitem', { name: 'Favorite' })
|
||||
.should('be.visible')
|
||||
.click()
|
||||
|
||||
cy.wait('@addToFavorites')
|
||||
closeSidebar()
|
||||
|
||||
// See favorites star
|
||||
getRowForFile('new folder')
|
||||
.findByRole('img', { name: 'Favorite' })
|
||||
.should('be.visible')
|
||||
|
||||
// See folder in navigation
|
||||
cy.get('[data-cy-files-navigation-item="favorites"]')
|
||||
cy.reload()
|
||||
|
||||
// can unfavorite
|
||||
triggerActionForFile('new folder', 'details')
|
||||
cy.get('[data-cy-sidebar]')
|
||||
.findByRole('button', { name: 'Actions' })
|
||||
.click()
|
||||
// trigger menu button
|
||||
cy.findAllByRole('menu')
|
||||
.findByRole('menuitem', { name: 'Unfavorite' })
|
||||
.should('be.visible')
|
||||
.contains('new folder')
|
||||
.should('exist')
|
||||
.click()
|
||||
|
||||
cy.wait('@addToFavorites')
|
||||
closeSidebar()
|
||||
|
||||
getRowForFile('new folder')
|
||||
.findByRole('img', { name: 'Favorite' })
|
||||
.should('not.exist')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue