From de2276e6d7e1f499012502c40718a217b594475b Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 10 Jun 2024 23:04:59 +0200 Subject: [PATCH] refactor(files): Migrate `favorites` view to `@nextcloud/files` functions and make it cancelable Also this fixes the view being writeable Signed-off-by: Ferdinand Thiessen --- apps/files/src/services/Favorites.ts | 66 ++++++++++++-------------- apps/files/src/views/favorites.spec.ts | 9 ++-- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/apps/files/src/services/Favorites.ts b/apps/files/src/services/Favorites.ts index 23f937511351b..e156c92c5117a 100644 --- a/apps/files/src/services/Favorites.ts +++ b/apps/files/src/services/Favorites.ts @@ -3,44 +3,38 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import type { ContentsWithRoot } from '@nextcloud/files' -import type { FileStat, ResponseDataDetailed } from 'webdav' -import { Folder, davGetDefaultPropfind, davGetFavoritesReport } from '@nextcloud/files' +import { getCurrentUser } from '@nextcloud/auth' +import { Folder, Permission, davRemoteURL, davRootPath, getFavoriteNodes } from '@nextcloud/files' +import { CancelablePromise } from 'cancelable-promise' +import { getContents as filesContents } from './Files.ts' +import { client } from './WebdavClient.ts' -import { getClient } from './WebdavClient' -import { resultToNode } from './Files' - -const client = getClient() - -export const getContents = async (path = '/'): Promise => { - const propfindPayload = davGetDefaultPropfind() - const reportPayload = davGetFavoritesReport() - - // Get root folder - let rootResponse - if (path === '/') { - rootResponse = await client.stat(path, { - details: true, - data: propfindPayload, - }) as ResponseDataDetailed +export const getContents = (path = '/'): CancelablePromise => { + // We only filter root files for favorites, for subfolders we can simply reuse the files contents + if (path !== '/') { + return filesContents(path) } - const contentsResponse = await client.getDirectoryContents(path, { - details: true, - // Only filter favorites if we're at the root - data: path === '/' ? reportPayload : propfindPayload, - headers: { - // Patched in WebdavClient.ts - method: path === '/' ? 'REPORT' : 'PROPFIND', - }, - includeSelf: true, - }) as ResponseDataDetailed - - const root = rootResponse?.data || contentsResponse.data[0] - const contents = contentsResponse.data.filter(node => node.filename !== path) - - return { - folder: resultToNode(root) as Folder, - contents: contents.map(resultToNode), - } + return new CancelablePromise((resolve, reject, cancel) => { + const promise = getFavoriteNodes(client) + .catch(reject) + .then((contents) => { + if (!contents) { + reject() + return + } + resolve({ + contents, + folder: new Folder({ + id: 0, + source: `${davRemoteURL}${davRootPath}`, + root: davRootPath, + owner: getCurrentUser()?.uid || null, + permissions: Permission.READ, + }), + }) + }) + cancel(() => promise.cancel()) + }) } diff --git a/apps/files/src/views/favorites.spec.ts b/apps/files/src/views/favorites.spec.ts index 58e60b2ee06ed..7dbb0dbc55116 100644 --- a/apps/files/src/views/favorites.spec.ts +++ b/apps/files/src/views/favorites.spec.ts @@ -6,6 +6,7 @@ import { basename } from 'path' import { expect } from '@jest/globals' import { Folder, Navigation, getNavigation } from '@nextcloud/files' +import { CancelablePromise } from 'cancelable-promise' import eventBus from '@nextcloud/event-bus' import * as initialState from '@nextcloud/initial-state' @@ -40,7 +41,7 @@ describe('Favorites view definition', () => { test('Default empty favorite view', () => { jest.spyOn(eventBus, 'subscribe') - jest.spyOn(favoritesService, 'getContents').mockReturnValue(Promise.resolve({ folder: {} as Folder, contents: [] })) + jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] })) registerFavoritesView() const favoritesView = Navigation.views.find(view => view.id === 'favorites') @@ -71,7 +72,7 @@ describe('Favorites view definition', () => { { fileid: 3, path: '/foo/bar' }, ] jest.spyOn(initialState, 'loadState').mockReturnValue(favoriteFolders) - jest.spyOn(favoritesService, 'getContents').mockReturnValue(Promise.resolve({ folder: {} as Folder, contents: [] })) + jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] })) registerFavoritesView() const favoritesView = Navigation.views.find(view => view.id === 'favorites') @@ -114,7 +115,7 @@ describe('Dynamic update of favourite folders', () => { test('Add a favorite folder creates a new entry in the navigation', async () => { jest.spyOn(eventBus, 'emit') jest.spyOn(initialState, 'loadState').mockReturnValue([]) - jest.spyOn(favoritesService, 'getContents').mockReturnValue(Promise.resolve({ folder: {} as Folder, contents: [] })) + jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] })) registerFavoritesView() const favoritesView = Navigation.views.find(view => view.id === 'favorites') @@ -143,7 +144,7 @@ describe('Dynamic update of favourite folders', () => { jest.spyOn(eventBus, 'emit') jest.spyOn(eventBus, 'subscribe') jest.spyOn(initialState, 'loadState').mockReturnValue([{ fileid: 42, path: '/Foo/Bar' }]) - jest.spyOn(favoritesService, 'getContents').mockReturnValue(Promise.resolve({ folder: {} as Folder, contents: [] })) + jest.spyOn(favoritesService, 'getContents').mockReturnValue(CancelablePromise.resolve({ folder: {} as Folder, contents: [] })) registerFavoritesView() let favoritesView = Navigation.views.find(view => view.id === 'favorites')