refactor: asset select manager (#27329)

This commit is contained in:
Jason Rasmussen 2026-03-27 14:23:33 -04:00 committed by GitHub
parent 14cce0cba3
commit 5889c42eb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 33 additions and 68 deletions

View File

@ -16,7 +16,7 @@
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
import { handlePromiseError } from '$lib/utils';
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
import type { AlbumResponseDto, SharedLinkResponseDto, UserResponseDto } from '@immich/sdk';
import type { AlbumResponseDto, SharedLinkResponseDto } from '@immich/sdk';
import { ActionButton, IconButton, Logo } from '@immich/ui';
import { mdiDownload, mdiFileImagePlusOutline, mdiPresentationPlay } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -26,10 +26,9 @@
interface Props {
sharedLink: SharedLinkResponseDto;
user?: UserResponseDto | undefined;
}
let { sharedLink, user = undefined }: Props = $props();
let { sharedLink }: Props = $props();
const album = sharedLink.album as AlbumResponseDto;
@ -97,11 +96,7 @@
<header>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar
ownerId={user?.id}
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
{#if sharedLink.allowDownload}
<DownloadAction filename="{album.albumName}.zip" />

View File

@ -336,11 +336,7 @@
{#if assetMultiSelectManager.selectionActive}
<div class="sticky top-0 z-1 dark">
<AssetSelectControlBar
forceDark
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar forceDark>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CreateSharedLink />
<IconButton

View File

@ -80,10 +80,7 @@
<header class="fixed top-0 inset-s-0 w-full">
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
<IconButton
shape="round"
color="secondary"

View File

@ -22,7 +22,7 @@
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import { getAssetBulkActions } from '$lib/services/asset.service';
import { mapSettings } from '$lib/stores/preferences.store';
import { preferences, user } from '$lib/stores/user.store';
import { preferences } from '$lib/stores/user.store';
import {
updateStackedAssetInTimeline,
updateUnstackedAssetInTimeline,
@ -122,11 +122,7 @@
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<Portal target="body">
<AssetSelectControlBar
ownerId={$user.id}
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
<CreateSharedLink />
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
<ActionButton action={Actions.AddToAlbum} />

View File

@ -1,32 +1,26 @@
<script lang="ts" module>
import { setAssetControlContext } from '$lib/utils/context';
import { t } from 'svelte-i18n';
</script>
<script lang="ts">
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
import { setAssetControlContext } from '$lib/utils/context';
import { mdiClose } from '@mdi/js';
import type { Snippet } from 'svelte';
import ControlAppBar from '../shared-components/control-app-bar.svelte';
import { t } from 'svelte-i18n';
type Props = {
assets: TimelineAsset[];
clearSelect: () => void;
ownerId?: string | undefined;
children?: Snippet;
forceDark?: boolean;
};
let { assets, clearSelect, ownerId = undefined, children, forceDark }: Props = $props();
let { children, forceDark }: Props = $props();
setAssetControlContext({
getAssets: () => assets,
getOwnedAssets: () => (ownerId === undefined ? assets : assets.filter((asset) => asset.ownerId === ownerId)),
clearSelect: () => clearSelect(),
});
setAssetControlContext(assetMultiSelectManager.asControlContext());
const onClose = () => assetMultiSelectManager.clear();
const assets = $derived(assetMultiSelectManager.assets);
</script>
<ControlAppBar onClose={clearSelect} {forceDark} backIcon={mdiClose} tailwindClasses="bg-white shadow-md">
<ControlAppBar {onClose} {forceDark} backIcon={mdiClose} tailwindClasses="bg-white shadow-md">
{#snippet leading()}
<div class="font-medium {forceDark ? 'text-immich-dark-primary' : 'text-primary'}">
<p class="block sm:hidden">{assets.length}</p>

View File

@ -43,7 +43,8 @@ export class AssetMultiSelectManager {
asControlContext(): AssetControlContext {
return {
getOwnedAssets: () => this.assets.filter((asset) => asset.ownerId === this.#userId),
getOwnedAssets: () =>
this.#userId ? this.assets.filter((asset) => asset.ownerId === this.#userId) : this.assets,
getAssets: () => this.assets,
clearSelect: () => this.clear(),
};

View File

@ -451,10 +451,7 @@
</main>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<CreateSharedLink />

View File

@ -59,7 +59,7 @@
</UserPageLayout>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<ArchiveAction

View File

@ -63,7 +63,7 @@
<!-- Multiselection mode app bar -->
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<FavoriteAction removeFavorite onFavorite={(assetIds) => timelineManager.removeAssets(assetIds)} />

View File

@ -110,7 +110,7 @@
{#if assetMultiSelectManager.selectionActive}
<div class="fixed top-0 start-0 w-full">
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<CreateSharedLink />

View File

@ -74,7 +74,7 @@
<!-- Multi-selection mode app bar -->
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
<SelectAllAssets withText {timelineManager} assetInteraction={assetMultiSelectManager} />
<SetVisibilityAction unlock onVisibilitySet={handleMoveOffLockedFolder} />
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>

View File

@ -39,7 +39,7 @@
</main>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<CreateSharedLink />

View File

@ -458,7 +458,7 @@
<header>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<CreateSharedLink />

View File

@ -25,7 +25,7 @@
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import { Route } from '$lib/route';
import { getAssetBulkActions } from '$lib/services/asset.service';
import { preferences, user } from '$lib/stores/user.store';
import { preferences } from '$lib/stores/user.store';
import { getAssetMediaUrl, memoryLaneTitle } from '$lib/utils';
import {
updateStackedAssetInTimeline,
@ -112,11 +112,7 @@
</UserPageLayout>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar
ownerId={$user.id}
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />

View File

@ -318,10 +318,7 @@
<section>
{#if assetMultiSelectManager.selectionActive}
<div class="fixed top-0 start-0 w-full z-2">
<AssetSelectControlBar
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />

View File

@ -27,7 +27,7 @@
import { Route } from '$lib/route';
import { getAssetBulkActions } from '$lib/services/asset.service';
import { getTagActions } from '$lib/services/tag.service';
import { preferences, user } from '$lib/stores/user.store';
import { preferences } from '$lib/stores/user.store';
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
import { getAllTags, type TagResponseDto } from '@immich/sdk';
import { ActionButton, CommandPaletteDefaultProvider, Text } from '@immich/ui';
@ -113,11 +113,7 @@
<section>
{#if assetMultiSelectManager.selectionActive}
<div class="fixed top-0 start-0 w-full">
<AssetSelectControlBar
ownerId={$user.id}
assets={assetMultiSelectManager.assets}
clearSelect={() => assetMultiSelectManager.clear()}
>
<AssetSelectControlBar>
{@const Actions = getAssetBulkActions($t, assetMultiSelectManager.asControlContext())}
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
<CreateSharedLink />

View File

@ -68,7 +68,7 @@
{/if}
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar assets={assetMultiSelectManager.assets} clearSelect={() => assetMultiSelectManager.clear()}>
<AssetSelectControlBar>
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
<DeleteAssets force onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)} />
<RestoreAssets onRestore={(assetIds) => timelineManager.removeAssets(assetIds)} />