|
3 | 3 | * SPDX-License-Identifier: AGPL-3.0-or-later
|
4 | 4 | */
|
5 | 5 | import type { ContentsWithRoot } from '@nextcloud/files'
|
| 6 | +import type { FileStat, ResponseDataDetailed } from 'webdav' |
6 | 7 |
|
7 | 8 | import { CancelablePromise } from 'cancelable-promise'
|
8 |
| -import { File, Folder, Permission, } from '@nextcloud/files' |
9 |
| -import { generateOcsUrl } from '@nextcloud/router' |
| 9 | +import { File, Folder, Permission } from '@nextcloud/files' |
10 | 10 | import { getCurrentUser } from '@nextcloud/auth'
|
11 |
| -import { getRemoteURL, getRootPath } from '@nextcloud/files/dav' |
12 |
| -import axios from '@nextcloud/axios' |
| 11 | +import { getDefaultPropfind, getRemoteURL, registerDavProperty, resultToNode } from '@nextcloud/files/dav' |
| 12 | +import { client } from './WebdavClient' |
| 13 | +import logger from '../logger' |
| 14 | +import { getCapabilities } from '@nextcloud/capabilities' |
| 15 | +import { getContents as getRecentContents } from './Recent' |
13 | 16 |
|
14 |
| -import { getContents as getDefaultContents } from './Files' |
15 |
| - |
16 |
| -type RecommendedFiles = { |
17 |
| - 'id': string |
18 |
| - 'timestamp': number |
19 |
| - 'name': string |
20 |
| - 'directory': string |
21 |
| - 'extension': string |
22 |
| - 'mimeType': string |
23 |
| - 'hasPreview': boolean |
24 |
| - 'reason': string |
25 |
| -} |
26 |
| - |
27 |
| -type RecommendedFilesResponse = { |
28 |
| - 'recommendations': RecommendedFiles[] |
| 17 | +// Check if the recommendations capability is enabled |
| 18 | +// If not, we'll just use recent files |
| 19 | +const isRecommendationEnabled = getCapabilities()?.recommendations?.enabled === true |
| 20 | +if (isRecommendationEnabled) { |
| 21 | + registerDavProperty('nc:recommendation-reason', { nc: 'http://nextcloud.org/ns' }) |
| 22 | + registerDavProperty('nc:recommendation-reason-label', { nc: 'http://nextcloud.org/ns' }) |
29 | 23 | }
|
30 | 24 |
|
31 |
| -const fetchRecommendedFiles = (controller: AbortController): Promise<RecommendedFilesResponse> => { |
32 |
| - const url = generateOcsUrl('apps/recommendations/api/v1/recommendations/always') |
33 |
| - |
34 |
| - return axios.get(url, { |
35 |
| - signal: controller.signal, |
36 |
| - headers: { |
37 |
| - 'OCS-APIRequest': 'true', |
38 |
| - 'Content-Type': 'application/json', |
39 |
| - }, |
40 |
| - }).then(resp => resp.data.ocs.data as RecommendedFilesResponse) |
41 |
| -} |
42 |
| - |
43 |
| -export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> => { |
44 |
| - if (path !== '/') { |
45 |
| - return getDefaultContents(path) |
| 25 | +export const getContents = (): CancelablePromise<ContentsWithRoot> => { |
| 26 | + if (!isRecommendationEnabled) { |
| 27 | + logger.debug('Recommendations capability is not enabled, falling back to recent files') |
| 28 | + return getRecentContents() |
46 | 29 | }
|
47 | 30 |
|
48 | 31 | const controller = new AbortController()
|
49 |
| - return new CancelablePromise(async (resolve, reject, cancel) => { |
50 |
| - cancel(() => controller.abort()) |
| 32 | + const propfindPayload = getDefaultPropfind() |
| 33 | + |
| 34 | + return new CancelablePromise(async (resolve, reject, onCancel) => { |
| 35 | + onCancel(() => controller.abort()) |
| 36 | + |
| 37 | + const root = `/recommendations/${getCurrentUser()?.uid}` |
51 | 38 | try {
|
52 |
| - const { recommendations } = await fetchRecommendedFiles(controller) |
| 39 | + const contentsResponse = await client.getDirectoryContents(root, { |
| 40 | + details: true, |
| 41 | + data: propfindPayload, |
| 42 | + includeSelf: false, |
| 43 | + signal: controller.signal, |
| 44 | + }) as ResponseDataDetailed<FileStat[]> |
53 | 45 |
|
| 46 | + const contents = contentsResponse.data |
54 | 47 | resolve({
|
55 | 48 | folder: new Folder({
|
56 | 49 | id: 0,
|
57 |
| - source: `${getRemoteURL()}${getRootPath()}`, |
58 |
| - root: getRootPath(), |
| 50 | + source: `${getRemoteURL()}${root}`, |
| 51 | + root, |
59 | 52 | owner: getCurrentUser()?.uid || null,
|
60 | 53 | permissions: Permission.READ,
|
61 | 54 | }),
|
62 |
| - contents: recommendations.map((rec) => { |
63 |
| - const Node = rec.mimeType === 'httpd/unix-directory' ? Folder : File |
64 |
| - return new Node({ |
65 |
| - id: parseInt(rec.id), |
66 |
| - source: `${getRemoteURL()}/${getRootPath()}/${rec.directory}/${rec.name}`.replace(/\/\//g, '/'), |
67 |
| - root: getRootPath(), |
68 |
| - mime: rec.mimeType, |
69 |
| - mtime: new Date(rec.timestamp * 1000), |
70 |
| - owner: getCurrentUser()?.uid || null, |
71 |
| - permissions: Permission.READ, |
72 |
| - attributes: rec, |
73 |
| - }) |
74 |
| - }), |
| 55 | + contents: contents.map((result) => { |
| 56 | + try { |
| 57 | + return resultToNode(result, root) |
| 58 | + } catch (error) { |
| 59 | + logger.error(`Invalid node detected '${result.basename}'`, { error }) |
| 60 | + return null |
| 61 | + } |
| 62 | + }).filter(Boolean) as File[], |
75 | 63 | })
|
76 | 64 | } catch (error) {
|
77 | 65 | reject(error)
|
|
0 commit comments