mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
fix: regression in select-all (#16969)
* bug: select-all * set->[] in interaction store, clear select-all on cancel * feedback --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
1a0a9ef36c
commit
9398b0d4b3
16 changed files with 82 additions and 82 deletions
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
{#if isShowConfirmation}
|
||||
<DeleteAssetDialog
|
||||
size={getOwnedAssets().size}
|
||||
size={getOwnedAssets().length}
|
||||
onConfirm={handleDelete}
|
||||
onCancel={() => (isShowConfirmation = false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
await downloadArchive(filename, { assetIds: assets.map((asset) => asset.id) });
|
||||
};
|
||||
|
||||
let menuItemIcon = $derived(getAssets().size === 1 ? mdiFileDownloadOutline : mdiFolderDownloadOutline);
|
||||
let menuItemIcon = $derived(getAssets().length === 1 ? mdiFileDownloadOutline : mdiFolderDownloadOutline);
|
||||
</script>
|
||||
|
||||
<svelte:window use:shortcut={{ shortcut: { key: 'd', shift: true }, onShortcut: handleDownloadFiles }} />
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
const removeFromAlbum = async () => {
|
||||
const isConfirmed = await dialogController.show({
|
||||
prompt: $t('remove_assets_album_confirmation', { values: { count: getAssets().size } }),
|
||||
prompt: $t('remove_assets_album_confirmation', { values: { count: getAssets().length } }),
|
||||
});
|
||||
|
||||
if (!isConfirmed) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
const handleRemove = async () => {
|
||||
const isConfirmed = await dialogController.show({
|
||||
title: $t('remove_assets_title'),
|
||||
prompt: $t('remove_assets_shared_link_confirmation', { values: { count: getAssets().size } }),
|
||||
prompt: $t('remove_assets_shared_link_confirmation', { values: { count: getAssets().length } }),
|
||||
confirmText: $t('remove'),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { AssetBucket } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetBucket, assetSnapshot, assetsSnapshot } from '$lib/stores/assets-store.svelte';
|
||||
import { navigate } from '$lib/utils/navigation';
|
||||
import { getDateLocaleString } from '$lib/utils/timeline-util';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
|
|
@ -71,9 +71,7 @@
|
|||
assetInteraction.removeGroupFromMultiselectGroup(groupTitle);
|
||||
}
|
||||
};
|
||||
const snapshotAssetArray = (assets: AssetResponseDto[]) => {
|
||||
return assets.map((a) => $state.snapshot(a));
|
||||
};
|
||||
|
||||
const assetMouseEventHandler = (groupTitle: string, asset: AssetResponseDto | null) => {
|
||||
// Show multi select icon on hover on date group
|
||||
hoveredDateGroup = groupTitle;
|
||||
|
|
@ -121,8 +119,8 @@
|
|||
<div
|
||||
transition:fly={{ x: -24, duration: 200, opacity: 0.5 }}
|
||||
class="inline-block px-2 hover:cursor-pointer"
|
||||
onclick={() => handleSelectGroup(dateGroup.groupTitle, snapshotAssetArray(dateGroup.getAssets()))}
|
||||
onkeydown={() => handleSelectGroup(dateGroup.groupTitle, snapshotAssetArray(dateGroup.getAssets()))}
|
||||
onclick={() => handleSelectGroup(dateGroup.groupTitle, assetsSnapshot(dateGroup.getAssets()))}
|
||||
onkeydown={() => handleSelectGroup(dateGroup.groupTitle, assetsSnapshot(dateGroup.getAssets()))}
|
||||
>
|
||||
{#if assetInteraction.selectedGroup.has(dateGroup.groupTitle)}
|
||||
<Icon path={mdiCheckCircle} size="24" color="#4250af" />
|
||||
|
|
@ -160,10 +158,10 @@
|
|||
{showArchiveIcon}
|
||||
{asset}
|
||||
{groupIndex}
|
||||
focussed={assetInteraction.isFocussedAsset(asset)}
|
||||
focussed={assetInteraction.isFocussedAsset(asset.id)}
|
||||
onClick={(asset) => onClick(dateGroup.getAssets(), dateGroup.groupTitle, asset)}
|
||||
onSelect={(asset) => assetSelectHandler(asset, dateGroup.getAssets(), dateGroup.groupTitle)}
|
||||
onMouseEvent={() => assetMouseEventHandler(dateGroup.groupTitle, $state.snapshot(asset))}
|
||||
onMouseEvent={() => assetMouseEventHandler(dateGroup.groupTitle, assetSnapshot(asset))}
|
||||
selected={assetInteraction.hasSelectedAsset(asset.id) || dateGroup.bucket.store.albumAssets.has(asset.id)}
|
||||
selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)}
|
||||
handleFocus={() => assetOnFocusHandler(asset)}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import type { Action } from '$lib/components/asset-viewer/actions/action';
|
||||
import { AppRoute, AssetAction } from '$lib/constants';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { AssetBucket, AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { AssetBucket, assetsSnapshot, AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||
import { isSearchEnabled } from '$lib/stores/search.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
|
|
@ -286,7 +286,7 @@
|
|||
};
|
||||
|
||||
const onDelete = () => {
|
||||
const hasTrashedAsset = assetInteraction.selectedAssetsArray.some((asset) => asset.isTrashed);
|
||||
const hasTrashedAsset = assetInteraction.selectedAssets.some((asset) => asset.isTrashed);
|
||||
|
||||
if ($showDeleteModal && (!isTrashEnabled || hasTrashedAsset)) {
|
||||
isShowDeleteConfirmation = true;
|
||||
|
|
@ -304,7 +304,7 @@
|
|||
};
|
||||
|
||||
const onStackAssets = async () => {
|
||||
const ids = await stackAssets(assetInteraction.selectedAssetsArray);
|
||||
const ids = await stackAssets(assetInteraction.selectedAssets);
|
||||
if (ids) {
|
||||
assetStore.removeAssets(ids);
|
||||
onEscape();
|
||||
|
|
@ -312,8 +312,8 @@
|
|||
};
|
||||
|
||||
const toggleArchive = async () => {
|
||||
await archiveAssets(assetInteraction.selectedAssetsArray, !assetInteraction.isAllArchived);
|
||||
assetStore.updateAssets(assetInteraction.selectedAssetsArray);
|
||||
await archiveAssets(assetInteraction.selectedAssets, !assetInteraction.isAllArchived);
|
||||
assetStore.updateAssets(assetInteraction.selectedAssets);
|
||||
deselectAllAssets();
|
||||
};
|
||||
|
||||
|
|
@ -450,7 +450,7 @@
|
|||
if (assetInteraction.selectedGroup.has(group)) {
|
||||
assetInteraction.removeGroupFromMultiselectGroup(group);
|
||||
for (const asset of assets) {
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
|
||||
}
|
||||
} else {
|
||||
assetInteraction.addGroupToMultiselectGroup(group);
|
||||
|
|
@ -471,15 +471,15 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const rangeSelection = assetInteraction.assetSelectionCandidates.size > 0;
|
||||
const rangeSelection = assetInteraction.assetSelectionCandidates.length > 0;
|
||||
const deselect = assetInteraction.hasSelectedAsset(asset.id);
|
||||
|
||||
// Select/deselect already loaded assets
|
||||
if (deselect) {
|
||||
for (const candidate of assetInteraction.assetSelectionCandidates) {
|
||||
assetInteraction.removeAssetFromMultiselectGroup(candidate);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(candidate.id);
|
||||
}
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
|
||||
} else {
|
||||
for (const candidate of assetInteraction.assetSelectionCandidates) {
|
||||
handleSelectAsset(candidate);
|
||||
|
|
@ -510,7 +510,7 @@
|
|||
await assetStore.loadBucket(bucket.bucketDate);
|
||||
for (const asset of bucket.getAssets()) {
|
||||
if (deselect) {
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
|
||||
} else {
|
||||
handleSelectAsset(asset);
|
||||
}
|
||||
|
|
@ -553,7 +553,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
const assets = assetStore.getAssets();
|
||||
const assets = assetsSnapshot(assetStore.getAssets());
|
||||
|
||||
let start = assets.findIndex((a) => a.id === startAsset.id);
|
||||
let end = assets.findIndex((a) => a.id === endAsset.id);
|
||||
|
|
@ -602,7 +602,7 @@
|
|||
|
||||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
||||
let isEmpty = $derived(assetStore.isInitialized && assetStore.buckets.length === 0);
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssetsArray.map(({ id }) => id));
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
||||
|
||||
$effect(() => {
|
||||
if (isEmpty) {
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
export interface AssetControlContext {
|
||||
// Wrap assets in a function, because context isn't reactive.
|
||||
getAssets: () => Set<AssetResponseDto>; // All assets includes partners' assets
|
||||
getOwnedAssets: () => Set<AssetResponseDto>; // Only assets owned by the user
|
||||
getAssets: () => AssetResponseDto[]; // All assets includes partners' assets
|
||||
getOwnedAssets: () => AssetResponseDto[]; // Only assets owned by the user
|
||||
clearSelect: () => void;
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
import type { Snippet } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
assets: Set<AssetResponseDto>;
|
||||
assets: AssetResponseDto[];
|
||||
clearSelect: () => void;
|
||||
ownerId?: string | undefined;
|
||||
children?: Snippet;
|
||||
|
|
@ -30,8 +30,7 @@
|
|||
|
||||
setContext({
|
||||
getAssets: () => assets,
|
||||
getOwnedAssets: () =>
|
||||
ownerId === undefined ? assets : new Set([...assets].filter((asset) => asset.ownerId === ownerId)),
|
||||
getOwnedAssets: () => (ownerId === undefined ? assets : assets.filter((asset) => asset.ownerId === ownerId)),
|
||||
clearSelect,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -39,8 +38,8 @@
|
|||
<ControlAppBar onClose={clearSelect} backIcon={mdiClose} tailwindClasses="bg-white shadow-md">
|
||||
{#snippet leading()}
|
||||
<div class="font-medium text-immich-primary dark:text-immich-dark-primary">
|
||||
<p class="block sm:hidden">{assets.size}</p>
|
||||
<p class="hidden sm:block">{$t('selected_count', { values: { count: assets.size } })}</p>
|
||||
<p class="block sm:hidden">{assets.length}</p>
|
||||
<p class="hidden sm:block">{$t('selected_count', { values: { count: assets.length } })}</p>
|
||||
</div>
|
||||
{/snippet}
|
||||
{#snippet trailing()}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
import CoordinatesInput from '$lib/components/shared-components/coordinates-input.svelte';
|
||||
import Map from '$lib/components/shared-components/map/map.svelte';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
interface Point {
|
||||
lng: number;
|
||||
lat: number;
|
||||
|
|
|
|||
|
|
@ -169,9 +169,9 @@
|
|||
// Select/deselect already loaded assets
|
||||
if (deselect) {
|
||||
for (const candidate of assetInteraction.assetSelectionCandidates) {
|
||||
assetInteraction.removeAssetFromMultiselectGroup(candidate);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(candidate.id);
|
||||
}
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset);
|
||||
assetInteraction.removeAssetFromMultiselectGroup(asset.id);
|
||||
} else {
|
||||
for (const candidate of assetInteraction.assetSelectionCandidates) {
|
||||
assetInteraction.selectAsset(candidate);
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
};
|
||||
|
||||
const onDelete = () => {
|
||||
const hasTrashedAsset = assetInteraction.selectedAssetsArray.some((asset) => asset.isTrashed);
|
||||
const hasTrashedAsset = assetInteraction.selectedAssets.some((asset) => asset.isTrashed);
|
||||
|
||||
if ($showDeleteModal && (!isTrashEnabled || hasTrashedAsset)) {
|
||||
isShowDeleteConfirmation = true;
|
||||
|
|
@ -245,7 +245,7 @@
|
|||
};
|
||||
|
||||
const toggleArchive = async () => {
|
||||
const ids = await archiveAssets(assetInteraction.selectedAssetsArray, !assetInteraction.isAllArchived);
|
||||
const ids = await archiveAssets(assetInteraction.selectedAssets, !assetInteraction.isAllArchived);
|
||||
if (ids) {
|
||||
assets = assets.filter((asset) => !ids.includes(asset.id));
|
||||
deselectAllAssets();
|
||||
|
|
@ -407,7 +407,7 @@
|
|||
};
|
||||
|
||||
let isTrashEnabled = $derived($featureFlags.loaded && $featureFlags.trash);
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssetsArray.map(({ id }) => id));
|
||||
let idsSelectedAssets = $derived(assetInteraction.selectedAssets.map(({ id }) => id));
|
||||
|
||||
$effect(() => {
|
||||
if (!lastAssetMouseEvent) {
|
||||
|
|
@ -438,7 +438,7 @@
|
|||
|
||||
{#if isShowDeleteConfirmation}
|
||||
<DeleteAssetDialog
|
||||
size={assetInteraction.selectedAssets.size}
|
||||
size={assetInteraction.selectedAssets.length}
|
||||
onCancel={() => (isShowDeleteConfirmation = false)}
|
||||
onConfirm={() => handlePromiseError(trashOrDelete(true))}
|
||||
/>
|
||||
|
|
@ -480,7 +480,7 @@
|
|||
{asset}
|
||||
selected={assetInteraction.hasSelectedAsset(asset.id)}
|
||||
selectionCandidate={assetInteraction.hasSelectionCandidate(asset.id)}
|
||||
focussed={assetInteraction.isFocussedAsset(asset)}
|
||||
focussed={assetInteraction.isFocussedAsset(asset.id)}
|
||||
thumbnailWidth={layout.width}
|
||||
thumbnailHeight={layout.height}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue