mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04: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
@ -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}
|
||||
/>
|
||||
|
@ -4,43 +4,43 @@ import { SvelteSet } from 'svelte/reactivity';
|
||||
import { fromStore } from 'svelte/store';
|
||||
|
||||
export class AssetInteraction {
|
||||
readonly selectedAssets = new SvelteSet<AssetResponseDto>();
|
||||
selectedAssets = $state<AssetResponseDto[]>([]);
|
||||
hasSelectedAsset(assetId: string) {
|
||||
return [...this.selectedAssets.values()].some((asset) => asset.id === assetId);
|
||||
return this.selectedAssets.some((asset) => asset.id === assetId);
|
||||
}
|
||||
readonly selectedGroup = new SvelteSet<string>();
|
||||
assetSelectionCandidates = $state(new SvelteSet<AssetResponseDto>());
|
||||
selectedGroup = new SvelteSet<string>();
|
||||
assetSelectionCandidates = $state<AssetResponseDto[]>([]);
|
||||
hasSelectionCandidate(assetId: string) {
|
||||
return [...this.assetSelectionCandidates.values()].some((asset) => asset.id === assetId);
|
||||
return this.assetSelectionCandidates.some((asset) => asset.id === assetId);
|
||||
}
|
||||
assetSelectionStart = $state<AssetResponseDto | null>(null);
|
||||
focussedAssetId = $state<string | null>(null);
|
||||
|
||||
selectionActive = $derived(this.selectedAssets.size > 0);
|
||||
selectedAssetsArray = $derived([...this.selectedAssets]);
|
||||
selectionActive = $derived(this.selectedAssets.length > 0);
|
||||
|
||||
private user = fromStore<UserAdminResponseDto | undefined>(user);
|
||||
private userId = $derived(this.user.current?.id);
|
||||
|
||||
isAllTrashed = $derived(this.selectedAssetsArray.every((asset) => asset.isTrashed));
|
||||
isAllArchived = $derived(this.selectedAssetsArray.every((asset) => asset.isArchived));
|
||||
isAllFavorite = $derived(this.selectedAssetsArray.every((asset) => asset.isFavorite));
|
||||
isAllUserOwned = $derived(this.selectedAssetsArray.every((asset) => asset.ownerId === this.userId));
|
||||
isAllTrashed = $derived(this.selectedAssets.every((asset) => asset.isTrashed));
|
||||
isAllArchived = $derived(this.selectedAssets.every((asset) => asset.isArchived));
|
||||
isAllFavorite = $derived(this.selectedAssets.every((asset) => asset.isFavorite));
|
||||
isAllUserOwned = $derived(this.selectedAssets.every((asset) => asset.ownerId === this.userId));
|
||||
|
||||
selectAsset(asset: AssetResponseDto) {
|
||||
this.selectedAssets.add(asset);
|
||||
if (!this.hasSelectedAsset(asset.id)) {
|
||||
this.selectedAssets.push(asset);
|
||||
}
|
||||
}
|
||||
|
||||
selectAssets(assets: AssetResponseDto[]) {
|
||||
for (const asset of assets) {
|
||||
this.selectedAssets.add(asset);
|
||||
this.selectAsset(asset);
|
||||
}
|
||||
}
|
||||
|
||||
removeAssetFromMultiselectGroup(asset: AssetResponseDto) {
|
||||
const selectedAsset = [...this.selectedAssets.values()].find((a) => a.id === asset.id);
|
||||
if (selectedAsset) {
|
||||
this.selectedAssets.delete(selectedAsset);
|
||||
removeAssetFromMultiselectGroup(assetId: string) {
|
||||
const index = this.selectedAssets.findIndex((a) => a.id == assetId);
|
||||
if (index !== -1) {
|
||||
this.selectedAssets.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,24 +57,24 @@ export class AssetInteraction {
|
||||
}
|
||||
|
||||
setAssetSelectionCandidates(assets: AssetResponseDto[]) {
|
||||
this.assetSelectionCandidates = new SvelteSet(assets);
|
||||
this.assetSelectionCandidates = assets;
|
||||
}
|
||||
|
||||
clearAssetSelectionCandidates() {
|
||||
this.assetSelectionCandidates.clear();
|
||||
this.assetSelectionCandidates = [];
|
||||
}
|
||||
|
||||
clearMultiselect() {
|
||||
// Multi-selection
|
||||
this.selectedAssets.clear();
|
||||
this.selectedAssets = [];
|
||||
this.selectedGroup.clear();
|
||||
|
||||
// Range selection
|
||||
this.assetSelectionCandidates.clear();
|
||||
this.assetSelectionCandidates = [];
|
||||
this.assetSelectionStart = null;
|
||||
}
|
||||
|
||||
isFocussedAsset(asset: AssetResponseDto) {
|
||||
return this.focussedAssetId === asset.id;
|
||||
isFocussedAsset(assetId: string) {
|
||||
return this.focussedAssetId === assetId;
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,13 @@ function updateObject(target: any, source: any): boolean {
|
||||
return updated;
|
||||
}
|
||||
|
||||
export function assetSnapshot(asset: AssetResponseDto) {
|
||||
return $state.snapshot(asset);
|
||||
}
|
||||
|
||||
export function assetsSnapshot(assets: AssetResponseDto[]) {
|
||||
return assets.map((a) => $state.snapshot(a));
|
||||
}
|
||||
class IntersectingAsset {
|
||||
// --- public ---
|
||||
readonly #group: AssetDateGroup;
|
||||
@ -284,9 +291,11 @@ export class AssetBucket {
|
||||
get lastDateGroup() {
|
||||
return this.dateGroups.at(-1);
|
||||
}
|
||||
|
||||
getFirstAsset() {
|
||||
return this.dateGroups[0]?.getFirstAsset();
|
||||
}
|
||||
|
||||
getAssets() {
|
||||
// eslint-disable-next-line unicorn/no-array-reduce
|
||||
return this.dateGroups.reduce(
|
||||
|
@ -5,7 +5,7 @@ import { NotificationType, notificationController } from '$lib/components/shared
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { isSelectingAllAssets, type AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { assetsSnapshot, isSelectingAllAssets, type AssetStore } from '$lib/stores/assets-store.svelte';
|
||||
import { downloadManager } from '$lib/stores/download';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { downloadRequest, getKey, withError } from '$lib/utils';
|
||||
@ -367,7 +367,7 @@ export const getAssetType = (type: AssetTypeEnum) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getSelectedAssets = (assets: Set<AssetResponseDto>, user: UserResponseDto | null): string[] => {
|
||||
export const getSelectedAssets = (assets: AssetResponseDto[], user: UserResponseDto | null): string[] => {
|
||||
const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
|
||||
|
||||
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
||||
@ -474,15 +474,10 @@ export const selectAllAssets = async (assetStore: AssetStore, assetInteraction:
|
||||
await assetStore.loadBucket(bucket.bucketDate);
|
||||
|
||||
if (!get(isSelectingAllAssets)) {
|
||||
assetInteraction.clearMultiselect();
|
||||
break; // Cancelled
|
||||
}
|
||||
assetInteraction.selectAssets(bucket.getAssets().map((a) => $state.snapshot(a)));
|
||||
|
||||
// We use setTimeout to allow the UI to update. Otherwise, this may
|
||||
// cause a long delay between the start of 'select all' and the
|
||||
// effective update of the UI, depending on the number of assets
|
||||
// to select
|
||||
await delay(0);
|
||||
assetInteraction.selectAssets(assetsSnapshot(bucket.getAssets()));
|
||||
}
|
||||
} catch (error) {
|
||||
const $t = get(t);
|
||||
|
@ -249,7 +249,7 @@
|
||||
album = await getAlbumInfo({ id: album.id, withoutAssets: true });
|
||||
};
|
||||
const handleAddAssets = async () => {
|
||||
const assetIds = timelineInteraction.selectedAssetsArray.map((asset) => asset.id);
|
||||
const assetIds = timelineInteraction.selectedAssets.map((asset) => asset.id);
|
||||
|
||||
try {
|
||||
const results = await addAssetsToAlbum({
|
||||
@ -364,7 +364,7 @@
|
||||
};
|
||||
|
||||
const updateThumbnailUsingCurrentSelection = async () => {
|
||||
if (assetInteraction.selectedAssets.size === 1) {
|
||||
if (assetInteraction.selectedAssets.length === 1) {
|
||||
const [firstAsset] = assetInteraction.selectedAssets;
|
||||
assetInteraction.clearMultiselect();
|
||||
await updateThumbnail(firstAsset.id);
|
||||
@ -479,7 +479,7 @@
|
||||
{#if assetInteraction.isAllUserOwned}
|
||||
<ChangeDate menuItem />
|
||||
<ChangeLocation menuItem />
|
||||
{#if assetInteraction.selectedAssets.size === 1}
|
||||
{#if assetInteraction.selectedAssets.length === 1}
|
||||
<MenuOption
|
||||
text={$t('set_as_album_cover')}
|
||||
icon={mdiImageOutline}
|
||||
@ -574,7 +574,7 @@
|
||||
{#if !timelineInteraction.selectionActive}
|
||||
{$t('add_to_album')}
|
||||
{:else}
|
||||
{$t('selected_count', { values: { count: timelineInteraction.selectedAssets.size } })}
|
||||
{$t('selected_count', { values: { count: timelineInteraction.selectedAssets.length } })}
|
||||
{/if}
|
||||
</p>
|
||||
{/snippet}
|
||||
|
@ -149,7 +149,7 @@
|
||||
});
|
||||
|
||||
const handleUnmerge = () => {
|
||||
assetStore.removeAssets(assetInteraction.selectedAssetsArray.map((a) => a.id));
|
||||
assetStore.removeAssets(assetInteraction.selectedAssets.map((a) => a.id));
|
||||
assetInteraction.clearMultiselect();
|
||||
viewMode = PersonPageViewMode.VIEW_ASSETS;
|
||||
};
|
||||
@ -368,7 +368,7 @@
|
||||
|
||||
{#if viewMode === PersonPageViewMode.UNASSIGN_ASSETS}
|
||||
<UnMergeFaceSelector
|
||||
assetIds={assetInteraction.selectedAssetsArray.map((a) => a.id)}
|
||||
assetIds={assetInteraction.selectedAssets.map((a) => a.id)}
|
||||
personAssets={person}
|
||||
onClose={() => (viewMode = PersonPageViewMode.VIEW_ASSETS)}
|
||||
onConfirm={handleUnmerge}
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
const assetInteraction = new AssetInteraction();
|
||||
|
||||
let selectedAssets = $derived(assetInteraction.selectedAssetsArray);
|
||||
let selectedAssets = $derived(assetInteraction.selectedAssets);
|
||||
let isAssetStackSelected = $derived(selectedAssets.length === 1 && !!selectedAssets[0].stack);
|
||||
let isLinkActionAvailable = $derived.by(() => {
|
||||
const isLivePhoto = selectedAssets.length === 1 && !!selectedAssets[0].livePhotoVideoId;
|
||||
@ -97,7 +97,7 @@
|
||||
></FavoriteAction>
|
||||
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||
<DownloadAction menuItem />
|
||||
{#if assetInteraction.selectedAssets.size > 1 || isAssetStackSelected}
|
||||
{#if assetInteraction.selectedAssets.length > 1 || isAssetStackSelected}
|
||||
<StackAction
|
||||
unstack={isAssetStackSelected}
|
||||
onStack={(assetIds) => assetStore.removeAssets(assetIds)}
|
||||
@ -107,7 +107,7 @@
|
||||
{#if isLinkActionAvailable}
|
||||
<LinkLivePhotoAction
|
||||
menuItem
|
||||
unlink={assetInteraction.selectedAssets.size === 1}
|
||||
unlink={assetInteraction.selectedAssets.length === 1}
|
||||
onLink={handleLink}
|
||||
onUnlink={handleUnlink}
|
||||
/>
|
||||
|
@ -85,7 +85,7 @@
|
||||
}
|
||||
|
||||
if (assetInteraction.selectionActive) {
|
||||
assetInteraction.selectedAssets.clear();
|
||||
assetInteraction.selectedAssets = [];
|
||||
return;
|
||||
}
|
||||
if (!$preventRaceConditionSearchBar) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user