diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte
index deeb89c5c3..62216a750c 100644
--- a/web/src/lib/components/album-page/album-viewer.svelte
+++ b/web/src/lib/components/album-page/album-viewer.svelte
@@ -2,6 +2,7 @@
import { shortcut } from '$lib/actions/shortcut';
import AlbumMap from '$lib/components/album-page/album-map.svelte';
import SelectAllAssets from '$lib/components/photos-page/actions/select-all-assets.svelte';
+ import AssetSelectControlBar from '$lib/components/photos-page/asset-select-control-bar.svelte';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { AssetStore } from '$lib/stores/assets-store.svelte';
@@ -16,7 +17,6 @@
import CircleIconButton from '../elements/buttons/circle-icon-button.svelte';
import DownloadAction from '../photos-page/actions/download-action.svelte';
import AssetGrid from '../photos-page/asset-grid.svelte';
- import AssetSelectControlBar from '../photos-page/asset-select-control-bar.svelte';
import ControlAppBar from '../shared-components/control-app-bar.svelte';
import ImmichLogoSmallLink from '../shared-components/immich-logo-small-link.svelte';
import ThemeButton from '../shared-components/theme-button.svelte';
diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts
index d85325b59a..0918c86bfe 100644
--- a/web/src/lib/components/asset-viewer/actions/action.ts
+++ b/web/src/lib/components/asset-viewer/actions/action.ts
@@ -1,20 +1,21 @@
import type { AssetAction } from '$lib/constants';
-import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
+import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
+import type { AlbumResponseDto } from '@immich/sdk';
type ActionMap = {
- [AssetAction.ARCHIVE]: { asset: AssetResponseDto };
- [AssetAction.UNARCHIVE]: { asset: AssetResponseDto };
- [AssetAction.FAVORITE]: { asset: AssetResponseDto };
- [AssetAction.UNFAVORITE]: { asset: AssetResponseDto };
- [AssetAction.TRASH]: { asset: AssetResponseDto };
- [AssetAction.DELETE]: { asset: AssetResponseDto };
- [AssetAction.RESTORE]: { asset: AssetResponseDto };
- [AssetAction.ADD]: { asset: AssetResponseDto };
- [AssetAction.ADD_TO_ALBUM]: { asset: AssetResponseDto; album: AlbumResponseDto };
- [AssetAction.UNSTACK]: { assets: AssetResponseDto[] };
- [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: AssetResponseDto };
- [AssetAction.SET_VISIBILITY_LOCKED]: { asset: AssetResponseDto };
- [AssetAction.SET_VISIBILITY_TIMELINE]: { asset: AssetResponseDto };
+ [AssetAction.ARCHIVE]: { asset: TimelineAsset };
+ [AssetAction.UNARCHIVE]: { asset: TimelineAsset };
+ [AssetAction.FAVORITE]: { asset: TimelineAsset };
+ [AssetAction.UNFAVORITE]: { asset: TimelineAsset };
+ [AssetAction.TRASH]: { asset: TimelineAsset };
+ [AssetAction.DELETE]: { asset: TimelineAsset };
+ [AssetAction.RESTORE]: { asset: TimelineAsset };
+ [AssetAction.ADD]: { asset: TimelineAsset };
+ [AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto };
+ [AssetAction.UNSTACK]: { assets: TimelineAsset[] };
+ [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset };
+ [AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset };
+ [AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset };
};
export type Action = {
diff --git a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte
index 202f0e4593..4ebe9d002a 100644
--- a/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/add-to-album-action.svelte
@@ -6,6 +6,7 @@
import Portal from '$lib/components/shared-components/portal/portal.svelte';
import { AssetAction } from '$lib/constants';
import { addAssetsToAlbum, addAssetsToNewAlbum } from '$lib/utils/asset-utils';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import type { AlbumResponseDto, AssetResponseDto } from '@immich/sdk';
import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -24,14 +25,14 @@
showSelectionModal = false;
const album = await addAssetsToNewAlbum(albumName, [asset.id]);
if (album) {
- onAction({ type: AssetAction.ADD_TO_ALBUM, asset, album });
+ onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album });
}
};
const handleAddToAlbum = async (album: AlbumResponseDto) => {
showSelectionModal = false;
await addAssetsToAlbum(album.id, [asset.id]);
- onAction({ type: AssetAction.ADD_TO_ALBUM, asset, album });
+ onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album });
};
diff --git a/web/src/lib/components/asset-viewer/actions/archive-action.svelte b/web/src/lib/components/asset-viewer/actions/archive-action.svelte
index ed19dff864..362a0a693a 100644
--- a/web/src/lib/components/asset-viewer/actions/archive-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/archive-action.svelte
@@ -4,6 +4,7 @@
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { AssetAction } from '$lib/constants';
import { toggleArchive } from '$lib/utils/asset-utils';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto } from '@immich/sdk';
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -18,11 +19,11 @@
const onArchive = async () => {
if (!asset.isArchived) {
- preAction({ type: AssetAction.ARCHIVE, asset });
+ preAction({ type: AssetAction.ARCHIVE, asset: toTimelineAsset(asset) });
}
const updatedAsset = await toggleArchive(asset);
if (updatedAsset) {
- onAction({ type: asset.isArchived ? AssetAction.ARCHIVE : AssetAction.UNARCHIVE, asset });
+ onAction({ type: asset.isArchived ? AssetAction.ARCHIVE : AssetAction.UNARCHIVE, asset: toTimelineAsset(asset) });
}
};
diff --git a/web/src/lib/components/asset-viewer/actions/delete-action.svelte b/web/src/lib/components/asset-viewer/actions/delete-action.svelte
index 24ba2c845d..90322c00f0 100644
--- a/web/src/lib/components/asset-viewer/actions/delete-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/delete-action.svelte
@@ -11,6 +11,7 @@
import { showDeleteModal } from '$lib/stores/preferences.store';
import { featureFlags } from '$lib/stores/server-config.store';
import { handleError } from '$lib/utils/handle-error';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import { deleteAssets, type AssetResponseDto } from '@immich/sdk';
import { mdiDeleteForeverOutline, mdiDeleteOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -42,9 +43,9 @@
const trashAsset = async () => {
try {
- preAction({ type: AssetAction.TRASH, asset });
+ preAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
- onAction({ type: AssetAction.TRASH, asset });
+ onAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
notificationController.show({
message: $t('moved_to_trash'),
@@ -58,7 +59,7 @@
const deleteAsset = async () => {
try {
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
- onAction({ type: AssetAction.DELETE, asset });
+ onAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
notificationController.show({
message: $t('permanently_deleted_asset'),
diff --git a/web/src/lib/components/asset-viewer/actions/download-action.svelte b/web/src/lib/components/asset-viewer/actions/download-action.svelte
index d7f4f56352..c32766a725 100644
--- a/web/src/lib/components/asset-viewer/actions/download-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/download-action.svelte
@@ -2,19 +2,21 @@
import { shortcut } from '$lib/actions/shortcut';
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
+ import { authManager } from '$lib/managers/auth-manager.svelte';
+ import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
import { downloadFile } from '$lib/utils/asset-utils';
- import type { AssetResponseDto } from '@immich/sdk';
+ import { getAssetInfo } from '@immich/sdk';
import { mdiFolderDownloadOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
- asset: AssetResponseDto;
+ asset: TimelineAsset;
menuItem?: boolean;
}
let { asset, menuItem = false }: Props = $props();
- const onDownloadFile = () => downloadFile(asset);
+ const onDownloadFile = async () => downloadFile(await getAssetInfo({ id: asset.id, key: authManager.key }));
diff --git a/web/src/lib/components/asset-viewer/actions/favorite-action.svelte b/web/src/lib/components/asset-viewer/actions/favorite-action.svelte
index 0cc3188d51..bb1a9343d9 100644
--- a/web/src/lib/components/asset-viewer/actions/favorite-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/favorite-action.svelte
@@ -7,6 +7,7 @@
} from '$lib/components/shared-components/notification/notification';
import { AssetAction } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
import { mdiHeart, mdiHeartOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -30,7 +31,10 @@
asset = { ...asset, isFavorite: data.isFavorite };
- onAction({ type: asset.isFavorite ? AssetAction.FAVORITE : AssetAction.UNFAVORITE, asset });
+ onAction({
+ type: asset.isFavorite ? AssetAction.FAVORITE : AssetAction.UNFAVORITE,
+ asset: toTimelineAsset(asset),
+ });
notificationController.show({
type: NotificationType.Info,
diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
index 090e87f4a9..80dfb35067 100644
--- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
+++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte
@@ -3,6 +3,7 @@
import { AssetAction } from '$lib/constants';
import { modalManager } from '$lib/managers/modal-manager.svelte';
import { keepThisDeleteOthers } from '$lib/utils/asset-utils';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto, StackResponseDto } from '@immich/sdk';
import { mdiPinOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -29,7 +30,7 @@
const keptAsset = await keepThisDeleteOthers(asset, stack);
if (keptAsset) {
- onAction({ type: AssetAction.UNSTACK, assets: [keptAsset] });
+ onAction({ type: AssetAction.UNSTACK, assets: [toTimelineAsset(keptAsset)] });
}
};
diff --git a/web/src/lib/components/asset-viewer/actions/restore-action.svelte b/web/src/lib/components/asset-viewer/actions/restore-action.svelte
index abcae5c4c9..c790dab853 100644
--- a/web/src/lib/components/asset-viewer/actions/restore-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/restore-action.svelte
@@ -6,6 +6,7 @@
} from '$lib/components/shared-components/notification/notification';
import { AssetAction } from '$lib/constants';
import { handleError } from '$lib/utils/handle-error';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import { restoreAssets, type AssetResponseDto } from '@immich/sdk';
import { mdiHistory } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -23,7 +24,7 @@
await restoreAssets({ bulkIdsDto: { ids: [asset.id] } });
asset.isTrashed = false;
- onAction({ type: AssetAction.RESTORE, asset });
+ onAction({ type: AssetAction.RESTORE, asset: toTimelineAsset(asset) });
notificationController.show({
type: NotificationType.Info,
diff --git a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
index 6a7f6d3078..d133010af7 100644
--- a/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/set-visibility-action.svelte
@@ -3,14 +3,15 @@
import { AssetAction } from '$lib/constants';
import { modalManager } from '$lib/managers/modal-manager.svelte';
+ import type { TimelineAsset } from '$lib/stores/assets-store.svelte';
import { handleError } from '$lib/utils/handle-error';
- import { AssetVisibility, updateAssets, Visibility, type AssetResponseDto } from '@immich/sdk';
+ import { AssetVisibility, updateAssets, Visibility } from '@immich/sdk';
import { mdiEyeOffOutline, mdiFolderMoveOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
import type { OnAction, PreAction } from './action';
interface Props {
- asset: AssetResponseDto;
+ asset: TimelineAsset;
onAction: OnAction;
preAction: PreAction;
}
diff --git a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte
index f2a50cce13..1adeead05f 100644
--- a/web/src/lib/components/asset-viewer/actions/unstack-action.svelte
+++ b/web/src/lib/components/asset-viewer/actions/unstack-action.svelte
@@ -2,6 +2,7 @@
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { AssetAction } from '$lib/constants';
import { deleteStack } from '$lib/utils/asset-utils';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import type { StackResponseDto } from '@immich/sdk';
import { mdiImageMinusOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@@ -17,7 +18,7 @@
const handleUnstack = async () => {
const unstackedAssets = await deleteStack([stack.id]);
if (unstackedAssets) {
- onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets });
+ onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets.map((asset) => toTimelineAsset(asset)) });
}
};
diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
index a25ea6bf90..f77fbc7f20 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
+++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.spec.ts
@@ -13,6 +13,7 @@ describe('AssetViewerNavBar component', () => {
showDownloadButton: false,
showMotionPlayButton: false,
showShareButton: false,
+ preAction: () => {},
onZoomImage: () => {},
onCopyImage: () => {},
onAction: () => {},
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 9436dc13c8..9a52067feb 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
@@ -25,6 +25,7 @@
import { getAssetJobName, getSharedLink } from '$lib/utils';
import { canCopyImageToClipboard } from '$lib/utils/asset-utils';
import { openFileUploadDialog } from '$lib/utils/file-uploader';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import {
AssetJobName,
AssetTypeEnum,
@@ -138,7 +139,7 @@
{/if}
{#if !isOwner && showDownloadButton}
-
+
{/if}
{#if showDetailButton}
@@ -166,7 +167,7 @@
{/if}
{#if showDownloadButton}
-
+
{/if}
{#if !isLocked}
@@ -210,7 +211,7 @@
{/if}
{#if !asset.isTrashed}
-
+
{/if}
void;
+ onClose: (asset: AssetResponseDto) => void;
onNext: () => Promise;
onPrevious: () => Promise;
- onRandom: () => Promise;
+ onRandom: () => Promise<{ id: string } | undefined>;
copyImage?: () => Promise;
}
@@ -81,7 +83,7 @@
copyImage = $bindable(),
}: Props = $props();
- const { setAsset } = assetViewingStore;
+ const { setAssetId } = assetViewingStore;
const {
restartProgress: restartSlideshowProgress,
stopProgress: stopSlideshowProgress,
@@ -121,7 +123,7 @@
untrack(() => {
if (stack && stack?.assets.length > 1) {
- preloadAssets.push(stack.assets[1]);
+ preloadAssets.push(toTimelineAsset(stack.assets[1]));
}
});
};
@@ -161,7 +163,7 @@
slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
if (value === SlideshowState.PlaySlideshow) {
slideshowHistory.reset();
- slideshowHistory.queue(asset);
+ slideshowHistory.queue(toTimelineAsset(asset));
handlePromiseError(handlePlaySlideshow());
} else if (value === SlideshowState.StopSlideshow) {
handlePromiseError(handleStopSlideshow());
@@ -171,7 +173,7 @@
shuffleSlideshowUnsubscribe = slideshowNavigation.subscribe((value) => {
if (value === SlideshowNavigation.Shuffle) {
slideshowHistory.reset();
- slideshowHistory.queue(asset);
+ slideshowHistory.queue(toTimelineAsset(asset));
}
});
@@ -225,7 +227,7 @@
};
const closeViewer = () => {
- onClose({ asset });
+ onClose(asset);
};
const closeEditor = () => {
@@ -292,8 +294,7 @@
let assetViewerHtmlElement = $state();
const slideshowHistory = new SlideshowHistory((asset) => {
- setAsset(asset);
- $restartSlideshowProgress = true;
+ handlePromiseError(setAssetId(asset.id).then(() => ($restartSlideshowProgress = true)));
});
const handleVideoStarted = () => {
@@ -563,8 +564,8 @@
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
brokenAssetClass="text-xs"
dimmed={stackedAsset.id !== asset.id}
- asset={stackedAsset}
- onClick={(stackedAsset) => {
+ asset={toTimelineAsset(stackedAsset)}
+ onClick={() => {
asset = stackedAsset;
}}
onMouseEvent={({ isMouseOver }) => handleStackedAssetMouseEvent(isMouseOver, stackedAsset)}
diff --git a/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte b/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte
index 9c4b0bcaa4..a264ad8ddd 100644
--- a/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte
+++ b/web/src/lib/components/asset-viewer/editor/crop-tool/crop-area.svelte
@@ -12,6 +12,7 @@
resetGlobalCropStore,
rotateDegrees,
} from '$lib/stores/asset-editor.store';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto } from '@immich/sdk';
import { animateCropChange, recalculateCrop } from './crop-settings';
import { cropAreaEl, cropFrame, imgElement, isResizingOrDragging, overlayEl, resetCropStore } from './crop-store';
@@ -81,7 +82,7 @@
aria-label="Crop area"
type="button"
>
-
+
diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte
index 6711d126ca..564cef5308 100644
--- a/web/src/lib/components/asset-viewer/photo-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte
@@ -3,7 +3,7 @@
import { zoomImageAction, zoomed } from '$lib/actions/zoom-image';
import FaceEditor from '$lib/components/asset-viewer/face-editor/face-editor.svelte';
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
- import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
+ import { photoViewerImgElement, type TimelineAsset } from '$lib/stores/assets-store.svelte';
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
import { boundingBoxesArray } from '$lib/stores/people.store';
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
@@ -13,9 +13,10 @@
import { canCopyImageToClipboard, copyImageToClipboard, isWebCompatibleImage } from '$lib/utils/asset-utils';
import { handleError } from '$lib/utils/handle-error';
import { getBoundingBox } from '$lib/utils/people-utils';
- import { cancelImageUrl, preloadImageUrl } from '$lib/utils/sw-messaging';
+ import { cancelImageUrl } from '$lib/utils/sw-messaging';
import { getAltText } from '$lib/utils/thumbnail-util';
- import { AssetMediaSize, AssetTypeEnum, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
+ import { toTimelineAsset } from '$lib/utils/timeline-util';
+ import { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
import { onDestroy, onMount } from 'svelte';
import { swipe, type SwipeCustomEvent } from 'svelte-gestures';
import { t } from 'svelte-i18n';
@@ -25,7 +26,7 @@
interface Props {
asset: AssetResponseDto;
- preloadAssets?: AssetResponseDto[] | undefined;
+ preloadAssets?: TimelineAsset[] | undefined;
element?: HTMLDivElement | undefined;
haveFadeTransition?: boolean;
sharedLink?: SharedLinkResponseDto | undefined;
@@ -69,10 +70,11 @@
$boundingBoxesArray = [];
});
- const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: AssetResponseDto[]) => {
+ const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: TimelineAsset[]) => {
for (const preloadAsset of preloadAssets || []) {
- if (preloadAsset.type === AssetTypeEnum.Image) {
- preloadImageUrl(getAssetUrl(preloadAsset.id, targetSize, preloadAsset.thumbhash));
+ if (preloadAsset.isImage) {
+ let img = new Image();
+ img.src = getAssetUrl(preloadAsset.id, targetSize, preloadAsset.thumbhash);
}
}
};
@@ -197,7 +199,7 @@
bind:clientWidth={containerWidth}
bind:clientHeight={containerHeight}
>
-

+

{#if !imageLoaded}
@@ -213,7 +215,7 @@
{#if $slideshowState !== SlideshowState.None && $slideshowLook === SlideshowLook.BlurredBackground}

@@ -221,7 +223,7 @@

void) | undefined;
- onSelect?: ((asset: AssetResponseDto) => void) | undefined;
- onMouseEvent?: ((event: { isMouseOver: boolean; selectedGroupIndex: number }) => void) | undefined;
- handleFocus?: (() => void) | undefined;
+ onClick?: (asset: TimelineAsset) => void;
+ onSelect?: (asset: TimelineAsset) => void;
+ onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
+ handleFocus?: () => void;
}
let {
@@ -290,13 +291,13 @@
{/if}
- {#if !authManager.key && showArchiveIcon && asset.isArchived}
+ {#if !authManager.key && showArchiveIcon && asset.visibility === Visibility.Archive}
{/if}
- {#if asset.type === AssetTypeEnum.Image && asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR}
+ {#if asset.isImage && asset.projectionType === ProjectionType.EQUIRECTANGULAR}
@@ -309,7 +310,7 @@
@@ -329,17 +330,17 @@
curve={selected}
onComplete={(errored) => ((loaded = true), (thumbError = errored))}
/>
- {#if asset.type === AssetTypeEnum.Video}
+ {#if asset.isVideo}
- {:else if asset.type === AssetTypeEnum.Image && asset.livePhotoVideoId}
+ {:else if asset.isImage && asset.livePhotoVideoId}
(undefined);
+ let currentMemoryAssetFull = $derived.by(async () =>
+ current?.asset ? await getAssetInfo({ id: current.asset.id, key: authManager.key }) : undefined,
+ );
+ let currentTimelineAssets = $derived(current?.memory.assets.map((asset) => toTimelineAsset(asset)) || []);
+
let isSaved = $derived(current?.memory.isSaved);
let viewerHeight = $state(0);
@@ -77,8 +84,8 @@
const assetInteraction = new AssetInteraction();
let progressBarController: Tween | undefined = $state(undefined);
let videoPlayer: HTMLVideoElement | undefined = $state();
- const asHref = (asset: AssetResponseDto) => `?${QueryParameter.ID}=${asset.id}`;
- const handleNavigate = async (asset?: AssetResponseDto) => {
+ const asHref = (asset: { id: string }) => `?${QueryParameter.ID}=${asset.id}`;
+ const handleNavigate = async (asset?: { id: string }) => {
if ($isViewing) {
return asset;
}
@@ -89,9 +96,9 @@
await goto(asHref(asset));
};
- const setProgressDuration = (asset: AssetResponseDto) => {
- if (asset.type === AssetTypeEnum.Video) {
- const timeParts = asset.duration.split(':').map(Number);
+ const setProgressDuration = (asset: TimelineAsset) => {
+ if (asset.isVideo) {
+ const timeParts = asset.duration!.split(':').map(Number);
const durationInMilliseconds = (timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]) * 1000;
progressBarController = new Tween(0, {
duration: (from: number, to: number) => (to ? durationInMilliseconds * (to - from) : 0),
@@ -107,7 +114,8 @@
const handleNextMemory = () => handleNavigate(current?.nextMemory?.assets[0]);
const handlePreviousMemory = () => handleNavigate(current?.previousMemory?.assets[0]);
const handleEscape = async () => goto(AppRoute.PHOTOS);
- const handleSelectAll = () => assetInteraction.selectAssets(current?.memory.assets || []);
+ const handleSelectAll = () =>
+ assetInteraction.selectAssets(current?.memory.assets.map((a) => toTimelineAsset(a)) || []);
const handleAction = async (callingContext: string, action: 'reset' | 'pause' | 'play') => {
// leaving these log statements here as comments. Very useful to figure out what's going on during dev!
// console.log(`handleAction[${callingContext}] called with: ${action}`);
@@ -240,7 +248,7 @@
};
const initPlayer = () => {
- const isVideoAssetButPlayerHasNotLoadedYet = current && current.asset.type === AssetTypeEnum.Video && !videoPlayer;
+ const isVideoAssetButPlayerHasNotLoadedYet = current && current.asset.isVideo && !videoPlayer;
if (playerInitialized || isVideoAssetButPlayerHasNotLoadedYet) {
return;
}
@@ -441,7 +449,7 @@
{#key current.asset.id}
- {#if current.asset.type === AssetTypeEnum.Video}
+ {#if current.asset.isVideo}
@@ -551,8 +559,10 @@
})}
- {current.asset.exifInfo?.city || ''}
- {current.asset.exifInfo?.country || ''}
+ {#await currentMemoryAssetFull then asset}
+ {asset?.exifInfo?.city || ''}
+ {asset?.exifInfo?.country || ''}
+ {/await}
@@ -623,7 +633,7 @@
import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
import type { OnArchive } from '$lib/utils/actions';
+ import { archiveAssets } from '$lib/utils/asset-utils';
+ import { AssetVisibility, Visibility } from '@immich/sdk';
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline, mdiTimerSand } from '@mdi/js';
+ import { t } from 'svelte-i18n';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
- import { archiveAssets } from '$lib/utils/asset-utils';
- import { t } from 'svelte-i18n';
interface Props {
onArchive?: OnArchive;
@@ -23,10 +24,10 @@
const { clearSelect, getOwnedAssets } = getAssetControlContext();
const handleArchive = async () => {
- const isArchived = !unarchive;
- const assets = [...getOwnedAssets()].filter((asset) => asset.isArchived !== isArchived);
+ const isArchived = unarchive ? Visibility.Timeline : Visibility.Archive;
+ const assets = [...getOwnedAssets()].filter((asset) => asset.visibility !== isArchived);
loading = true;
- const ids = await archiveAssets(assets, isArchived);
+ const ids = await archiveAssets(assets, isArchived as unknown as AssetVisibility);
if (ids) {
onArchive?.(ids, isArchived);
clearSelect();
diff --git a/web/src/lib/components/photos-page/actions/asset-job-actions.svelte b/web/src/lib/components/photos-page/actions/asset-job-actions.svelte
index 89c0b42165..5676ad5fbf 100644
--- a/web/src/lib/components/photos-page/actions/asset-job-actions.svelte
+++ b/web/src/lib/components/photos-page/actions/asset-job-actions.svelte
@@ -6,9 +6,9 @@
} from '$lib/components/shared-components/notification/notification';
import { getAssetJobIcon, getAssetJobMessage, getAssetJobName } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
- import { AssetJobName, AssetTypeEnum, runAssetJobs } from '@immich/sdk';
- import { getAssetControlContext } from '../asset-select-control-bar.svelte';
+ import { AssetJobName, runAssetJobs } from '@immich/sdk';
import { t } from 'svelte-i18n';
+ import { getAssetControlContext } from '../asset-select-control-bar.svelte';
interface Props {
jobs?: AssetJobName[];
@@ -19,7 +19,7 @@
const { clearSelect, getOwnedAssets } = getAssetControlContext();
- let isAllVideos = $derived([...getOwnedAssets()].every((asset) => asset.type === AssetTypeEnum.Video));
+ const isAllVideos = $derived([...getOwnedAssets()].every((asset) => asset.isVideo));
const handleRunJob = async (name: AssetJobName) => {
try {
diff --git a/web/src/lib/components/photos-page/actions/change-date-action.svelte b/web/src/lib/components/photos-page/actions/change-date-action.svelte
index 3232cbd2b4..5f65fdd744 100644
--- a/web/src/lib/components/photos-page/actions/change-date-action.svelte
+++ b/web/src/lib/components/photos-page/actions/change-date-action.svelte
@@ -4,11 +4,11 @@
import { getSelectedAssets } from '$lib/utils/asset-utils';
import { handleError } from '$lib/utils/handle-error';
import { updateAssets } from '@immich/sdk';
+ import { mdiCalendarEditOutline } from '@mdi/js';
import { DateTime } from 'luxon';
+ import { t } from 'svelte-i18n';
import MenuOption from '../../shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '../asset-select-control-bar.svelte';
- import { mdiCalendarEditOutline } from '@mdi/js';
- import { t } from 'svelte-i18n';
interface Props {
menuItem?: boolean;
}
diff --git a/web/src/lib/components/photos-page/actions/download-action.svelte b/web/src/lib/components/photos-page/actions/download-action.svelte
index 1651936c08..df079e45b2 100644
--- a/web/src/lib/components/photos-page/actions/download-action.svelte
+++ b/web/src/lib/components/photos-page/actions/download-action.svelte
@@ -1,11 +1,14 @@
@@ -94,14 +97,14 @@
clearSelect={() => cancelMultiselect(assetInteraction)}
>
-
+
cancelMultiselect(assetInteraction)} />
cancelMultiselect(assetInteraction)} shared />
{
+ onFavorite={function handleFavoriteUpdate(ids, isFavorite) {
if (data.pathAssets && data.pathAssets.length > 0) {
for (const id of ids) {
const asset = data.pathAssets.find((asset) => asset.id === id);
@@ -141,17 +144,17 @@
icons={{ default: mdiFolderOutline, active: mdiFolder }}
items={tree}
active={currentPath}
- {getLink}
+ getLink={getLinkForPath}
/>
{/snippet}
-
+