Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(web): asset delete #7555

Merged
merged 3 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions web/src/lib/components/photos-page/asset-grid.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};

Expand Down Expand Up @@ -169,7 +169,7 @@
(await handleNext()) || (await handlePrevious()) || handleClose();

// delete after find the next one
assetStore.removeAsset(asset.id);
assetStore.removeAssets([asset.id]);
break;
}

Expand Down Expand Up @@ -414,7 +414,7 @@
<slot name="empty" />
{/if}
<section id="virtual-timeline" style:height={$assetStore.timelineHeight + 'px'}>
{#each $assetStore.buckets as bucket, bucketIndex (bucketIndex)}
{#each $assetStore.buckets as bucket (bucket.bucketDate)}
<IntersectionObserver
on:intersected={intersectedHandler}
on:hidden={() => assetStore.cancelBucket(bucket)}
Expand Down
40 changes: 18 additions & 22 deletions web/src/lib/stores/assets.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ interface DeleteAsset {
value: string;
}

interface TrashAsset {
interface TrashAssets {
type: 'trash';
value: string;
value: string[];
}

export const photoViewer = writable<HTMLImageElement | null>(null);

type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAsset;
type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAssets;

export class AssetStore {
private store$ = writable(this);
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
}

removeAsset(id: string) {
this.assets = this.assets.filter((asset) => asset.id !== id);
delete this.assetToBucket[id];
const idSet = new Set(ids);
this.assets = this.assets.filter((asset) => !idSet.has(asset.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<string | null> {
Expand Down
6 changes: 2 additions & 4 deletions web/src/lib/utils/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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`,
Expand Down
10 changes: 2 additions & 8 deletions web/src/routes/(user)/albums/[albumId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,6 @@
}
};

const handleRemoveAssets = (assetIds: string[]) => {
for (const assetId of assetIds) {
assetStore.removeAsset(assetId);
}
};

const handleCloseSelectAssets = () => {
viewMode = ViewMode.VIEW;
timelineInteractionStore.clearMultiselect();
Expand Down Expand Up @@ -434,10 +428,10 @@
{/if}
<DownloadAction menuItem filename="{album.albumName}.zip" />
{#if isOwned || isAllUserOwned}
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => handleRemoveAssets(assetIds)} />
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => assetStore.removeAssets(assetIds)} />
{/if}
{#if isAllUserOwned}
<DeleteAssets menuItem onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
<ChangeDate menuItem />
<ChangeLocation menuItem />
{/if}
Expand Down
4 changes: 2 additions & 2 deletions web/src/routes/(user)/archive/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@

{#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<ArchiveAction unarchive onArchive={(ids) => assetStore.removeAssets(ids)} />
<ArchiveAction unarchive onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
<CreateSharedLink />
<SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={mdiPlus} title="Add">
<AddToAlbum />
<AddToAlbum shared />
</AssetSelectContextMenu>
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<DeleteAssets onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
<DownloadAction menuItem />
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
Expand Down
6 changes: 3 additions & 3 deletions web/src/routes/(user)/favorites/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@
<!-- Multiselection mode app bar -->
{#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<FavoriteAction removeFavorite onFavorite={(ids) => assetStore.removeAssets(ids)} />
<FavoriteAction removeFavorite onFavorite={(assetIds) => assetStore.removeAssets(assetIds)} />
<CreateSharedLink />
<SelectAllAssets {assetStore} {assetInteractionStore} />
<AssetSelectContextMenu icon={mdiPlus} title="Add">
<AddToAlbum />
<AddToAlbum shared />
</AssetSelectContextMenu>
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<DeleteAssets onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
<DownloadAction menuItem />
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(ids) => assetStore.removeAssets(ids)} />
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
<ChangeDate menuItem />
<ChangeLocation menuItem />
</AssetSelectContextMenu>
Expand Down
4 changes: 2 additions & 2 deletions web/src/routes/(user)/people/[personId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,11 @@
<AddToAlbum />
<AddToAlbum shared />
</AssetSelectContextMenu>
<DeleteAssets onAssetDelete={(assetId) => $assetStore.removeAsset(assetId)} />
<DeleteAssets onAssetDelete={(assetIds) => $assetStore.removeAssets(assetIds)} />
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
<DownloadAction menuItem filename="{data.person.name || 'immich'}.zip" />
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(ids) => $assetStore.removeAssets(ids)} />
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(assetIds) => $assetStore.removeAssets(assetIds)} />
<MenuOption text="Fix incorrect match" on:click={handleReassignAssets} />
<ChangeDate menuItem />
<ChangeLocation menuItem />
Expand Down
6 changes: 3 additions & 3 deletions web/src/routes/(user)/photos/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@
</AssetSelectContextMenu>
<DeleteAssets
on:escape={() => (handleEscapeKey = true)}
onAssetDelete={(assetId) => assetStore.removeAsset(assetId)}
onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)}
/>
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
<DownloadAction menuItem />
<ArchiveAction menuItem onArchive={(ids) => assetStore.removeAssets(ids)} />
<ArchiveAction menuItem onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
{#if $selectedAssets.size > 1}
<StackAction onStack={(ids) => assetStore.removeAssets(ids)} />
<StackAction onStack={(assetIds) => assetStore.removeAssets(assetIds)} />
{/if}
<ChangeDate menuItem />
<ChangeLocation menuItem />
Expand Down
5 changes: 3 additions & 2 deletions web/src/routes/(user)/search/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions web/src/routes/(user)/trash/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
{#if $isMultiSelectState}
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
<SelectAllAssets {assetStore} {assetInteractionStore} />
<DeleteAssets force onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
<RestoreAssets onRestore={(ids) => assetStore.removeAssets(ids)} />
<DeleteAssets force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
<RestoreAssets onRestore={(assetIds) => assetStore.removeAssets(assetIds)} />
</AssetSelectControlBar>
{/if}

Expand Down