From bdc964d2ee751afa4a83792fb35e20131f96f396 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Thu, 15 Sep 2022 18:41:51 -0700 Subject: [PATCH] Implement selecting and favorite (fix #26) --- src/components/Timeline.vue | 40 ++++++++++++++++++++++ src/services/DavRequests.ts | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index 406b2fba..b00a74ee 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -102,6 +102,14 @@ {{ t('memories', 'Delete') }} + + + {{ t('memories', 'Toggle Favorite') }} + + @@ -932,6 +940,38 @@ export default class Timeline extends Mixins(GlobalMixin) { } await dav.downloadFilesByIds(Array.from(this.selection.keys())); } + + /** + * Check if all files selected currently are favorites + */ + allSelectedFavorites() { + return Array.from(this.selection.values()).every(p => p.flag & this.c.FLAG_IS_FAVORITE); + } + + /** + * Favorite the currently selected photos + */ + async favoriteSelection() { + try { + const val = !this.allSelectedFavorites(); + this.loading++; + for await (const favIds of dav.favoriteFilesByIds(Array.from(this.selection.keys()), val)) { + favIds.forEach(id => { + const photo = this.selection.get(id); + if (!photo) { + return; + } + + if (val) { + photo.flag |= this.c.FLAG_IS_FAVORITE; + } else { + photo.flag &= ~this.c.FLAG_IS_FAVORITE; + } + }); + } + } finally { + this.loading--; + } } /** diff --git a/src/services/DavRequests.ts b/src/services/DavRequests.ts index 7ff50033..9cad3fe0 100644 --- a/src/services/DavRequests.ts +++ b/src/services/DavRequests.ts @@ -265,3 +265,69 @@ export async function downloadFilesByIds(fileIds: number[]) { const fileInfos = await getFiles(fileIds); await downloadFiles(fileInfos.map(f => f.filename)); } + +/** + * Favorite a file + * https://github.com/nextcloud/photos/blob/7687e214f9b0f71a2cc73778b8b22ab781490a3b/src/services/FileActions.js + * + * @param fileName - The file's name + * @param favoriteState - The new favorite state + */ + export async function favoriteFile(fileName: string, favoriteState: boolean) { + let encodedPath = encodePath(fileName) + while (encodedPath[0] === '/') { + encodedPath = encodedPath.substring(1) + } + + try { + return axios.post( + `${generateUrl('/apps/files/api/v1/files/')}${encodedPath}`, + { + tags: favoriteState ? ['_$!!$_'] : [], + }, + ) + } catch (error) { + console.error(t('memories', 'Failed to favorite {fileName}.', { fileName }), error) + showError(t('memories', 'Failed to favorite {fileName}.', { fileName })) + } +} + +/** + * Favorite all files in a given list of Ids + * + * @param fileIds list of file ids + * @param favoriteState the new favorite state + * @returns generator of lists of file ids that were state-changed + */ + export async function* favoriteFilesByIds(fileIds: number[], favoriteState: boolean) { + const fileIdsSet = new Set(fileIds); + + if (fileIds.length === 0) { + return; + } + + // Get files data + let fileInfos: any[] = []; + try { + fileInfos = await getFiles(fileIds.filter(f => f)); + } catch (e) { + showError(t('memories', 'Failed to favorite files.')); + console.error('Failed to get file info for files to favorite', fileIds, e); + return; + } + + // Favorite each file + fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid)); + const calls = fileInfos.map((fileInfo) => async () => { + try { + await favoriteFile(fileInfo.filename, favoriteState); + return fileInfo.fileid as number; + } catch (error) { + console.error(t('memories', 'Failed to favorite {fileName}.', fileInfo), error); + showError(t('memories', 'Failed to favorite {fileName}.', fileInfo)); + return 0; + } + }); + + yield* runInParallel(calls, 10); +} \ No newline at end of file