(cherry picked from commit d26c0eec10f1bb49117b71b667c88a8a715b2489)
148 lines
6.1 KiB
Svelte
148 lines
6.1 KiB
Svelte
<script lang="ts">
|
|
import ActionMenuItem from '#lib/components/ActionMenuItem.svelte';
|
|
import UserPageLayout from '#lib/components/layouts/UserPageLayout.svelte';
|
|
import ButtonContextMenu from '#lib/components/shared-components/context-menu/ButtonContextMenu.svelte';
|
|
import EmptyPlaceholder from '#lib/components/shared-components/EmptyPlaceholder.svelte';
|
|
import ChangeDate from '#lib/components/timeline/actions/ChangeDateAction.svelte';
|
|
import ChangeDescription from '#lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
|
import ChangeLocation from '#lib/components/timeline/actions/ChangeLocationAction.svelte';
|
|
import DeleteAssets from '#lib/components/timeline/actions/DeleteAssetsAction.svelte';
|
|
import DownloadAction from '#lib/components/timeline/actions/DownloadAction.svelte';
|
|
import FavoriteAction from '#lib/components/timeline/actions/FavoriteAction.svelte';
|
|
import LinkLivePhotoAction from '#lib/components/timeline/actions/LinkLivePhotoAction.svelte';
|
|
import SelectAllAssets from '#lib/components/timeline/actions/SelectAllAction.svelte';
|
|
import SetVisibilityAction from '#lib/components/timeline/actions/SetVisibilityAction.svelte';
|
|
import AssetSelectControlBar from '#lib/components/timeline/AssetSelectControlBar.svelte';
|
|
import Timeline from '#lib/components/timeline/Timeline.svelte';
|
|
import { AssetAction } from '#lib/constants.ts';
|
|
import { assetMultiSelectManager } from '#lib/managers/asset-multi-select-manager.svelte.ts';
|
|
import { assetViewerManager } from '#lib/managers/asset-viewer-manager.svelte.ts';
|
|
import { TimelineManager } from '#lib/managers/timeline-manager/timeline-manager.svelte.ts';
|
|
import { getAssetBulkActions } from '#lib/services/asset.service.ts';
|
|
import { type OnLink, type OnUnlink } from '#lib/utils/actions.ts';
|
|
import { openFileUploadDialog } from '#lib/utils/file-uploader.ts';
|
|
import { AssetVisibility, AssetOrderBy } from '@immich/sdk';
|
|
import { ActionButton, CommandPaletteDefaultProvider } from '@immich/ui';
|
|
import { mdiDotsVertical } from '@mdi/js';
|
|
import { t } from 'svelte-i18n';
|
|
import type { PageData } from './$types';
|
|
import { getStackBulkActions } from '#lib/services/stack.service.ts';
|
|
|
|
type Props = {
|
|
data: PageData;
|
|
};
|
|
|
|
let { data }: Props = $props();
|
|
|
|
let timelineManager = $state<TimelineManager>() as TimelineManager;
|
|
const options = {
|
|
visibility: AssetVisibility.Timeline,
|
|
withStacked: true,
|
|
withPartners: true,
|
|
orderBy: AssetOrderBy.CreatedAt,
|
|
};
|
|
|
|
let selectedAssets = $derived(assetMultiSelectManager.assets);
|
|
let isLinkActionAvailable = $derived.by(() => {
|
|
const isLivePhoto = selectedAssets.length === 1 && !!selectedAssets[0].livePhotoVideoId;
|
|
const isLivePhotoCandidate =
|
|
selectedAssets.length === 2 &&
|
|
selectedAssets.some((asset) => asset.isImage) &&
|
|
selectedAssets.some((asset) => asset.isVideo);
|
|
|
|
return assetMultiSelectManager.isAllUserOwned && (isLivePhoto || isLivePhotoCandidate);
|
|
});
|
|
|
|
const handleEscape = () => {
|
|
if (assetViewerManager.isViewing) {
|
|
return;
|
|
}
|
|
if (assetMultiSelectManager.selectionActive) {
|
|
assetMultiSelectManager.clear();
|
|
return;
|
|
}
|
|
};
|
|
|
|
const handleLink: OnLink = ({ still, motion }) => {
|
|
timelineManager.removeAssets([motion.id]);
|
|
timelineManager.upsertAssets([still]);
|
|
};
|
|
|
|
const handleUnlink: OnUnlink = ({ still, motion }) => {
|
|
timelineManager.upsertAssets([motion]);
|
|
timelineManager.upsertAssets([still]);
|
|
};
|
|
|
|
const handleSetVisibility = (assetIds: string[]) => {
|
|
timelineManager.removeAssets(assetIds);
|
|
assetMultiSelectManager.clear();
|
|
};
|
|
</script>
|
|
|
|
<UserPageLayout hideNavbar={assetMultiSelectManager.selectionActive} title={data.meta.title} scrollbar={false}>
|
|
<Timeline
|
|
enableRouting={true}
|
|
bind:timelineManager
|
|
{options}
|
|
assetInteraction={assetMultiSelectManager}
|
|
removeAction={AssetAction.ARCHIVE}
|
|
onEscape={handleEscape}
|
|
withStacked
|
|
>
|
|
{#snippet empty()}
|
|
<EmptyPlaceholder text={$t('no_assets_message')} onClick={() => openFileUploadDialog()} class="mx-auto mt-10" />
|
|
{/snippet}
|
|
</Timeline>
|
|
</UserPageLayout>
|
|
|
|
{#if assetMultiSelectManager.selectionActive}
|
|
<AssetSelectControlBar>
|
|
{const Actions = $derived(getAssetBulkActions($t))}
|
|
{const StackActions = $derived(getStackBulkActions($t))}
|
|
<CommandPaletteDefaultProvider name={$t('assets')} actions={Object.values(Actions)} />
|
|
|
|
<ActionButton action={Actions.CreateSharedLink} />
|
|
<SelectAllAssets {timelineManager} assetInteraction={assetMultiSelectManager} />
|
|
<ActionButton action={Actions.AddToAlbum} />
|
|
|
|
{#if assetMultiSelectManager.isAllUserOwned}
|
|
<FavoriteAction
|
|
removeFavorite={assetMultiSelectManager.isAllFavorite}
|
|
onFavorite={(ids, isFavorite) => timelineManager.update(ids, (asset) => (asset.isFavorite = isFavorite))}
|
|
/>
|
|
|
|
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
|
<DownloadAction menuItem />
|
|
<ActionMenuItem action={StackActions.Stack} />
|
|
<ActionMenuItem action={StackActions.Unstack} />
|
|
{#if isLinkActionAvailable}
|
|
<LinkLivePhotoAction
|
|
menuItem
|
|
unlink={assetMultiSelectManager.assets.length === 1}
|
|
onLink={handleLink}
|
|
onUnlink={handleUnlink}
|
|
/>
|
|
{/if}
|
|
<ChangeDate menuItem />
|
|
<ChangeDescription menuItem />
|
|
<ChangeLocation menuItem />
|
|
<ActionMenuItem action={Actions.Archive} />
|
|
<ActionMenuItem action={Actions.Unarchive} />
|
|
<ActionMenuItem action={Actions.Tag} />
|
|
<DeleteAssets
|
|
menuItem
|
|
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}
|
|
onUndoDelete={(assets) => timelineManager.upsertAssets(assets)}
|
|
/>
|
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
|
<hr />
|
|
<ActionMenuItem action={Actions.RegenerateThumbnailJob} />
|
|
<ActionMenuItem action={Actions.RefreshMetadataJob} />
|
|
<ActionMenuItem action={Actions.TranscodeVideoJob} />
|
|
</ButtonContextMenu>
|
|
{:else}
|
|
<DownloadAction />
|
|
{/if}
|
|
</AssetSelectControlBar>
|
|
{/if}
|