Don't overwhelm server with delete calls
parent
2fff228777
commit
69fb546cf2
|
@ -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; }
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
promises.push((async () => {
|
||||
fileInfos = fileInfos.filter((f) => fileIdsSet.has(f.fileid));
|
||||
const calls = fileInfos.map((fileInfo) => async () => {
|
||||
try {
|
||||
await deleteFile(fileInfo.filename);
|
||||
delIds.add(fileInfo.fileid);
|
||||
return fileInfo.fileid as number;
|
||||
} catch {
|
||||
console.error('Failed to delete', fileInfo.filename)
|
||||
return 0;
|
||||
}
|
||||
})());
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
|
||||
return delIds;
|
||||
yield* runInParallel(calls, 10);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue