immich/web/src/lib/components/album-page/AlbumViewer.svelte
Brandon Wees 7c25bcc0a7
refactor: use ControlBar UI Library component (#28567)
* refactor: use ControlBar UI Library component

* chore: ci fix

* fix: memory viewer bar

* chore: rework e2e test

* chore: more ci fixes
2026-05-26 12:03:37 -04:00

152 lines
5.6 KiB
Svelte

<script lang="ts">
import { shortcut } from '$lib/actions/shortcut';
import AlbumMap from '$lib/components/album-page/AlbumMap.svelte';
import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte';
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
import Timeline from '$lib/components/timeline/Timeline.svelte';
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import { handleDownloadAlbum } from '$lib/services/album.service';
import { getGlobalActions } from '$lib/services/app.service';
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
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 } from '@immich/sdk';
import { ActionButton, IconButton, Logo } from '@immich/ui';
import { mdiDownload, mdiFileImagePlusOutline, mdiPresentationPlay } from '@mdi/js';
import { t } from 'svelte-i18n';
import ControlAppBar from '../shared-components/ControlAppBar.svelte';
import ThemeButton from '../shared-components/ThemeButton.svelte';
import AlbumSummary from './AlbumSummary.svelte';
interface Props {
sharedLink: SharedLinkResponseDto;
}
let { sharedLink }: Props = $props();
const album = sharedLink.album as AlbumResponseDto;
let { slideshowState, slideshowNavigation } = slideshowStore;
const options = $derived({ albumId: album.id, order: album.order });
let timelineManager = $state<TimelineManager>() as TimelineManager;
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {
handlePromiseError(fileUploadHandler({ files: value.files, albumId: album.id }));
dragAndDropFilesStore.set({ isDragging: false, files: [] });
}
});
const handleStartSlideshow = async () => {
const asset =
$slideshowNavigation === SlideshowNavigation.Shuffle
? await timelineManager.getRandomAsset()
: timelineManager.months[0]?.timelineDays[0]?.viewerAssets[0]?.asset;
if (asset) {
handlePromiseError(
assetViewerManager.setAssetId(asset.id).then(() => ($slideshowState = SlideshowState.PlaySlideshow)),
);
}
};
const { Cast } = $derived(getGlobalActions($t));
</script>
<svelte:document
use:shortcut={{
shortcut: { key: 'Escape' },
onShortcut: () => {
if (!assetViewerManager.isViewing && assetMultiSelectManager.selectionActive) {
assetMultiSelectManager.clear();
}
},
}}
/>
<main class="relative h-dvh overflow-hidden px-2 pt-(--navbar-height) max-md:pt-(--navbar-height-md) md:px-6">
<Timeline enableRouting={true} {album} bind:timelineManager {options} assetInteraction={assetMultiSelectManager}>
<section class="px-2 pt-8 md:px-0 md:pt-24">
<!-- ALBUM TITLE -->
<h1 class="text-2xl text-primary transition-all outline-none md:text-4xl lg:text-6xl">
{album.albumName}
</h1>
{#if album.assetCount > 0}
<AlbumSummary {album} />
{/if}
<!-- ALBUM DESCRIPTION -->
{#if album.description}
<p
class="mt-6 mb-12 w-full pb-2 text-start text-base font-medium whitespace-pre-line text-black dark:text-gray-300"
>
{album.description}
</p>
{/if}
</section>
</Timeline>
</main>
<header>
{#if assetMultiSelectManager.selectionActive}
<AssetSelectControlBar>
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
{#if sharedLink.allowDownload}
<DownloadAction filename="{album.albumName}.zip" />
{/if}
</AssetSelectControlBar>
{:else}
<ControlAppBar>
{#snippet leading()}
<a data-sveltekit-preload-data="hover" class="ms-4" href="/">
<Logo variant={mediaQueryManager.maxMd ? 'icon' : 'inline'} class="min-w-10" />
</a>
{/snippet}
{#snippet trailing()}
<ActionButton action={Cast} />
{#if sharedLink.allowUpload}
<IconButton
shape="round"
color="secondary"
variant="ghost"
aria-label={$t('add_photos')}
onclick={() => openFileUploadDialog({ albumId: album.id })}
icon={mdiFileImagePlusOutline}
/>
{/if}
{#if album.assetCount > 0 && sharedLink.allowDownload}
<IconButton
shape="round"
variant="ghost"
color="secondary"
aria-label={$t('slideshow')}
onclick={handleStartSlideshow}
icon={mdiPresentationPlay}
/>
<IconButton
shape="round"
color="secondary"
variant="ghost"
aria-label={$t('download')}
onclick={() => handleDownloadAlbum(album)}
icon={mdiDownload}
/>
{/if}
{#if sharedLink.showMetadata && featureFlagsManager.value.map}
<AlbumMap {album} />
{/if}
<ThemeButton />
{/snippet}
</ControlAppBar>
{/if}
</header>