mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
perf(web): asset delete (#7555)
* perf(web): asset delete * update asset delete on search page * don't use arrow function in class
This commit is contained in:
parent
7303fab9d9
commit
a4e6c43823
@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
const trashOrDelete = async (force: boolean = false) => {
|
const trashOrDelete = async (force: boolean = false) => {
|
||||||
isShowDeleteConfirmation = false;
|
isShowDeleteConfirmation = false;
|
||||||
await deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
|
await deleteAssets(!(isTrashEnabled && !force), (assetIds) => assetStore.removeAssets(assetIds), idsSelectedAssets);
|
||||||
assetInteractionStore.clearMultiselect();
|
assetInteractionStore.clearMultiselect();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,7 +169,7 @@
|
|||||||
(await handleNext()) || (await handlePrevious()) || handleClose();
|
(await handleNext()) || (await handlePrevious()) || handleClose();
|
||||||
|
|
||||||
// delete after find the next one
|
// delete after find the next one
|
||||||
assetStore.removeAsset(asset.id);
|
assetStore.removeAssets([asset.id]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +414,7 @@
|
|||||||
<slot name="empty" />
|
<slot name="empty" />
|
||||||
{/if}
|
{/if}
|
||||||
<section id="virtual-timeline" style:height={$assetStore.timelineHeight + 'px'}>
|
<section id="virtual-timeline" style:height={$assetStore.timelineHeight + 'px'}>
|
||||||
{#each $assetStore.buckets as bucket, bucketIndex (bucketIndex)}
|
{#each $assetStore.buckets as bucket (bucket.bucketDate)}
|
||||||
<IntersectionObserver
|
<IntersectionObserver
|
||||||
on:intersected={intersectedHandler}
|
on:intersected={intersectedHandler}
|
||||||
on:hidden={() => assetStore.cancelBucket(bucket)}
|
on:hidden={() => assetStore.cancelBucket(bucket)}
|
||||||
|
@ -59,14 +59,14 @@ interface DeleteAsset {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TrashAsset {
|
interface TrashAssets {
|
||||||
type: 'trash';
|
type: 'trash';
|
||||||
value: string;
|
value: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const photoViewer = writable<HTMLImageElement | null>(null);
|
export const photoViewer = writable<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAsset;
|
type PendingChange = AddAsset | UpdateAsset | DeleteAsset | TrashAssets;
|
||||||
|
|
||||||
export class AssetStore {
|
export class AssetStore {
|
||||||
private store$ = writable(this);
|
private store$ = writable(this);
|
||||||
@ -105,7 +105,7 @@ export class AssetStore {
|
|||||||
this.addPendingChanges({ type: 'add', value: asset });
|
this.addPendingChanges({ type: 'add', value: asset });
|
||||||
}),
|
}),
|
||||||
websocketEvents.on('on_asset_trash', (ids) => {
|
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) => {
|
websocketEvents.on('on_asset_update', (asset) => {
|
||||||
this.addPendingChanges({ type: 'update', value: asset });
|
this.addPendingChanges({ type: 'update', value: asset });
|
||||||
@ -137,13 +137,13 @@ export class AssetStore {
|
|||||||
|
|
||||||
case 'trash': {
|
case 'trash': {
|
||||||
if (!this.options.isTrashed) {
|
if (!this.options.isTrashed) {
|
||||||
this.removeAsset(value);
|
this.removeAssets(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'delete': {
|
case 'delete': {
|
||||||
this.removeAsset(value);
|
this.removeAssets([value]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,7 +363,7 @@ export class AssetStore {
|
|||||||
|
|
||||||
const recalculate = asset.fileCreatedAt !== _asset.fileCreatedAt;
|
const recalculate = asset.fileCreatedAt !== _asset.fileCreatedAt;
|
||||||
if (recalculate) {
|
if (recalculate) {
|
||||||
this.removeAsset(asset.id);
|
this.removeAssets([asset.id]);
|
||||||
this.addAssetToBucket(_asset);
|
this.addAssetToBucket(_asset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -373,33 +373,29 @@ export class AssetStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeAssets(ids: string[]) {
|
removeAssets(ids: string[]) {
|
||||||
// TODO: this could probably be more efficient
|
const idSet = new Set(ids);
|
||||||
for (const id of ids) {
|
this.assets = this.assets.filter((asset) => !idSet.has(asset.id));
|
||||||
this.removeAsset(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAsset(id: string) {
|
// Iterate in reverse to allow array splicing.
|
||||||
this.assets = this.assets.filter((asset) => asset.id !== id);
|
for (let index = this.buckets.length - 1; index >= 0; index--) {
|
||||||
delete this.assetToBucket[id];
|
|
||||||
|
|
||||||
for (let index = 0; index < this.buckets.length; index++) {
|
|
||||||
const bucket = this.buckets[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_];
|
const asset = bucket.assets[index_];
|
||||||
if (asset.id !== id) {
|
if (!idSet.has(asset.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bucket.assets.splice(index_, 1);
|
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.buckets.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit(true);
|
delete this.assetToBucket[asset.id];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.emit(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPreviousAssetId(assetId: string): Promise<string | null> {
|
async getPreviousAssetId(assetId: string): Promise<string | null> {
|
||||||
|
@ -2,7 +2,7 @@ import { notificationController, NotificationType } from '$lib/components/shared
|
|||||||
import { deleteAssets as deleteBulk } from '@immich/sdk';
|
import { deleteAssets as deleteBulk } from '@immich/sdk';
|
||||||
import { handleError } from './handle-error';
|
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 OnRestore = (ids: string[]) => void;
|
||||||
export type OnArchive = (ids: string[], isArchived: boolean) => void;
|
export type OnArchive = (ids: string[], isArchived: boolean) => void;
|
||||||
export type OnFavorite = (ids: string[], favorite: 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[]) => {
|
export const deleteAssets = async (force: boolean, onAssetDelete: OnDelete, ids: string[]) => {
|
||||||
try {
|
try {
|
||||||
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
|
await deleteBulk({ assetBulkDeleteDto: { ids, force } });
|
||||||
for (const id of ids) {
|
onAssetDelete(ids);
|
||||||
onAssetDelete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
|
message: `${force ? 'Permanently deleted' : 'Trashed'} ${ids.length} assets`,
|
||||||
|
@ -328,12 +328,6 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveAssets = (assetIds: string[]) => {
|
|
||||||
for (const assetId of assetIds) {
|
|
||||||
assetStore.removeAsset(assetId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloseSelectAssets = () => {
|
const handleCloseSelectAssets = () => {
|
||||||
viewMode = ViewMode.VIEW;
|
viewMode = ViewMode.VIEW;
|
||||||
timelineInteractionStore.clearMultiselect();
|
timelineInteractionStore.clearMultiselect();
|
||||||
@ -434,10 +428,10 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
<DownloadAction menuItem filename="{album.albumName}.zip" />
|
||||||
{#if isOwned || isAllUserOwned}
|
{#if isOwned || isAllUserOwned}
|
||||||
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => handleRemoveAssets(assetIds)} />
|
<RemoveFromAlbum menuItem bind:album onRemove={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if isAllUserOwned}
|
{#if isAllUserOwned}
|
||||||
<DeleteAssets menuItem onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets menuItem onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -28,14 +28,14 @@
|
|||||||
|
|
||||||
{#if $isMultiSelectState}
|
{#if $isMultiSelectState}
|
||||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||||
<ArchiveAction unarchive onArchive={(ids) => assetStore.removeAssets(ids)} />
|
<ArchiveAction unarchive onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
||||||
|
@ -31,17 +31,17 @@
|
|||||||
<!-- Multiselection mode app bar -->
|
<!-- Multiselection mode app bar -->
|
||||||
{#if $isMultiSelectState}
|
{#if $isMultiSelectState}
|
||||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||||
<FavoriteAction removeFavorite onFavorite={(ids) => assetStore.removeAssets(ids)} />
|
<FavoriteAction removeFavorite onFavorite={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<CreateSharedLink />
|
<CreateSharedLink />
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
<AssetSelectContextMenu icon={mdiPlus} title="Add">
|
||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<DeleteAssets onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(ids) => assetStore.removeAssets(ids)} />
|
<ArchiveAction menuItem unarchive={isAllArchive} onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
|
@ -443,11 +443,11 @@
|
|||||||
<AddToAlbum />
|
<AddToAlbum />
|
||||||
<AddToAlbum shared />
|
<AddToAlbum shared />
|
||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<DeleteAssets onAssetDelete={(assetId) => $assetStore.removeAsset(assetId)} />
|
<DeleteAssets onAssetDelete={(assetIds) => $assetStore.removeAssets(assetIds)} />
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Add">
|
||||||
<DownloadAction menuItem filename="{data.person.name || 'immich'}.zip" />
|
<DownloadAction menuItem filename="{data.person.name || 'immich'}.zip" />
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<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} />
|
<MenuOption text="Fix incorrect match" on:click={handleReassignAssets} />
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
|
@ -61,14 +61,14 @@
|
|||||||
</AssetSelectContextMenu>
|
</AssetSelectContextMenu>
|
||||||
<DeleteAssets
|
<DeleteAssets
|
||||||
on:escape={() => (handleEscapeKey = true)}
|
on:escape={() => (handleEscapeKey = true)}
|
||||||
onAssetDelete={(assetId) => assetStore.removeAsset(assetId)}
|
onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)}
|
||||||
/>
|
/>
|
||||||
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
<AssetSelectContextMenu icon={mdiDotsVertical} title="Menu">
|
||||||
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
<FavoriteAction menuItem removeFavorite={isAllFavorite} onFavorite={() => assetStore.triggerUpdate()} />
|
||||||
<DownloadAction menuItem />
|
<DownloadAction menuItem />
|
||||||
<ArchiveAction menuItem onArchive={(ids) => assetStore.removeAssets(ids)} />
|
<ArchiveAction menuItem onArchive={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
{#if $selectedAssets.size > 1}
|
{#if $selectedAssets.size > 1}
|
||||||
<StackAction onStack={(ids) => assetStore.removeAssets(ids)} />
|
<StackAction onStack={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
{/if}
|
{/if}
|
||||||
<ChangeDate menuItem />
|
<ChangeDate menuItem />
|
||||||
<ChangeLocation menuItem />
|
<ChangeLocation menuItem />
|
||||||
|
@ -95,8 +95,9 @@
|
|||||||
$: isAllArchived = [...selectedAssets].every((asset) => asset.isArchived);
|
$: isAllArchived = [...selectedAssets].every((asset) => asset.isArchived);
|
||||||
$: isAllFavorite = [...selectedAssets].every((asset) => asset.isFavorite);
|
$: isAllFavorite = [...selectedAssets].every((asset) => asset.isFavorite);
|
||||||
|
|
||||||
const onAssetDelete = (assetId: string) => {
|
const onAssetDelete = (assetIds: string[]) => {
|
||||||
searchResultAssets = searchResultAssets.filter((a: AssetResponseDto) => a.id !== assetId);
|
const assetIdSet = new Set(assetIds);
|
||||||
|
searchResultAssets = searchResultAssets.filter((a: AssetResponseDto) => !assetIdSet.has(a.id));
|
||||||
};
|
};
|
||||||
const handleSelectAll = () => {
|
const handleSelectAll = () => {
|
||||||
selectedAssets = new Set(searchResultAssets);
|
selectedAssets = new Set(searchResultAssets);
|
||||||
|
@ -65,8 +65,8 @@
|
|||||||
{#if $isMultiSelectState}
|
{#if $isMultiSelectState}
|
||||||
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
<AssetSelectControlBar assets={$selectedAssets} clearSelect={() => assetInteractionStore.clearMultiselect()}>
|
||||||
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
<SelectAllAssets {assetStore} {assetInteractionStore} />
|
||||||
<DeleteAssets force onAssetDelete={(assetId) => assetStore.removeAsset(assetId)} />
|
<DeleteAssets force onAssetDelete={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
<RestoreAssets onRestore={(ids) => assetStore.removeAssets(ids)} />
|
<RestoreAssets onRestore={(assetIds) => assetStore.removeAssets(assetIds)} />
|
||||||
</AssetSelectControlBar>
|
</AssetSelectControlBar>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user