fix(files): sort favorites navigation alphabetically

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
pull/53967/head
skjnldsv 2025-07-16 09:08:28 +07:00
parent 4317bb7594
commit 9d7214bc5a
2 changed files with 26 additions and 12 deletions

@ -7,6 +7,7 @@
import type { Folder as CFolder, Navigation } from '@nextcloud/files' import type { Folder as CFolder, Navigation } from '@nextcloud/files'
import * as filesUtils from '@nextcloud/files' import * as filesUtils from '@nextcloud/files'
import * as filesDavUtils from '@nextcloud/files/dav'
import { CancelablePromise } from 'cancelable-promise' import { CancelablePromise } from 'cancelable-promise'
import { basename } from 'path' import { basename } from 'path'
import { beforeEach, describe, expect, test, vi } from 'vitest' import { beforeEach, describe, expect, test, vi } from 'vitest'
@ -43,7 +44,7 @@ describe('Favorites view definition', () => {
test('Default empty favorite view', async () => { test('Default empty favorite view', async () => {
vi.spyOn(eventBus, 'subscribe') vi.spyOn(eventBus, 'subscribe')
vi.spyOn(filesUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([])) vi.spyOn(filesDavUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([]))
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] })) vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] }))
await registerFavoritesView() await registerFavoritesView()
@ -89,8 +90,14 @@ describe('Favorites view definition', () => {
source: 'http://nextcloud.local/remote.php/dav/files/admin/foo/bar', source: 'http://nextcloud.local/remote.php/dav/files/admin/foo/bar',
owner: 'admin', owner: 'admin',
}), }),
new Folder({
id: 4,
root: '/files/admin',
source: 'http://nextcloud.local/remote.php/dav/files/admin/foo/bar/yabadaba',
owner: 'admin',
}),
] ]
vi.spyOn(filesUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve(favoriteFolders)) vi.spyOn(filesDavUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve(favoriteFolders))
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] })) vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] }))
await registerFavoritesView() await registerFavoritesView()
@ -98,9 +105,12 @@ describe('Favorites view definition', () => {
const favoriteFoldersViews = Navigation.views.filter(view => view.parent === 'favorites') const favoriteFoldersViews = Navigation.views.filter(view => view.parent === 'favorites')
// one main view and 3 children // one main view and 3 children
expect(Navigation.views.length).toBe(4) expect(Navigation.views.length).toBe(5)
expect(favoritesView).toBeDefined() expect(favoritesView).toBeDefined()
expect(favoriteFoldersViews.length).toBe(3) expect(favoriteFoldersViews.length).toBe(4)
// Sorted by basename: bar, bar, foo
const expectedOrder = [2, 0, 1, 3]
favoriteFolders.forEach((folder, index) => { favoriteFolders.forEach((folder, index) => {
const favoriteView = favoriteFoldersViews[index] const favoriteView = favoriteFoldersViews[index]
@ -108,7 +118,7 @@ describe('Favorites view definition', () => {
expect(favoriteView?.id).toBeDefined() expect(favoriteView?.id).toBeDefined()
expect(favoriteView?.name).toBe(basename(folder.path)) expect(favoriteView?.name).toBe(basename(folder.path))
expect(favoriteView?.icon).toMatch(/<svg.+<\/svg>/) expect(favoriteView?.icon).toMatch(/<svg.+<\/svg>/)
expect(favoriteView?.order).toBe(index) expect(favoriteView?.order).toBe(expectedOrder[index])
expect(favoriteView?.params).toStrictEqual({ expect(favoriteView?.params).toStrictEqual({
dir: folder.path, dir: folder.path,
fileid: String(folder.fileid), fileid: String(folder.fileid),
@ -132,7 +142,7 @@ describe('Dynamic update of favorite folders', () => {
test('Add a favorite folder creates a new entry in the navigation', async () => { test('Add a favorite folder creates a new entry in the navigation', async () => {
vi.spyOn(eventBus, 'emit') vi.spyOn(eventBus, 'emit')
vi.spyOn(filesUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([])) vi.spyOn(filesDavUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([]))
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] })) vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] }))
await registerFavoritesView() await registerFavoritesView()
@ -160,7 +170,7 @@ describe('Dynamic update of favorite folders', () => {
test('Remove a favorite folder remove the entry from the navigation column', async () => { test('Remove a favorite folder remove the entry from the navigation column', async () => {
vi.spyOn(eventBus, 'emit') vi.spyOn(eventBus, 'emit')
vi.spyOn(filesUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([ vi.spyOn(filesDavUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([
new Folder({ new Folder({
id: 42, id: 42,
root: '/files/admin', root: '/files/admin',
@ -211,7 +221,7 @@ describe('Dynamic update of favorite folders', () => {
test('Renaming a favorite folder updates the navigation', async () => { test('Renaming a favorite folder updates the navigation', async () => {
vi.spyOn(eventBus, 'emit') vi.spyOn(eventBus, 'emit')
vi.spyOn(filesUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([])) vi.spyOn(filesDavUtils, 'getFavoriteNodes').mockReturnValue(CancelablePromise.resolve([]))
vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] })) vi.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as CFolder, contents: [] }))
await registerFavoritesView() await registerFavoritesView()

@ -4,13 +4,15 @@
*/ */
import type { Folder, Node } from '@nextcloud/files' import type { Folder, Node } from '@nextcloud/files'
import { FileType, View, getNavigation } from '@nextcloud/files'
import { getCanonicalLocale, getLanguage, t } from '@nextcloud/l10n'
import { getFavoriteNodes } from '@nextcloud/files/dav'
import { subscribe } from '@nextcloud/event-bus' import { subscribe } from '@nextcloud/event-bus'
import { FileType, View, getFavoriteNodes, getNavigation } from '@nextcloud/files'
import { getLanguage, translate as t } from '@nextcloud/l10n'
import { client } from '../services/WebdavClient.ts'
import FolderSvg from '@mdi/svg/svg/folder.svg?raw' import FolderSvg from '@mdi/svg/svg/folder.svg?raw'
import StarSvg from '@mdi/svg/svg/star.svg?raw' import StarSvg from '@mdi/svg/svg/star.svg?raw'
import { client } from '../services/WebdavClient.ts'
import { getContents } from '../services/Favorites' import { getContents } from '../services/Favorites'
import { hashCode } from '../utils/hashUtils' import { hashCode } from '../utils/hashUtils'
import logger from '../logger' import logger from '../logger'
@ -118,7 +120,7 @@ export const registerFavoritesView = async () => {
* update the order property of the existing views * update the order property of the existing views
*/ */
const updateAndSortViews = function() { const updateAndSortViews = function() {
favoriteFolders.sort((a, b) => a.path.localeCompare(b.path, getLanguage(), { ignorePunctuation: true })) favoriteFolders.sort((a, b) => a.basename.localeCompare(b.basename, [getLanguage(), getCanonicalLocale()], { ignorePunctuation: true, numeric: true, usage: 'sort' }))
favoriteFolders.forEach((folder, index) => { favoriteFolders.forEach((folder, index) => {
const view = favoriteFoldersViews.find((view) => view.id === generateIdFromPath(folder.path)) const view = favoriteFoldersViews.find((view) => view.id === generateIdFromPath(folder.path))
if (view) { if (view) {
@ -176,4 +178,6 @@ export const registerFavoritesView = async () => {
removePathFromFavorites(favoriteFolder.path) removePathFromFavorites(favoriteFolder.path)
addToFavorites(node) addToFavorites(node)
} }
updateAndSortViews()
} }