From a4e6c43823db20ee88e8b3d101fba5c2e62c938a Mon Sep 17 00:00:00 2001
From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
Date: Sat, 2 Mar 2024 01:49:31 +0100
Subject: [PATCH] perf(web): asset delete (#7555)
* perf(web): asset delete
* update asset delete on search page
* don't use arrow function in class
---
.../components/photos-page/asset-grid.svelte | 6 +--
web/src/lib/stores/assets.store.ts | 40 +++++++++----------
web/src/lib/utils/actions.ts | 6 +--
.../(user)/albums/[albumId]/+page.svelte | 10 +----
web/src/routes/(user)/archive/+page.svelte | 4 +-
web/src/routes/(user)/favorites/+page.svelte | 6 +--
.../(user)/people/[personId]/+page.svelte | 4 +-
web/src/routes/(user)/photos/+page.svelte | 6 +--
web/src/routes/(user)/search/+page.svelte | 5 ++-
web/src/routes/(user)/trash/+page.svelte | 4 +-
10 files changed, 40 insertions(+), 51 deletions(-)
diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte
index a512adaad5..10226a5ae4 100644
--- a/web/src/lib/components/photos-page/asset-grid.svelte
+++ b/web/src/lib/components/photos-page/asset-grid.svelte
@@ -72,7 +72,7 @@
const trashOrDelete = async (force: boolean = false) => {
isShowDeleteConfirmation = false;
- await deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
+ await deleteAssets(!(isTrashEnabled && !force), (assetIds) => assetStore.removeAssets(assetIds), idsSelectedAssets);
assetInteractionStore.clearMultiselect();
};
@@ -169,7 +169,7 @@
(await handleNext()) || (await handlePrevious()) || handleClose();
// delete after find the next one
- assetStore.removeAsset(asset.id);
+ assetStore.removeAssets([asset.id]);
break;
}
@@ -414,7 +414,7 @@
{/if}
- {#each $assetStore.buckets as bucket, bucketIndex (bucketIndex)}
+ {#each $assetStore.buckets as bucket (bucket.bucketDate)}
assetStore.cancelBucket(bucket)}
diff --git a/web/src/lib/stores/assets.store.ts b/web/src/lib/stores/assets.store.ts
index 9fde533936..418d530fee 100644
--- a/web/src/lib/stores/assets.store.ts
+++ b/web/src/lib/stores/assets.store.ts
@@ -59,14 +59,14 @@ interface DeleteAsset {
value: string;
}
-interface TrashAsset {
+interface TrashAssets {
type: 'trash';
- value: string;
+ value: string[];
}
export const photoViewer = writable(null);
-type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAsset;
+type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAssets;
export class AssetStore {
private store$ = writable(this);
@@ -105,7 +105,7 @@ export class AssetStore {
this.addPendingChanges({ type: 'add', value: asset });
}),
websocketEvents.on('on_asset_trash', (ids) => {
- this.addPendingChanges(...ids.map((id): TrashAsset => ({ type: 'trash', value: id })));
+ this.addPendingChanges({ type: 'trash', value: ids });
}),
websocketEvents.on('on_asset_update', (asset) => {
this.addPendingChanges({ type: 'update', value: asset });
@@ -137,13 +137,13 @@ export class AssetStore {
case 'trash': {
if (!this.options.isTrashed) {
- this.removeAsset(value);
+ this.removeAssets(value);
}
break;
}
case 'delete': {
- this.removeAsset(value);
+ this.removeAssets([value]);
break;
}
}
@@ -363,7 +363,7 @@ export class AssetStore {
const recalculate = asset.fileCreatedAt !== _asset.fileCreatedAt;
if (recalculate) {
- this.removeAsset(asset.id);
+ this.removeAssets([asset.id]);
this.addAssetToBucket(_asset);
return;
}
@@ -373,33 +373,29 @@ export class AssetStore {
}
removeAssets(ids: string[]) {
- // TODO: this could probably be more efficient
- for (const id of ids) {
- this.removeAsset(id);
- }
- }
+ const idSet = new Set(ids);
+ this.assets = this.assets.filter((asset) => !idSet.has(asset.id));
- removeAsset(id: string) {
- this.assets = this.assets.filter((asset) => asset.id !== id);
- delete this.assetToBucket[id];
-
- for (let index = 0; index < this.buckets.length; index++) {
+ // Iterate in reverse to allow array splicing.
+ for (let index = this.buckets.length - 1; index >= 0; index--) {
const bucket = this.buckets[index];
- for (let index_ = 0; index_ < bucket.assets.length; index_++) {
+ for (let index_ = bucket.assets.length - 1; index_ >= 0; index_--) {
const asset = bucket.assets[index_];
- if (asset.id !== id) {
+ if (!idSet.has(asset.id)) {
continue;
}
bucket.assets.splice(index_, 1);
- if (bucket.assets.length === 0) {
+ bucket.bucketCount = bucket.assets.length;
+ if (bucket.bucketCount === 0) {
this.buckets.splice(index, 1);
}
- this.emit(true);
- return;
+ delete this.assetToBucket[asset.id];
}
}
+
+ this.emit(false);
}
async getPreviousAssetId(assetId: string): Promise {
diff --git a/web/src/lib/utils/actions.ts b/web/src/lib/utils/actions.ts
index 981ef65d67..b6718e63a1 100644
--- a/web/src/lib/utils/actions.ts
+++ b/web/src/lib/utils/actions.ts
@@ -2,7 +2,7 @@ import { notificationController, NotificationType } from '$lib/components/shared
import { deleteAssets as deleteBulk } from '@immich/sdk';
import { handleError } from './handle-error';
-export type OnDelete = (assetId: string) => void;
+export type OnDelete = (assetIds: string[]) => void;
export type OnRestore = (ids: string[]) => void;
export type OnArchive = (ids: string[], isArchived: boolean) => void;
export type OnFavorite = (ids: string[], favorite: boolean) => void;
@@ -11,9 +11,7 @@ export type OnStack = (ids: string[]) => void;
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
try {
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
- for (const id of ids) {
- onAssetDelete(id);
- }
+ onAssetDelete(ids);
notificationController.show({
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
diff --git a/web/src/routes/(user)/albums/[albumId]/+page.svelte b/web/src/routes/(user)/albums/[albumId]/+page.svelte
index fca02b710e..4b87b4cd51 100644
--- a/web/src/routes/(user)/albums/[albumId]/+page.svelte
+++ b/web/src/routes/(user)/albums/[albumId]/+page.svelte
@@ -328,12 +328,6 @@
}
};
- const handleRemoveAssets = (assetIds: string[]) => {
- for (const assetId of assetIds) {
- assetStore.removeAsset(assetId);
- }
- };
-
const handleCloseSelectAssets = () => {
viewMode = ViewMode.VIEW;
timelineInteractionStore.clearMultiselect();
@@ -434,10 +428,10 @@
{/if}
{#if isOwned || isAllUserOwned}
- handleRemoveAssets(assetIds)} />
+ assetStore.removeAssets(assetIds)} />
{/if}
{#if isAllUserOwned}
- assetStore.removeAsset(assetId)} />
+ assetStore.removeAssets(assetIds)} />
{/if}
diff --git a/web/src/routes/(user)/archive/+page.svelte b/web/src/routes/(user)/archive/+page.svelte
index 09ca9d93e1..2278353547 100644
--- a/web/src/routes/(user)/archive/+page.svelte
+++ b/web/src/routes/(user)/archive/+page.svelte
@@ -28,14 +28,14 @@
{#if $isMultiSelectState}
assetInteractionStore.clearMultiselect()}>
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
- assetStore.removeAsset(assetId)} />
+ assetStore.removeAssets(assetIds)} />
assetStore.triggerUpdate()} />
diff --git a/web/src/routes/(user)/favorites/+page.svelte b/web/src/routes/(user)/favorites/+page.svelte
index ec7fd55f7c..21b563a50d 100644
--- a/web/src/routes/(user)/favorites/+page.svelte
+++ b/web/src/routes/(user)/favorites/+page.svelte
@@ -31,17 +31,17 @@
{#if $isMultiSelectState}
assetInteractionStore.clearMultiselect()}>
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
- assetStore.removeAsset(assetId)} />
+ assetStore.removeAssets(assetIds)} />
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
diff --git a/web/src/routes/(user)/people/[personId]/+page.svelte b/web/src/routes/(user)/people/[personId]/+page.svelte
index dd4f2d7bd1..abc3882f4b 100644
--- a/web/src/routes/(user)/people/[personId]/+page.svelte
+++ b/web/src/routes/(user)/people/[personId]/+page.svelte
@@ -443,11 +443,11 @@
- $assetStore.removeAsset(assetId)} />
+ $assetStore.removeAssets(assetIds)} />
assetStore.triggerUpdate()} />
- $assetStore.removeAssets(ids)} />
+ $assetStore.removeAssets(assetIds)} />
diff --git a/web/src/routes/(user)/photos/+page.svelte b/web/src/routes/(user)/photos/+page.svelte
index 6a68cfe57a..df2328aaa7 100644
--- a/web/src/routes/(user)/photos/+page.svelte
+++ b/web/src/routes/(user)/photos/+page.svelte
@@ -61,14 +61,14 @@
(handleEscapeKey = true)}
- onAssetDelete={(assetId) => assetStore.removeAsset(assetId)}
+ onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)}
/>
assetStore.triggerUpdate()} />
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
{#if $selectedAssets.size > 1}
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
{/if}
diff --git a/web/src/routes/(user)/search/+page.svelte b/web/src/routes/(user)/search/+page.svelte
index 28ad6bcbd7..b6c28f4b35 100644
--- a/web/src/routes/(user)/search/+page.svelte
+++ b/web/src/routes/(user)/search/+page.svelte
@@ -95,8 +95,9 @@
$: isAllArchived = [...selectedAssets].every((asset) => asset.isArchived);
$: isAllFavorite = [...selectedAssets].every((asset) => asset.isFavorite);
- const onAssetDelete = (assetId: string) => {
- searchResultAssets = searchResultAssets.filter((a: AssetResponseDto) => a.id !== assetId);
+ const onAssetDelete = (assetIds: string[]) => {
+ const assetIdSet = new Set(assetIds);
+ searchResultAssets = searchResultAssets.filter((a: AssetResponseDto) => !assetIdSet.has(a.id));
};
const handleSelectAll = () => {
selectedAssets = new Set(searchResultAssets);
diff --git a/web/src/routes/(user)/trash/+page.svelte b/web/src/routes/(user)/trash/+page.svelte
index 1a695bb2fc..cba8373b86 100644
--- a/web/src/routes/(user)/trash/+page.svelte
+++ b/web/src/routes/(user)/trash/+page.svelte
@@ -65,8 +65,8 @@
{#if $isMultiSelectState}
assetInteractionStore.clearMultiselect()}>
- assetStore.removeAsset(assetId)} />
- assetStore.removeAssets(ids)} />
+ assetStore.removeAssets(assetIds)} />
+ assetStore.removeAssets(assetIds)} />
{/if}