Don't overwhelm server with delete calls

pull/62/head
Varun Patil 2022-09-15 17:40:27 -07:00
parent 2fff228777
commit 69fb546cf2
3 changed files with 46 additions and 38 deletions

View File

@ -224,7 +224,7 @@ export default class Photo extends Mixins(GlobalMixin) {
&.exit-left { &.exit-left {
transition: all 0.2s ease-in; transition: all 0.2s ease-in;
transform: translateX(-20%); transform: translateX(-20%);
opacity: 0.4; opacity: 0.8;
} }
&.enter-right { &.enter-right {
animation: enter-right 0.2s ease-out forwards; animation: enter-right 0.2s ease-out forwards;
@ -232,7 +232,7 @@ export default class Photo extends Mixins(GlobalMixin) {
} }
@keyframes enter-right { @keyframes enter-right {
from { transform: translateX(20%); opacity: 0.4; } from { transform: translateX(20%); opacity: 0.8; }
to { transform: translateX(0); opacity: 1; } to { transform: translateX(0); opacity: 1; }
} }

View File

@ -870,14 +870,13 @@ export default class Timeline extends Mixins(GlobalMixin) {
} }
/** Clear all selected photos */ /** Clear all selected photos */
clearSelection() { clearSelection(only?: Set<IPhoto>) {
const heads = new Set<IHeadRow>(); const heads = new Set<IHeadRow>();
for (const photo of this.selection) { new Set(only || this.selection).forEach((photo: IPhoto) => {
photo.flag &= ~this.c.FLAG_SELECTED; photo.flag &= ~this.c.FLAG_SELECTED;
heads.add(this.heads[photo.d.dayid]); heads.add(this.heads[photo.d.dayid]);
} this.selection.delete(photo);
});
this.selection.clear();
heads.forEach(this.updateHeadSelected); heads.forEach(this.updateHeadSelected);
this.$forceUpdate(); this.$forceUpdate();
} }
@ -935,11 +934,12 @@ export default class Timeline extends Mixins(GlobalMixin) {
this.loading = true; this.loading = true;
const list = [...this.selection]; const list = [...this.selection];
const delIds = await dav.deleteFilesByIds(list.map(p => p.fileid)); for await (const delIds of dav.deleteFilesByIds(list.map(p => p.fileid))) {
const delIdsSet = new Set(delIds.filter(i => i));
const updatedDays = new Set(list.filter(f => delIdsSet.has(f.fileid)).map(f => f.d));
await this.deleteFromViewWithAnimation(delIdsSet, updatedDays);
}
this.loading = false; this.loading = false;
const updatedDays = new Set(list.filter(f => delIds.has(f.fileid)).map(f => f.d));
await this.deleteFromViewWithAnimation(delIds, updatedDays);
} }
/** /**
@ -959,12 +959,16 @@ export default class Timeline extends Mixins(GlobalMixin) {
return; return;
} }
// Set of photos that are being deleted
const delPhotos = new Set<IPhoto>();
// Animate the deletion // Animate the deletion
for (const day of updatedDays) { for (const day of updatedDays) {
for (const row of day.rows) { for (const row of day.rows) {
for (const photo of row.photos) { for (const photo of row.photos) {
if (delIds.has(photo.fileid)) { if (delIds.has(photo.fileid)) {
photo.flag |= this.c.FLAG_LEAVING; photo.flag |= this.c.FLAG_LEAVING;
delPhotos.add(photo);
} }
} }
} }
@ -973,8 +977,11 @@ export default class Timeline extends Mixins(GlobalMixin) {
// wait for 200ms // wait for 200ms
await new Promise(resolve => setTimeout(resolve, 200)); await new Promise(resolve => setTimeout(resolve, 200));
// clear selection at this point
this.clearSelection(delPhotos);
// Speculate day reflow for animation // Speculate day reflow for animation
const exitedLeft = new Set(); const exitedLeft = new Set<IPhoto>();
for (const day of updatedDays) { for (const day of updatedDays) {
let nextExit = false; let nextExit = false;
for (const row of day.rows) { for (const row of day.rows) {
@ -1005,9 +1012,6 @@ export default class Timeline extends Mixins(GlobalMixin) {
photo.flag |= this.c.FLAG_ENTER_RIGHT; photo.flag |= this.c.FLAG_ENTER_RIGHT;
}); });
// clear selection at this point
this.clearSelection();
// wait for 200ms // wait for 200ms
await new Promise(resolve => setTimeout(resolve, 200)); await new Promise(resolve => setTimeout(resolve, 200));

View File

@ -150,6 +150,20 @@ export async function getFolderPreviewFileIds(folderPath: string, limit: number)
})); }));
} }
/**
* Run promises in parallel, but only n at a time
* @param promises Array of promise generator funnction (async functions)
* @param n Number of promises to run in parallel
*/
export async function* runInParallel<T>(promises: (() => Promise<T>)[], n: number) {
while (promises.length > 0) {
const promisesToRun = promises.splice(0, n);
const resultsForThisBatch = await Promise.all(promisesToRun.map(p => p()));
yield resultsForThisBatch;
}
return;
}
/** /**
* Delete a single file * Delete a single file
* *
@ -166,12 +180,11 @@ export async function deleteFile(path: string) {
* @param fileIds list of file ids * @param fileIds list of file ids
* @returns list of file ids that were deleted * @returns list of file ids that were deleted
*/ */
export async function deleteFilesByIds(fileIds: number[]) { export async function* deleteFilesByIds(fileIds: number[]) {
const delIds = new Set<number>();
const fileIdsSet = new Set(fileIds); const fileIdsSet = new Set(fileIds);
if (fileIds.length === 0) { if (fileIds.length === 0) {
return delIds; return;
} }
// Get files data // Get files data
@ -180,31 +193,22 @@ export async function deleteFilesByIds(fileIds: number[]) {
fileInfos = await getFiles(fileIds.filter(f => f)); fileInfos = await getFiles(fileIds.filter(f => f));
} catch (e) { } catch (e) {
console.error('Failed to get file info for files to delete', fileIds, e); console.error('Failed to get file info for files to delete', fileIds, e);
return delIds; return;
} }
// Run all promises together
const promises: Promise<void>[] = [];
// Delete each file // Delete each file
for (const fileInfo of fileInfos) { fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
if (!fileIdsSet.has(fileInfo.fileid)) { const calls = fileInfos.map((fileInfo) => async () => {
continue
}
promises.push((async () => {
try { try {
await deleteFile(fileInfo.filename); await deleteFile(fileInfo.filename);
delIds.add(fileInfo.fileid); return fileInfo.fileid as number;
} catch { } catch {
console.error('Failed to delete', fileInfo.filename) console.error('Failed to delete', fileInfo.filename)
return 0;
} }
})()); });
}
await Promise.allSettled(promises); yield* runInParallel(calls, 10);
return delIds;
} }