fix: correctly cancel select all assets (#26067)

This commit is contained in:
Michel Heusschen
2026-02-10 17:47:23 +01:00
committed by GitHub
parent e6e56d75e2
commit a9e0fa43fa
6 changed files with 18 additions and 31 deletions

View File

@@ -1,7 +1,5 @@
<script lang="ts">
import { browser } from '$app/environment';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
import { IconButton } from '@immich/ui';
import { mdiClose } from '@mdi/js';
import { onDestroy, onMount, type Snippet } from 'svelte';
@@ -46,11 +44,6 @@
}
};
const handleClose = () => {
$isSelectingAllAssets = false;
onClose();
};
onMount(() => {
if (browser) {
document.addEventListener('scroll', onScroll, { passive: true });
@@ -82,7 +75,7 @@
{#if showBackButton}
<IconButton
aria-label={$t('close')}
onclick={handleClose}
onclick={onClose}
color="secondary"
shape="round"
variant="ghost"

View File

@@ -20,7 +20,6 @@
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
@@ -411,11 +410,7 @@
}
}
if (timelineManager.assetCount == assetInteraction.selectedAssets.length) {
isSelectingAllAssets.set(true);
} else {
isSelectingAllAssets.set(false);
}
assetInteraction.selectAll = timelineManager.assetCount === assetInteraction.selectedAssets.length;
};
const onSelectAssets = async (asset: TimelineAsset) => {
@@ -559,7 +554,7 @@
assetInteraction.removeGroupFromMultiselectGroup(groupTitle);
}
isSelectingAllAssets.set(timelineManager.assetCount === assetInteraction.selectedAssets.length);
assetInteraction.selectAll = timelineManager.assetCount === assetInteraction.selectedAssets.length;
};
const _onClick = (

View File

@@ -1,7 +1,6 @@
<script lang="ts">
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
import { cancelMultiselect, selectAllAssets } from '$lib/utils/asset-utils';
import { Button, IconButton } from '@immich/ui';
import { mdiSelectAll, mdiSelectRemove } from '@mdi/js';
@@ -14,6 +13,7 @@
}
let { timelineManager, assetInteraction, withText = false }: Props = $props();
const allAssetsSelected = $derived(assetInteraction.selectAll);
const handleSelectAll = async () => {
await selectAllAssets(timelineManager, assetInteraction);
@@ -26,21 +26,21 @@
{#if withText}
<Button
leadingIcon={$isSelectingAllAssets ? mdiSelectRemove : mdiSelectAll}
leadingIcon={allAssetsSelected ? mdiSelectRemove : mdiSelectAll}
size="medium"
color="secondary"
variant="ghost"
onclick={$isSelectingAllAssets ? handleCancel : handleSelectAll}
onclick={allAssetsSelected ? handleCancel : handleSelectAll}
>
{$isSelectingAllAssets ? $t('unselect_all') : $t('select_all')}
{allAssetsSelected ? $t('unselect_all') : $t('select_all')}
</Button>
{:else}
<IconButton
shape="round"
color="secondary"
variant="ghost"
aria-label={$isSelectingAllAssets ? $t('unselect_all') : $t('select_all')}
icon={$isSelectingAllAssets ? mdiSelectRemove : mdiSelectAll}
onclick={$isSelectingAllAssets ? handleCancel : handleSelectAll}
aria-label={allAssetsSelected ? $t('unselect_all') : $t('select_all')}
icon={allAssetsSelected ? mdiSelectRemove : mdiSelectAll}
onclick={allAssetsSelected ? handleCancel : handleSelectAll}
/>
{/if}

View File

@@ -6,6 +6,7 @@ import { fromStore } from 'svelte/store';
export class AssetInteraction {
selectedAssets = $state<TimelineAsset[]>([]);
selectAll = $state(false);
hasSelectedAsset(assetId: string) {
return this.selectedAssets.some((asset) => asset.id === assetId);
}
@@ -65,6 +66,8 @@ export class AssetInteraction {
}
clearMultiselect() {
this.selectAll = false;
// Multi-selection
this.selectedAssets = [];
this.selectedGroup.clear();

View File

@@ -1,3 +0,0 @@
import { writable } from 'svelte/store';
export const isSelectingAllAssets = writable(false);

View File

@@ -7,7 +7,6 @@ import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import { Route } from '$lib/route';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
import { preferences } from '$lib/stores/user.store';
import { downloadRequest, withError } from '$lib/utils';
import { getByteUnitString } from '$lib/utils/byte-units';
@@ -427,17 +426,17 @@ export const keepThisDeleteOthers = async (keepAsset: AssetResponseDto, stack: S
};
export const selectAllAssets = async (timelineManager: TimelineManager, assetInteraction: AssetInteraction) => {
if (get(isSelectingAllAssets)) {
if (assetInteraction.selectAll) {
// Selection is already ongoing
return;
}
isSelectingAllAssets.set(true);
assetInteraction.selectAll = true;
try {
for (const monthGroup of timelineManager.months) {
await timelineManager.loadMonthGroup(monthGroup.yearMonth);
if (!get(isSelectingAllAssets)) {
if (!assetInteraction.selectAll) {
assetInteraction.clearMultiselect();
break; // Cancelled
}
@@ -450,12 +449,12 @@ export const selectAllAssets = async (timelineManager: TimelineManager, assetInt
} catch (error) {
const $t = get(t);
handleError(error, $t('errors.error_selecting_all_assets'));
isSelectingAllAssets.set(false);
assetInteraction.selectAll = false;
}
};
export const cancelMultiselect = (assetInteraction: AssetInteraction) => {
isSelectingAllAssets.set(false);
assetInteraction.selectAll = false;
assetInteraction.clearMultiselect();
};