diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index b7c980f4f4..5e024b560c 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -8,7 +8,7 @@ import * as Oazapfts from "@oazapfts/runtime"; import * as QS from "@oazapfts/runtime/query"; export const defaults: Oazapfts.Defaults = { headers: {}, - baseUrl: "/api", + baseUrl: "/api" }; const oazapfts = Oazapfts.runtime(defaults); export const servers = { diff --git a/web/src/lib/components/asset-viewer/actions/close-action.svelte b/web/src/lib/components/asset-viewer/actions/close-action.svelte deleted file mode 100644 index 7b3f525a3a..0000000000 --- a/web/src/lib/components/asset-viewer/actions/close-action.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - - - diff --git a/web/src/lib/components/asset-viewer/actions/motion-photo-action.svelte b/web/src/lib/components/asset-viewer/actions/motion-photo-action.svelte deleted file mode 100644 index ee09c2976b..0000000000 --- a/web/src/lib/components/asset-viewer/actions/motion-photo-action.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - onClick(!isPlaying)} -/> diff --git a/web/src/lib/components/asset-viewer/actions/show-detail-action.svelte b/web/src/lib/components/asset-viewer/actions/show-detail-action.svelte deleted file mode 100644 index 99b6c1dcde..0000000000 --- a/web/src/lib/components/asset-viewer/actions/show-detail-action.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index 43fe6978e5..a5050075dc 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -7,7 +7,6 @@ import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte'; import AddToStackAction from '$lib/components/asset-viewer/actions/add-to-stack-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'; @@ -20,12 +19,10 @@ import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte'; import SetStackPrimaryAsset from '$lib/components/asset-viewer/actions/set-stack-primary-asset.svelte'; import SetVisibilityAction from '$lib/components/asset-viewer/actions/set-visibility-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 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 { AppRoute } from '$lib/constants'; - import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte'; import { getAssetActions, handleReplaceAsset } from '$lib/services/asset.service'; import { photoViewerImgElement } from '$lib/stores/assets-store.svelte'; @@ -44,9 +41,9 @@ type PersonResponseDto, type StackResponseDto, } from '@immich/sdk'; - import { IconButton } from '@immich/ui'; + import { CommandPaletteDefaultProvider, IconButton, type ActionItem } from '@immich/ui'; import { - mdiAlertOutline, + mdiArrowLeft, mdiCogRefreshOutline, mdiCompare, mdiContentCopy, @@ -61,7 +58,6 @@ mdiUpload, mdiVideoOutline, } from '@mdi/js'; - import type { Snippet } from 'svelte'; import { t } from 'svelte-i18n'; interface Props { @@ -79,7 +75,6 @@ onPlaySlideshow: () => void; // export let showEditorHandler: () => void; onClose?: () => void; - motionPhoto?: Snippet; playOriginalVideo: boolean; setPlayOriginalVideo: (value: boolean) => void; } @@ -98,7 +93,6 @@ onRunJob, onPlaySlideshow, onClose, - motionPhoto, playOriginalVideo = false, setPlayOriginalVideo, }: Props = $props(); @@ -109,7 +103,15 @@ let isLocked = $derived(asset.visibility === AssetVisibility.Locked); let smartSearchEnabled = $derived(featureFlagsManager.value.smartSearch); - const { Share } = $derived(getAssetActions($t, asset)); + const Close: ActionItem = { + title: $t('go_back'), + icon: mdiArrowLeft, + $if: () => !!onClose, + onAction: () => onClose?.(), + shortcuts: [{ key: 'Escape' }], + }; + + const { Share, Offline, PlayMotionPhoto, StopMotionPhoto, Info } = $derived(getAssetActions($t, asset)); // $: showEditorButton = // isOwner && @@ -122,30 +124,26 @@ // !asset.livePhotoVideoId; + +
- {#if onClose} - - {/if} +
+
- {#if asset.isOffline} - assetViewerManager.toggleDetailPanel()} - aria-label={$t('asset_offline')} - /> - {/if} - {#if asset.livePhotoVideoId} - {@render motionPhoto?.()} - {/if} + + + + {#if asset.type === AssetTypeEnum.Image}
{/if} @@ -483,7 +474,7 @@ {:else} {#key asset.id} {#if asset.type === AssetTypeEnum.Image} - {#if shouldPlayMotionPhoto && asset.livePhotoVideoId} + {#if assetViewerManager.isPlayingMotionPhoto && asset.livePhotoVideoId} navigateAsset('previous')} onNextAsset={() => navigateAsset('next')} - onVideoEnded={() => (shouldPlayMotionPhoto = false)} + onVideoEnded={() => (assetViewerManager.isPlayingMotionPhoto = false)} {playOriginalVideo} /> {:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR || (asset.originalPath && asset.originalPath diff --git a/web/src/lib/managers/asset-viewer-manager.svelte.ts b/web/src/lib/managers/asset-viewer-manager.svelte.ts index 56470eac35..7b482faa76 100644 --- a/web/src/lib/managers/asset-viewer-manager.svelte.ts +++ b/web/src/lib/managers/asset-viewer-manager.svelte.ts @@ -3,15 +3,8 @@ import { PersistedLocalStorage } from '$lib/utils/persisted'; const isShowDetailPanel = new PersistedLocalStorage('asset-viewer-state', false); export class AssetViewerManager { - #isShowActivityPanel = $state(false); - - get isShowActivityPanel() { - return this.#isShowActivityPanel; - } - - private set isShowActivityPanel(value: boolean) { - this.#isShowActivityPanel = value; - } + isShowActivityPanel = $state(false); + isPlayingMotionPhoto = $state(false); get isShowDetailPanel() { return isShowDetailPanel.current; diff --git a/web/src/lib/services/asset.service.ts b/web/src/lib/services/asset.service.ts index a64da2a6d6..de5223db23 100644 --- a/web/src/lib/services/asset.service.ts +++ b/web/src/lib/services/asset.service.ts @@ -1,10 +1,17 @@ +import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; import { eventManager } from '$lib/managers/event-manager.svelte'; import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte'; import { user as authUser } from '$lib/stores/user.store'; import { openFileUploadDialog } from '$lib/utils/file-uploader'; import { AssetVisibility, copyAsset, deleteAssets, type AssetResponseDto } from '@immich/sdk'; import { modalManager, type ActionItem } from '@immich/ui'; -import { mdiShareVariantOutline } from '@mdi/js'; +import { + mdiAlertOutline, + mdiInformationOutline, + mdiMotionPauseOutline, + mdiMotionPlayOutline, + mdiShareVariantOutline, +} from '@mdi/js'; import type { MessageFormatter } from 'svelte-i18n'; import { get } from 'svelte/store'; @@ -16,7 +23,41 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) = onAction: () => modalManager.show(SharedLinkCreateModal, { assetIds: [asset.id] }), }; - return { Share }; + const PlayMotionPhoto: ActionItem = { + title: $t('play_motion_photo'), + icon: mdiMotionPlayOutline, + $if: () => !!asset.livePhotoVideoId && !assetViewerManager.isPlayingMotionPhoto, + onAction: () => { + assetViewerManager.isPlayingMotionPhoto = true; + }, + }; + + const StopMotionPhoto: ActionItem = { + title: $t('stop_motion_photo'), + icon: mdiMotionPauseOutline, + $if: () => !!asset.livePhotoVideoId && assetViewerManager.isPlayingMotionPhoto, + onAction: () => { + assetViewerManager.isPlayingMotionPhoto = false; + }, + }; + + const Offline: ActionItem = { + title: $t('asset_offline'), + icon: mdiAlertOutline, + color: 'danger', + $if: () => !!asset.isOffline, + onAction: () => assetViewerManager.toggleDetailPanel(), + }; + + const Info: ActionItem = { + title: $t('info'), + icon: mdiInformationOutline, + $if: () => asset.hasMetadata, + onAction: () => assetViewerManager.toggleDetailPanel(), + shortcuts: [{ key: 'i' }], + }; + + return { Share, PlayMotionPhoto, StopMotionPhoto, Offline, Info }; }; export const handleReplaceAsset = async (oldAssetId: string) => {