Files
Immich/web/src/routes/(user)/recently-added/[[photos=photos]]/[[assetId=id]]/+page.svelte
T
Mees FrenselandCodex f96816b55f refactor(web): bulk (un)archive actions (#32189)
(cherry picked from commit d26c0eec10f1bb49117b71b667c88a8a715b2489)
2026-10-27 23:12:21 +03:00

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}