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

View File

@ -870,14 +870,13 @@ export default class Timeline extends Mixins(GlobalMixin) {
}
/** Clear all selected photos */
clearSelection() {
clearSelection(only?: Set<IPhoto>) {
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;
heads.add(this.heads[photo.d.dayid]);
}
this.selection.clear();
this.selection.delete(photo);
});
heads.forEach(this.updateHeadSelected);
this.$forceUpdate();
}
@ -935,11 +934,12 @@ export default class Timeline extends Mixins(GlobalMixin) {
this.loading = true;
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;
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;
}
// Set of photos that are being deleted
const delPhotos = new Set<IPhoto>();
// Animate the deletion
for (const day of updatedDays) {
for (const row of day.rows) {
for (const photo of row.photos) {
if (delIds.has(photo.fileid)) {
photo.flag |= this.c.FLAG_LEAVING;
delPhotos.add(photo);
}
}
}
@ -973,8 +977,11 @@ export default class Timeline extends Mixins(GlobalMixin) {
// wait for 200ms
await new Promise(resolve => setTimeout(resolve, 200));
// clear selection at this point
this.clearSelection(delPhotos);
// Speculate day reflow for animation
const exitedLeft = new Set();
const exitedLeft = new Set<IPhoto>();
for (const day of updatedDays) {
let nextExit = false;
for (const row of day.rows) {
@ -1005,9 +1012,6 @@ export default class Timeline extends Mixins(GlobalMixin) {
photo.flag |= this.c.FLAG_ENTER_RIGHT;
});
// clear selection at this point
this.clearSelection();
// wait for 200ms
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
*
@ -166,12 +180,11 @@ export async function deleteFile(path: string) {
* @param fileIds list of file ids
* @returns list of file ids that were deleted
*/
export async function deleteFilesByIds(fileIds: number[]) {
const delIds = new Set<number>();
export async function* deleteFilesByIds(fileIds: number[]) {
const fileIdsSet = new Set(fileIds);
if (fileIds.length === 0) {
return delIds;
return;
}
// Get files data
@ -180,31 +193,22 @@ export async function deleteFilesByIds(fileIds: number[]) {
fileInfos = await getFiles(fileIds.filter(f => f));
} catch (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
for (const fileInfo of fileInfos) {
if (!fileIdsSet.has(fileInfo.fileid)) {
continue
fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
const calls = fileInfos.map((fileInfo) => async () => {
try {
await deleteFile(fileInfo.filename);
return fileInfo.fileid as number;
} catch {
console.error('Failed to delete', fileInfo.filename)
return 0;
}
});
promises.push((async () => {
try {
await deleteFile(fileInfo.filename);
delIds.add(fileInfo.fileid);
} catch {
console.error('Failed to delete', fileInfo.filename)
}
})());
}
await Promise.allSettled(promises);
return delIds;
yield* runInParallel(calls, 10);
}