mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
refactor(web): asset viewer actions (#11449)
* refactor(web): asset viewer actions * motion photo slot and more refactoring
This commit is contained in:
parent
3a3ea6135e
commit
281cfc95a4
27 changed files with 682 additions and 476 deletions
|
|
@ -1,135 +1,80 @@
|
|||
<script lang="ts">
|
||||
import type { OnAction } from '$lib/components/asset-viewer/actions/action';
|
||||
import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte';
|
||||
import ArchiveAction from '$lib/components/asset-viewer/actions/archive-action.svelte';
|
||||
import CloseAction from '$lib/components/asset-viewer/actions/close-action.svelte';
|
||||
import DeleteAction from '$lib/components/asset-viewer/actions/delete-action.svelte';
|
||||
import DownloadAction from '$lib/components/asset-viewer/actions/download-action.svelte';
|
||||
import FavoriteAction from '$lib/components/asset-viewer/actions/favorite-action.svelte';
|
||||
import RestoreAction from '$lib/components/asset-viewer/actions/restore-action.svelte';
|
||||
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte';
|
||||
import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte';
|
||||
import ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte';
|
||||
import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte';
|
||||
import UnstackAction from '$lib/components/asset-viewer/actions/unstack-action.svelte';
|
||||
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||
import DeleteButton from './delete-button.svelte';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { photoZoomState } from '$lib/stores/zoom-image.store';
|
||||
import { getAssetJobName } from '$lib/utils';
|
||||
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { AssetJobName, AssetTypeEnum, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk';
|
||||
import {
|
||||
mdiAccountCircleOutline,
|
||||
mdiAlertOutline,
|
||||
mdiArchiveArrowDownOutline,
|
||||
mdiArchiveArrowUpOutline,
|
||||
mdiArrowLeft,
|
||||
mdiCogRefreshOutline,
|
||||
mdiContentCopy,
|
||||
mdiDatabaseRefreshOutline,
|
||||
mdiDotsVertical,
|
||||
mdiFolderDownloadOutline,
|
||||
mdiHeart,
|
||||
mdiHeartOutline,
|
||||
mdiHistory,
|
||||
mdiImageAlbum,
|
||||
mdiImageMinusOutline,
|
||||
mdiImageOutline,
|
||||
mdiImageRefreshOutline,
|
||||
mdiInformationOutline,
|
||||
mdiMagnifyMinusOutline,
|
||||
mdiMagnifyPlusOutline,
|
||||
mdiMotionPauseOutline,
|
||||
mdiPlaySpeed,
|
||||
mdiPresentationPlay,
|
||||
mdiShareVariantOutline,
|
||||
mdiUpload,
|
||||
} from '@mdi/js';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import MenuOption from '../shared-components/context-menu/menu-option.svelte';
|
||||
import { canCopyImagesToClipboard } from 'copy-image-clipboard';
|
||||
import { t } from 'svelte-i18n';
|
||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
export let album: AlbumResponseDto | null = null;
|
||||
export let showCopyButton: boolean;
|
||||
export let showZoomButton: boolean;
|
||||
export let showMotionPlayButton: boolean;
|
||||
export let isMotionPhotoPlaying = false;
|
||||
export let showDownloadButton: boolean;
|
||||
export let stackedAssets: AssetResponseDto[];
|
||||
export let showDetailButton: boolean;
|
||||
export let showShareButton: boolean;
|
||||
export let showSlideshow = false;
|
||||
export let hasStackChildren = false;
|
||||
export let onZoomImage: () => void;
|
||||
export let onCopyImage: () => void;
|
||||
export let onAction: OnAction;
|
||||
export let onRunJob: (name: AssetJobName) => void;
|
||||
export let onPlaySlideshow: () => void;
|
||||
export let onShowDetail: () => void;
|
||||
export let onClose: () => void;
|
||||
|
||||
const sharedLink = getSharedLink();
|
||||
|
||||
$: isOwner = $user && asset.ownerId === $user?.id;
|
||||
|
||||
type EventTypes = {
|
||||
back: void;
|
||||
stopMotionPhoto: void;
|
||||
playMotionPhoto: void;
|
||||
download: void;
|
||||
showDetail: void;
|
||||
favorite: void;
|
||||
delete: void;
|
||||
permanentlyDelete: void;
|
||||
toggleArchive: void;
|
||||
addToAlbum: void;
|
||||
restoreAsset: void;
|
||||
addToSharedAlbum: void;
|
||||
asProfileImage: void;
|
||||
setAsAlbumCover: void;
|
||||
runJob: AssetJobName;
|
||||
playSlideShow: void;
|
||||
unstack: void;
|
||||
showShareModal: void;
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher<EventTypes>();
|
||||
|
||||
const onJobClick = (name: AssetJobName) => {
|
||||
dispatch('runJob', name);
|
||||
};
|
||||
|
||||
const onMenuClick = (eventName: keyof EventTypes) => {
|
||||
dispatch(eventName);
|
||||
};
|
||||
$: showDownloadButton = sharedLink ? sharedLink.allowDownload : !asset.isOffline;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="z-[1001] flex h-16 place-items-center justify-between bg-gradient-to-b from-black/40 px-3 transition-transform duration-200"
|
||||
>
|
||||
<div class="text-white">
|
||||
<CircleIconButton color="opaque" icon={mdiArrowLeft} title={$t('go_back')} on:click={() => dispatch('back')} />
|
||||
<CloseAction {onClose} />
|
||||
</div>
|
||||
<div
|
||||
class="flex w-[calc(100%-3rem)] justify-end gap-2 overflow-hidden text-white"
|
||||
data-testid="asset-viewer-navbar-actions"
|
||||
>
|
||||
{#if showShareButton}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiShareVariantOutline}
|
||||
on:click={() => dispatch('showShareModal')}
|
||||
title={$t('share')}
|
||||
/>
|
||||
{#if !asset.isTrashed && $user}
|
||||
<ShareAction {asset} />
|
||||
{/if}
|
||||
{#if asset.isOffline}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiAlertOutline}
|
||||
on:click={() => dispatch('showDetail')}
|
||||
title={$t('asset_offline')}
|
||||
/>
|
||||
<CircleIconButton color="opaque" icon={mdiAlertOutline} on:click={onShowDetail} title={$t('asset_offline')} />
|
||||
{/if}
|
||||
{#if showMotionPlayButton}
|
||||
{#if isMotionPhotoPlaying}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiMotionPauseOutline}
|
||||
title={$t('stop_motion_photo')}
|
||||
on:click={() => dispatch('stopMotionPhoto')}
|
||||
/>
|
||||
{:else}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiPlaySpeed}
|
||||
title={$t('play_motion_photo')}
|
||||
on:click={() => dispatch('playMotionPhoto')}
|
||||
/>
|
||||
{/if}
|
||||
{#if asset.livePhotoVideoId}
|
||||
<slot name="motion-photo" />
|
||||
{/if}
|
||||
{#if showZoomButton}
|
||||
{#if asset.type === AssetTypeEnum.Image}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
hideMobile={true}
|
||||
|
|
@ -138,84 +83,50 @@
|
|||
on:click={onZoomImage}
|
||||
/>
|
||||
{/if}
|
||||
{#if showCopyButton}
|
||||
{#if canCopyImagesToClipboard() && asset.type === AssetTypeEnum.Image}
|
||||
<CircleIconButton color="opaque" icon={mdiContentCopy} title={$t('copy_image')} on:click={onCopyImage} />
|
||||
{/if}
|
||||
|
||||
{#if !isOwner && showDownloadButton}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiFolderDownloadOutline}
|
||||
on:click={() => dispatch('download')}
|
||||
title={$t('download')}
|
||||
/>
|
||||
<DownloadAction {asset} />
|
||||
{/if}
|
||||
|
||||
{#if showDetailButton}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={mdiInformationOutline}
|
||||
on:click={() => dispatch('showDetail')}
|
||||
title={$t('info')}
|
||||
/>
|
||||
<ShowDetailAction {onShowDetail} />
|
||||
{/if}
|
||||
|
||||
{#if isOwner}
|
||||
<CircleIconButton
|
||||
color="opaque"
|
||||
icon={asset.isFavorite ? mdiHeart : mdiHeartOutline}
|
||||
on:click={() => dispatch('favorite')}
|
||||
title={asset.isFavorite ? $t('unfavorite') : $t('to_favorite')}
|
||||
/>
|
||||
<FavoriteAction {asset} {onAction} />
|
||||
{/if}
|
||||
|
||||
{#if isOwner}
|
||||
<DeleteButton
|
||||
{asset}
|
||||
on:delete={() => dispatch('delete')}
|
||||
on:permanentlyDelete={() => dispatch('permanentlyDelete')}
|
||||
/>
|
||||
<DeleteAction {asset} {onAction} />
|
||||
|
||||
<ButtonContextMenu direction="left" align="top-right" color="opaque" title={$t('more')} icon={mdiDotsVertical}>
|
||||
{#if showSlideshow}
|
||||
<MenuOption icon={mdiPresentationPlay} onClick={() => onMenuClick('playSlideShow')} text={$t('slideshow')} />
|
||||
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
|
||||
{/if}
|
||||
{#if showDownloadButton}
|
||||
<MenuOption icon={mdiFolderDownloadOutline} onClick={() => onMenuClick('download')} text={$t('download')} />
|
||||
<DownloadAction {asset} menuItem />
|
||||
{/if}
|
||||
{#if asset.isTrashed}
|
||||
<MenuOption icon={mdiHistory} onClick={() => onMenuClick('restoreAsset')} text={$t('restore')} />
|
||||
<RestoreAction {asset} {onAction} />
|
||||
{:else}
|
||||
<MenuOption icon={mdiImageAlbum} onClick={() => onMenuClick('addToAlbum')} text={$t('add_to_album')} />
|
||||
<MenuOption
|
||||
icon={mdiShareVariantOutline}
|
||||
onClick={() => onMenuClick('addToSharedAlbum')}
|
||||
text={$t('add_to_shared_album')}
|
||||
/>
|
||||
<AddToAlbumAction {asset} {onAction} />
|
||||
<AddToAlbumAction {asset} {onAction} shared />
|
||||
{/if}
|
||||
|
||||
{#if isOwner}
|
||||
{#if hasStackChildren}
|
||||
<MenuOption icon={mdiImageMinusOutline} onClick={() => onMenuClick('unstack')} text={$t('unstack')} />
|
||||
<UnstackAction {stackedAssets} {onAction} />
|
||||
{/if}
|
||||
{#if album}
|
||||
<MenuOption
|
||||
text={$t('set_as_album_cover')}
|
||||
icon={mdiImageOutline}
|
||||
onClick={() => onMenuClick('setAsAlbumCover')}
|
||||
/>
|
||||
<SetAlbumCoverAction {asset} {album} />
|
||||
{/if}
|
||||
{#if asset.type === AssetTypeEnum.Image}
|
||||
<MenuOption
|
||||
icon={mdiAccountCircleOutline}
|
||||
onClick={() => onMenuClick('asProfileImage')}
|
||||
text={$t('set_as_profile_picture')}
|
||||
/>
|
||||
<SetProfilePictureAction {asset} />
|
||||
{/if}
|
||||
<MenuOption
|
||||
onClick={() => onMenuClick('toggleArchive')}
|
||||
icon={asset.isArchived ? mdiArchiveArrowUpOutline : mdiArchiveArrowDownOutline}
|
||||
text={asset.isArchived ? $t('unarchive') : $t('to_archive')}
|
||||
/>
|
||||
<ArchiveAction {asset} {onAction} />
|
||||
<MenuOption
|
||||
icon={mdiUpload}
|
||||
onClick={() => openFileUploadDialog({ multiple: false, assetId: asset.id })}
|
||||
|
|
@ -224,18 +135,18 @@
|
|||
<hr />
|
||||
<MenuOption
|
||||
icon={mdiDatabaseRefreshOutline}
|
||||
onClick={() => onJobClick(AssetJobName.RefreshMetadata)}
|
||||
onClick={() => onRunJob(AssetJobName.RefreshMetadata)}
|
||||
text={$getAssetJobName(AssetJobName.RefreshMetadata)}
|
||||
/>
|
||||
<MenuOption
|
||||
icon={mdiImageRefreshOutline}
|
||||
onClick={() => onJobClick(AssetJobName.RegenerateThumbnail)}
|
||||
onClick={() => onRunJob(AssetJobName.RegenerateThumbnail)}
|
||||
text={$getAssetJobName(AssetJobName.RegenerateThumbnail)}
|
||||
/>
|
||||
{#if asset.type === AssetTypeEnum.Video}
|
||||
<MenuOption
|
||||
icon={mdiCogRefreshOutline}
|
||||
onClick={() => onJobClick(AssetJobName.TranscodeVideo)}
|
||||
onClick={() => onRunJob(AssetJobName.TranscodeVideo)}
|
||||
text={$getAssetJobName(AssetJobName.TranscodeVideo)}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue