diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-06-10 23:04:59 +0200 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2024-08-27 14:59:05 +0200 |
commit | 0c77417bc4bfe05964b9b5786b52e86c5aae1e77 (patch) | |
tree | 323af85f3685d76c2bc97885d2f600c508643d1a /apps/files | |
parent | 57b81d8ec51d8dddd172b0064bfc61c6a1bbc4b2 (diff) | |
download | nextcloud-server-0c77417bc4bfe05964b9b5786b52e86c5aae1e77.tar.gz nextcloud-server-0c77417bc4bfe05964b9b5786b52e86c5aae1e77.zip |
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 <opensource@fthiessen.de>
Diffstat (limited to 'apps/files')
-rw-r--r-- | apps/files/src/services/Favorites.ts | 66 | ||||
-rw-r--r-- | apps/files/src/services/WebdavClient.ts | 43 | ||||
-rw-r--r-- | apps/files/src/views/favorites.spec.ts | 18 |
3 files changed, 45 insertions, 82 deletions
diff --git a/apps/files/src/services/Favorites.ts b/apps/files/src/services/Favorites.ts index 83577f9a75e..18ecb04cc3c 100644 --- a/apps/files/src/services/Favorites.ts +++ b/apps/files/src/services/Favorites.ts @@ -20,44 +20,38 @@ * */ 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<ContentsWithRoot> => { - const propfindPayload = davGetDefaultPropfind() - const reportPayload = davGetFavoritesReport() - - // Get root folder - let rootResponse - if (path === '/') { - rootResponse = await client.stat(path, { - details: true, - data: propfindPayload, - }) as ResponseDataDetailed<FileStat> +export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> | Promise<ContentsWithRoot> => { + // 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<FileStat[]> - - 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/services/WebdavClient.ts b/apps/files/src/services/WebdavClient.ts index 6c98b299703..e404010e204 100644 --- a/apps/files/src/services/WebdavClient.ts +++ b/apps/files/src/services/WebdavClient.ts @@ -20,47 +20,16 @@ * */ -import { createClient, getPatcher } from 'webdav' import { generateRemoteUrl } from '@nextcloud/router' import { getCurrentUser, getRequestToken, onRequestTokenUpdate } from '@nextcloud/auth' +import { davGetClient } from '@nextcloud/files' export const rootPath = `/files/${getCurrentUser()?.uid}` export const defaultRootUrl = generateRemoteUrl('dav' + rootPath) -export const getClient = (rootUrl = defaultRootUrl) => { - const client = createClient(rootUrl) - - // set CSRF token header - const setHeaders = (token: string | null) => { - client?.setHeaders({ - // Add this so the server knows it is an request from the browser - 'X-Requested-With': 'XMLHttpRequest', - // Inject user auth - requesttoken: token ?? '', - }); - } - - // refresh headers when request token changes - onRequestTokenUpdate(setHeaders) - setHeaders(getRequestToken()) - - /** - * Allow to override the METHOD to support dav REPORT - * - * @see https://github.com/perry-mitchell/webdav-client/blob/8d9694613c978ce7404e26a401c39a41f125f87f/source/request.ts - */ - const patcher = getPatcher() - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - // https://github.com/perry-mitchell/hot-patcher/issues/6 - patcher.patch('fetch', (url: string, options: RequestInit): Promise<Response> => { - const headers = options.headers as Record<string, string> - if (headers?.method) { - options.method = headers.method - delete headers.method - } - return fetch(url, options) - }) +/** + * @deprecated use `davGetClient` from `@nextcloud/files` + */ +export const getClient = (rootUrl = defaultRootUrl) => davGetClient(rootUrl) - return client; -} +export const client = davGetClient()
\ No newline at end of file diff --git a/apps/files/src/views/favorites.spec.ts b/apps/files/src/views/favorites.spec.ts index 4832f640d28..b14869a0589 100644 --- a/apps/files/src/views/favorites.spec.ts +++ b/apps/files/src/views/favorites.spec.ts @@ -23,7 +23,8 @@ import { basename } from 'path' import { expect } from '@jest/globals' import { Folder, Navigation, getNavigation } from '@nextcloud/files' -import eventBus, { emit } from '@nextcloud/event-bus' +import { CancelablePromise } from 'cancelable-promise' +import eventBus from '@nextcloud/event-bus' import * as initialState from '@nextcloud/initial-state' import { action } from '../actions/favoriteAction' @@ -57,7 +58,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') @@ -89,7 +90,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') @@ -132,7 +133,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') @@ -161,7 +162,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') @@ -201,7 +202,8 @@ describe('Dynamic update of favourite folders', () => { test('Renaming a favorite folder updates 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') @@ -212,8 +214,6 @@ describe('Dynamic update of favourite folders', () => { expect(favoritesView).toBeDefined() expect(favoriteFoldersViews.length).toBe(0) - // expect(eventBus.emit).toHaveBeenCalledTimes(2) - // Create new folder to favorite const folder = new Folder({ id: 1, @@ -233,7 +233,7 @@ describe('Dynamic update of favourite folders', () => { }) // Exec the rename action - emit('files:node:renamed', renamedFolder) + eventBus.emit('files:node:renamed', renamedFolder) expect(eventBus.emit).toHaveBeenNthCalledWith(2, 'files:node:renamed', renamedFolder) }) }) |