This commit is contained in:
Min Idzelis 2025-10-10 11:33:51 -05:00 committed by GitHub
commit 57e7bf2264
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 259 additions and 280 deletions

View file

@ -1,24 +1,24 @@
import type { AssetAction } from '$lib/constants';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import type { AlbumResponseDto, AssetResponseDto, PersonResponseDto, StackResponseDto } from '@immich/sdk';
type ActionMap = {
[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.ARCHIVE]: { asset: Asset };
[AssetAction.UNARCHIVE]: { asset: Asset };
[AssetAction.FAVORITE]: { asset: Asset };
[AssetAction.UNFAVORITE]: { asset: Asset };
[AssetAction.TRASH]: { asset: Asset };
[AssetAction.DELETE]: { asset: Asset };
[AssetAction.RESTORE]: { asset: Asset };
[AssetAction.ADD]: { asset: Asset };
[AssetAction.ADD_TO_ALBUM]: { asset: Asset; album: AlbumResponseDto };
[AssetAction.STACK]: { stack: StackResponseDto };
[AssetAction.UNSTACK]: { assets: TimelineAsset[] };
[AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset };
[AssetAction.UNSTACK]: { assets: Asset[] };
[AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: Asset };
[AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto };
[AssetAction.REMOVE_ASSET_FROM_STACK]: { stack: StackResponseDto | null; asset: AssetResponseDto };
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset };
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset };
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: Asset };
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: Asset };
[AssetAction.SET_PERSON_FEATURED_PHOTO]: { asset: AssetResponseDto; person: PersonResponseDto };
};

View file

@ -5,7 +5,7 @@
import { AssetAction } from '$lib/constants';
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto } from '@immich/sdk';
import { modalManager } from '@immich/ui';
import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js';
@ -29,13 +29,13 @@
if (albums.length === 1) {
const album = albums[0];
await addAssetsToAlbum(album.id, [asset.id]);
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album });
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toAsset(asset), album });
} else {
await addAssetsToAlbums(
albums.map((a) => a.id),
[asset.id],
);
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toTimelineAsset(asset), album: albums[0] });
onAction({ type: AssetAction.ADD_TO_ALBUM, asset: toAsset(asset), album: albums[0] });
}
};
</script>

View file

@ -4,7 +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 { toAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto } from '@immich/sdk';
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -19,11 +19,11 @@
const onArchive = async () => {
if (!asset.isArchived) {
preAction({ type: AssetAction.ARCHIVE, asset: toTimelineAsset(asset) });
preAction({ type: AssetAction.ARCHIVE, asset: toAsset(asset) });
}
const updatedAsset = await toggleArchive(asset);
if (updatedAsset) {
onAction({ type: asset.isArchived ? AssetAction.ARCHIVE : AssetAction.UNARCHIVE, asset: toTimelineAsset(asset) });
onAction({ type: asset.isArchived ? AssetAction.ARCHIVE : AssetAction.UNARCHIVE, asset: toAsset(asset) });
}
};
</script>

View file

@ -10,7 +10,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 { toAsset } from '$lib/utils/timeline-util';
import { deleteAssets, type AssetResponseDto } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import { mdiDeleteForeverOutline, mdiDeleteOutline } from '@mdi/js';
@ -43,9 +43,9 @@
const trashAsset = async () => {
try {
preAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
preAction({ type: AssetAction.TRASH, asset: toAsset(asset) });
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
onAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
onAction({ type: AssetAction.TRASH, asset: toAsset(asset) });
notificationController.show({
message: $t('moved_to_trash'),
@ -58,9 +58,9 @@
const deleteAsset = async () => {
try {
preAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
preAction({ type: AssetAction.DELETE, asset: toAsset(asset) });
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
onAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
onAction({ type: AssetAction.DELETE, asset: toAsset(asset) });
notificationController.show({
message: $t('permanently_deleted_asset'),

View file

@ -2,7 +2,7 @@
import { shortcut } from '$lib/actions/shortcut';
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/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { downloadFile } from '$lib/utils/asset-utils';
import { getAssetInfo } from '@immich/sdk';
import { IconButton } from '@immich/ui';
@ -10,7 +10,7 @@
import { t } from 'svelte-i18n';
interface Props {
asset: TimelineAsset;
asset: Asset;
menuItem?: boolean;
}

View file

@ -6,12 +6,12 @@
} 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 { toAsset } from '$lib/utils/timeline-util';
import { updateAsset, type AssetResponseDto } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import { mdiHeart, mdiHeartOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
import type { OnAction } from './action';
import { IconButton } from '@immich/ui';
interface Props {
asset: AssetResponseDto;
@ -33,7 +33,7 @@
onAction({
type: asset.isFavorite ? AssetAction.FAVORITE : AssetAction.UNFAVORITE,
asset: toTimelineAsset(asset),
asset: toAsset(asset),
});
notificationController.show({

View file

@ -2,7 +2,7 @@
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { AssetAction } from '$lib/constants';
import { keepThisDeleteOthers } from '$lib/utils/asset-utils';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto, StackResponseDto } from '@immich/sdk';
import { modalManager } from '@immich/ui';
import { mdiPinOutline } from '@mdi/js';
@ -30,7 +30,7 @@
const keptAsset = await keepThisDeleteOthers(asset, stack);
if (keptAsset) {
onAction({ type: AssetAction.UNSTACK, assets: [toTimelineAsset(keptAsset)] });
onAction({ type: AssetAction.UNSTACK, assets: [toAsset(keptAsset)] });
}
};
</script>

View file

@ -6,7 +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 { toAsset } from '$lib/utils/timeline-util';
import { restoreAssets, type AssetResponseDto } from '@immich/sdk';
import { mdiHistory } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -24,7 +24,7 @@
await restoreAssets({ bulkIdsDto: { ids: [asset.id] } });
asset.isTrashed = false;
onAction({ type: AssetAction.RESTORE, asset: toTimelineAsset(asset) });
onAction({ type: AssetAction.RESTORE, asset: toAsset(asset) });
notificationController.show({
type: NotificationType.Info,

View file

@ -1,7 +1,7 @@
<script lang="ts">
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { AssetAction } from '$lib/constants';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { handleError } from '$lib/utils/handle-error';
import { AssetVisibility, updateAssets } from '@immich/sdk';
import { modalManager } from '@immich/ui';
@ -10,7 +10,7 @@
import type { OnAction, PreAction } from './action';
interface Props {
asset: TimelineAsset;
asset: Asset;
onAction: OnAction;
preAction: PreAction;
}

View file

@ -2,7 +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 { toAsset } from '$lib/utils/timeline-util';
import type { StackResponseDto } from '@immich/sdk';
import { mdiImageOffOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -18,7 +18,7 @@
const handleUnstack = async () => {
const unstackedAssets = await deleteStack([stack.id]);
if (unstackedAssets) {
onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets.map((asset) => toTimelineAsset(asset)) });
onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets.map((asset) => toAsset(asset)) });
}
};
</script>

View file

@ -31,7 +31,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 { toAsset } from '$lib/utils/timeline-util';
import {
AssetJobName,
AssetTypeEnum,
@ -165,7 +165,7 @@
{/if}
{#if !isOwner && showDownloadButton}
<DownloadAction asset={toTimelineAsset(asset)} />
<DownloadAction asset={toAsset(asset)} />
{/if}
{#if showDetailButton}
@ -184,7 +184,7 @@
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
{/if}
{#if showDownloadButton}
<DownloadAction asset={toTimelineAsset(asset)} menuItem />
<DownloadAction asset={toAsset(asset)} menuItem />
{/if}
{#if !isLocked}
@ -243,7 +243,7 @@
{/if}
{#if !asset.isTrashed}
<SetVisibilityAction asset={toTimelineAsset(asset)} {onAction} {preAction} />
<SetVisibilityAction asset={toAsset(asset)} {onAction} {preAction} />
{/if}
<hr />
<MenuOption

View file

@ -8,7 +8,7 @@
import { AssetAction, ProjectionType } from '$lib/constants';
import { activityManager } from '$lib/managers/activity-manager.svelte';
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { closeEditorCofirm } from '$lib/stores/asset-editor.store';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { isShowDetail } from '$lib/stores/preferences.store';
@ -18,7 +18,7 @@
import { getAssetJobMessage, getSharedLink, handlePromiseError } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { SlideshowHistory } from '$lib/utils/slideshow-history';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import {
AssetJobName,
AssetTypeEnum,
@ -50,7 +50,7 @@
interface Props {
asset: AssetResponseDto;
preloadAssets?: TimelineAsset[];
preloadAssets?: Asset[];
showNavigation?: boolean;
withStacked?: boolean;
isShared?: boolean;
@ -126,7 +126,7 @@
untrack(() => {
if (stack && stack?.assets.length > 1) {
preloadAssets.push(toTimelineAsset(stack.assets[1]));
preloadAssets.push(toAsset(stack.assets[1]));
}
});
};
@ -156,7 +156,7 @@
slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
if (value === SlideshowState.PlaySlideshow) {
slideshowHistory.reset();
slideshowHistory.queue(toTimelineAsset(asset));
slideshowHistory.queue(toAsset(asset));
handlePromiseError(handlePlaySlideshow());
} else if (value === SlideshowState.StopSlideshow) {
handlePromiseError(handleStopSlideshow());
@ -166,7 +166,7 @@
shuffleSlideshowUnsubscribe = slideshowNavigation.subscribe((value) => {
if (value === SlideshowNavigation.Shuffle) {
slideshowHistory.reset();
slideshowHistory.queue(toTimelineAsset(asset));
slideshowHistory.queue(toAsset(asset));
}
});
@ -569,7 +569,7 @@
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
brokenAssetClass="text-xs"
dimmed={stackedAsset.id !== asset.id}
asset={toTimelineAsset(stackedAsset)}
asset={toAsset(stackedAsset)}
onClick={() => {
asset = stackedAsset;
previewStackedAsset = undefined;

View file

@ -12,7 +12,7 @@
resetGlobalCropStore,
rotateDegrees,
} from '$lib/stores/asset-editor.store';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } 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';
@ -80,7 +80,7 @@
aria-label="Crop area"
type="button"
>
<img draggable="false" src={img?.src} alt={$getAltText(toTimelineAsset(asset))} />
<img draggable="false" src={img?.src} alt={$getAltText(toAsset(asset))} />
<div class={`${$isResizingOrDragging ? 'resizing' : ''} crop-frame`} bind:this={$cropFrame}>
<div class="grid"></div>
<div class="corner top-left"></div>

View file

@ -5,7 +5,7 @@
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
import { assetViewerFadeDuration } from '$lib/constants';
import { castManager } from '$lib/managers/cast-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
import { boundingBoxesArray } from '$lib/stores/people.store';
@ -18,7 +18,7 @@
import { getBoundingBox } from '$lib/utils/people-utils';
import { cancelImageUrl } from '$lib/utils/sw-messaging';
import { getAltText } from '$lib/utils/thumbnail-util';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
import { LoadingSpinner } from '@immich/ui';
import { onDestroy, onMount } from 'svelte';
@ -29,7 +29,7 @@
interface Props {
asset: AssetResponseDto;
preloadAssets?: TimelineAsset[] | undefined;
preloadAssets?: Asset[] | undefined;
element?: HTMLDivElement | undefined;
haveFadeTransition?: boolean;
sharedLink?: SharedLinkResponseDto | undefined;
@ -72,7 +72,7 @@
$boundingBoxesArray = [];
});
const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: TimelineAsset[]) => {
const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: Asset[]) => {
for (const preloadAsset of preloadAssets || []) {
if (preloadAsset.isImage) {
let img = new Image();
@ -249,7 +249,7 @@
<img
bind:this={$photoViewerImgElement}
src={assetFileUrl}
alt={$getAltText(toTimelineAsset(asset))}
alt={$getAltText(toAsset(asset))}
class="h-full w-full {$slideshowState === SlideshowState.None
? 'object-contain'
: slideshowLookCssMapping[$slideshowLook]}"

View file

@ -17,7 +17,7 @@
import { thumbhash } from '$lib/actions/thumbhash';
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { moveFocus } from '$lib/utils/focus-util';
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
@ -30,7 +30,7 @@
import VideoThumbnail from './video-thumbnail.svelte';
interface Props {
asset: TimelineAsset;
asset: Asset;
groupIndex?: number;
thumbnailSize?: number;
thumbnailWidth?: number;
@ -45,8 +45,8 @@
imageClass?: ClassValue;
brokenAssetClass?: ClassValue;
dimmed?: boolean;
onClick?: (asset: TimelineAsset) => void;
onSelect?: (asset: TimelineAsset) => void;
onClick?: (asset: Asset) => void;
onSelect?: (asset: Asset) => void;
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { assetViewerFadeDuration } from '$lib/constants';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAltText } from '$lib/utils/thumbnail-util';
import { AssetMediaSize } from '@immich/sdk';
@ -9,7 +9,7 @@
import { fade } from 'svelte/transition';
interface Props {
asset: TimelineAsset;
asset: Asset;
onImageLoad: () => void;
}

View file

@ -1,13 +1,13 @@
<script lang="ts">
import { assetViewerFadeDuration } from '$lib/constants';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
import { AssetMediaSize } from '@immich/sdk';
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
interface Props {
asset: TimelineAsset;
asset: Asset;
videoPlayer: HTMLVideoElement | undefined;
videoViewerMuted?: boolean;
videoViewerVolume?: number;

View file

@ -27,7 +27,7 @@
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
import { AppRoute, QueryParameter } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
import type { Asset, Viewport } from '$lib/managers/timeline-manager/types';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { type MemoryAsset, memoryStore } from '$lib/stores/memory.store.svelte';
@ -35,7 +35,7 @@
import { preferences } from '$lib/stores/user.store';
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
import { cancelMultiselect } from '$lib/utils/asset-utils';
import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
import { fromISODateTimeUTC, toAsset } from '$lib/utils/timeline-util';
import { AssetMediaSize, getAssetInfo } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import {
@ -71,7 +71,7 @@
let currentMemoryAssetFull = $derived.by(async () =>
current?.asset ? await getAssetInfo({ ...authManager.params, id: current.asset.id }) : undefined,
);
let currentTimelineAssets = $derived(current?.memory.assets.map((asset) => toTimelineAsset(asset)) || []);
let currentAssets = $derived(current?.memory.assets.map((asset) => toAsset(asset)) || []);
let isSaved = $derived(current?.memory.isSaved);
let viewerHeight = $state(0);
@ -97,7 +97,7 @@
await goto(asHref(asset));
};
const setProgressDuration = (asset: TimelineAsset) => {
const setProgressDuration = (asset: Asset) => {
if (asset.isVideo) {
const timeParts = asset.duration!.split(':').map(Number);
const durationInMilliseconds = (timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]) * 1000;
@ -116,8 +116,7 @@
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.map((a) => toTimelineAsset(a)) || []);
const handleSelectAll = () => assetInteraction.selectAssets(current?.memory.assets.map((a) => toAsset(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!
@ -658,7 +657,7 @@
<GalleryViewer
onNext={handleNextAsset}
onPrevious={handlePreviousAsset}
assets={currentTimelineAssets}
assets={currentAssets}
viewport={galleryViewport}
{assetInteraction}
slidingWindowOffset={viewerHeight}

View file

@ -4,7 +4,7 @@
import { memoryStore } from '$lib/stores/memory.store.svelte';
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
import { getAltText } from '$lib/utils/thumbnail-util';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { Icon } from '@immich/ui';
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
import { onMount } from 'svelte';
@ -89,7 +89,7 @@
<img
class="h-full w-full rounded-xl object-cover"
src={getAssetThumbnailUrl(memory.assets[0].id)}
alt={$t('memory_lane_title', { values: { title: $getAltText(toTimelineAsset(memory.assets[0])) } })}
alt={$t('memory_lane_title', { values: { title: $getAltText(toAsset(memory.assets[0])) } })}
draggable="false"
/>
<div

View file

@ -14,7 +14,7 @@
import { cancelMultiselect, downloadArchive } from '$lib/utils/asset-utils';
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
import { handleError } from '$lib/utils/handle-error';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { addSharedLinkAssets, getAssetInfo, type SharedLinkResponseDto } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import { mdiArrowLeft, mdiDownload, mdiFileImagePlusOutline, mdiSelectAll } from '@mdi/js';
@ -34,7 +34,7 @@
const viewport: Viewport = $state({ width: 0, height: 0 });
const assetInteraction = new AssetInteraction();
let assets = $derived(sharedLink.assets.map((a) => toTimelineAsset(a)));
let assets = $derived(sharedLink.assets.map((a) => toAsset(a)));
dragAndDropFilesStore.subscribe((value) => {
if (value.isDragging && value.files.length > 0) {

View file

@ -5,7 +5,7 @@
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
import { AppRoute, AssetAction } from '$lib/constants';
import Portal from '$lib/elements/Portal.svelte';
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
import type { Asset, Viewport } from '$lib/managers/timeline-manager/types';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
@ -18,7 +18,7 @@
import { handleError } from '$lib/utils/handle-error';
import { getJustifiedLayoutFromAssets, type CommonJustifiedLayout } from '$lib/utils/layout-utils';
import { navigate } from '$lib/utils/navigation';
import { isTimelineAsset, toTimelineAsset } from '$lib/utils/timeline-util';
import { isAsset, toAsset } from '$lib/utils/timeline-util';
import { AssetVisibility, type AssetResponseDto } from '@immich/sdk';
import { modalManager } from '@immich/ui';
import { debounce } from 'lodash-es';
@ -28,7 +28,7 @@
interface Props {
initialAssetId?: string;
assets: (TimelineAsset | AssetResponseDto)[];
assets: (Asset | AssetResponseDto)[];
assetInteraction: AssetInteraction;
disableAssetSelect?: boolean;
showArchiveIcon?: boolean;
@ -130,7 +130,7 @@
}
let shiftKeyIsDown = $state(false);
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let lastAssetMouseEvent: Asset | null = $state(null);
let slidingWindow = $state({ top: 0, bottom: 0 });
const updateSlidingWindow = () => {
@ -157,14 +157,14 @@
}
}
});
const viewAssetHandler = async (asset: TimelineAsset) => {
const viewAssetHandler = async (asset: Asset) => {
currentIndex = assets.findIndex((a) => a.id == asset.id);
await setAssetId(assets[currentIndex].id);
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
};
const selectAllAssets = () => {
assetInteraction.selectAssets(assets.map((a) => toTimelineAsset(a)));
assetInteraction.selectAssets(assets.map((a) => toAsset(a)));
};
const deselectAllAssets = () => {
@ -186,7 +186,7 @@
}
};
const handleSelectAssets = (asset: TimelineAsset) => {
const handleSelectAssets = (asset: Asset) => {
if (!asset) {
return;
}
@ -209,14 +209,14 @@
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
};
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
const handleSelectAssetCandidates = (asset: Asset | null) => {
if (asset) {
selectAssetCandidates(asset);
}
lastAssetMouseEvent = asset;
};
const selectAssetCandidates = (endAsset: TimelineAsset) => {
const selectAssetCandidates = (endAsset: Asset) => {
if (!shiftKeyIsDown) {
return;
}
@ -233,7 +233,7 @@
[start, end] = [end, start];
}
assetInteraction.setAssetSelectionCandidates(assets.slice(start, end + 1).map((a) => toTimelineAsset(a)));
assetInteraction.setAssetSelectionCandidates(assets.slice(start, end + 1).map((a) => toAsset(a)));
};
const onSelectStart = (event: Event) => {
@ -434,7 +434,7 @@
}
};
const assetMouseEventHandler = (asset: TimelineAsset | null) => {
const assetMouseEventHandler = (asset: Asset | null) => {
if (assetInteraction.selectionActive) {
handleSelectAssetCandidates(asset);
}
@ -496,21 +496,21 @@
readonly={disableAssetSelect}
onClick={() => {
if (assetInteraction.selectionActive) {
handleSelectAssets(toTimelineAsset(currentAsset));
handleSelectAssets(toAsset(currentAsset));
return;
}
void viewAssetHandler(toTimelineAsset(currentAsset));
void viewAssetHandler(toAsset(currentAsset));
}}
onSelect={() => handleSelectAssets(toTimelineAsset(currentAsset))}
onMouseEvent={() => assetMouseEventHandler(toTimelineAsset(currentAsset))}
onSelect={() => handleSelectAssets(toAsset(currentAsset))}
onMouseEvent={() => assetMouseEventHandler(toAsset(currentAsset))}
{showArchiveIcon}
asset={toTimelineAsset(currentAsset)}
asset={toAsset(currentAsset)}
selected={assetInteraction.hasSelectedAsset(currentAsset.id)}
selectionCandidate={assetInteraction.hasSelectionCandidate(currentAsset.id)}
thumbnailWidth={layout.width}
thumbnailHeight={layout.height}
/>
{#if showAssetName && !isTimelineAsset(currentAsset)}
{#if showAssetName && !isAsset(currentAsset)}
<div
class="absolute text-center p-1 text-xs font-mono font-semibold w-full bottom-0 bg-linear-to-t bg-slate-50/75 dark:bg-slate-800/75 overflow-clip text-ellipsis whitespace-pre-wrap"
>

View file

@ -4,8 +4,8 @@
export interface AssetControlContext {
// Wrap assets in a function, because context isn't reactive.
getAssets: () => TimelineAsset[]; // All assets includes partners' assets
getOwnedAssets: () => TimelineAsset[]; // Only assets owned by the user
getAssets: () => Asset[]; // All assets includes partners' assets
getOwnedAssets: () => Asset[]; // Only assets owned by the user
clearSelect: () => void;
}
@ -14,13 +14,13 @@
</script>
<script lang="ts">
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { mdiClose } from '@mdi/js';
import type { Snippet } from 'svelte';
import ControlAppBar from '../shared-components/control-app-bar.svelte';
interface Props {
assets: TimelineAsset[];
assets: Asset[];
clearSelect: () => void;
ownerId?: string | undefined;
children?: Snippet;

View file

@ -3,7 +3,7 @@
import { page } from '$app/stores';
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
import Scrubber from '$lib/components/timeline/Scrubber.svelte';
import TimelineAssetViewer from '$lib/components/timeline/TimelineAssetViewer.svelte';
import AssetViewer from '$lib/components/timeline/TimelineAssetViewer.svelte';
import TimelineKeyboardActions from '$lib/components/timeline/actions/TimelineKeyboardActions.svelte';
import { AssetAction } from '$lib/constants';
import HotModuleReload from '$lib/elements/HotModuleReload.svelte';
@ -12,7 +12,7 @@
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
@ -48,21 +48,16 @@
album?: AlbumResponseDto | null;
person?: PersonResponseDto | null;
isShowDeleteConfirmation?: boolean;
onSelect?: (asset: TimelineAsset) => void;
onSelect?: (asset: Asset) => void;
onEscape?: () => void;
children?: Snippet;
empty?: Snippet;
customLayout?: Snippet<[TimelineAsset]>;
customLayout?: Snippet<[Asset]>;
onThumbnailClick?: (
asset: TimelineAsset,
asset: Asset,
timelineManager: TimelineManager,
dayGroup: DayGroup,
onClick: (
timelineManager: TimelineManager,
assets: TimelineAsset[],
groupTitle: string,
asset: TimelineAsset,
) => void,
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
) => void;
}
@ -182,7 +177,7 @@
return true;
};
const scrollToAsset = (asset: TimelineAsset) => {
const scrollToAsset = (asset: Asset) => {
const monthGroup = timelineManager.getMonthGroupByAssetId(asset.id);
if (!monthGroup) {
return false;
@ -384,13 +379,13 @@
}
};
const handleSelectAsset = (asset: TimelineAsset) => {
const handleSelectAsset = (asset: Asset) => {
if (!timelineManager.albumAssets.has(asset.id)) {
assetInteraction.selectAsset(asset);
}
};
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let lastAssetMouseEvent: Asset | null = $state(null);
let shiftKeyIsDown = $state(false);
@ -407,14 +402,14 @@
shiftKeyIsDown = false;
}
};
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
const handleSelectAssetCandidates = (asset: Asset | null) => {
if (asset) {
void selectAssetCandidates(asset);
}
lastAssetMouseEvent = asset;
};
const handleGroupSelect = (timelineManager: TimelineManager, group: string, assets: TimelineAsset[]) => {
const handleGroupSelect = (timelineManager: TimelineManager, group: string, assets: Asset[]) => {
if (assetInteraction.selectedGroup.has(group)) {
assetInteraction.removeGroupFromMultiselectGroup(group);
for (const asset of assets) {
@ -434,7 +429,7 @@
}
};
const handleSelectAssets = async (asset: TimelineAsset) => {
const handleSelectAssets = async (asset: Asset) => {
if (!asset) {
return;
}
@ -518,7 +513,7 @@
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
};
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
const selectAssetCandidates = async (endAsset: Asset) => {
if (!shiftKeyIsDown) {
return;
}
@ -686,7 +681,7 @@
<Portal target="body">
{#if $showAssetViewer}
<TimelineAssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
<AssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
{/if}
</Portal>

View file

@ -6,7 +6,7 @@
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
import { navigate } from '$lib/utils/navigation';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
let { asset: viewingAsset, gridScrollTarget, mutex, preloadAssets } = assetViewingStore;
@ -135,12 +135,12 @@
break;
}
case AssetAction.REMOVE_ASSET_FROM_STACK: {
timelineManager.addAssets([toTimelineAsset(action.asset)]);
timelineManager.addAssets([toAsset(action.asset)]);
if (action.stack) {
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
updateUnstackedAssetInTimeline(
timelineManager,
action.stack.assets.map((asset) => toTimelineAsset(asset)),
action.stack.assets.map((asset) => toAsset(asset)),
);
updateStackedAssetInTimeline(timelineManager, {
stack: action.stack,
@ -155,7 +155,7 @@
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
updateUnstackedAssetInTimeline(
timelineManager,
action.stack.assets.map((asset) => toTimelineAsset(asset)),
action.stack.assets.map((asset) => toAsset(asset)),
);
updateStackedAssetInTimeline(timelineManager, {
stack: action.stack,

View file

@ -3,7 +3,7 @@
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
import type { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { assetSnapshot, assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
@ -28,22 +28,17 @@
monthGroup: MonthGroup;
timelineManager: TimelineManager;
assetInteraction: AssetInteraction;
customLayout?: Snippet<[TimelineAsset]>;
customLayout?: Snippet<[Asset]>;
onSelect: ({ title, assets }: { title: string; assets: TimelineAsset[] }) => void;
onSelectAssets: (asset: TimelineAsset) => void;
onSelectAssetCandidates: (asset: TimelineAsset | null) => void;
onSelect: ({ title, assets }: { title: string; assets: Asset[] }) => void;
onSelectAssets: (asset: Asset) => void;
onSelectAssetCandidates: (asset: Asset | null) => void;
onScrollCompensation: (compensation: { heightDelta?: number; scrollTop?: number }) => void;
onThumbnailClick?: (
asset: TimelineAsset,
asset: Asset,
timelineManager: TimelineManager,
dayGroup: DayGroup,
onClick: (
timelineManager: TimelineManager,
assets: TimelineAsset[],
groupTitle: string,
asset: TimelineAsset,
) => void,
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
) => void;
}
@ -70,12 +65,7 @@
monthGroup.timelineManager.suspendTransitions && !$isUploading ? 0 : 150,
);
const scaleDuration = $derived(transitionDuration === 0 ? 0 : transitionDuration + 100);
const _onClick = (
timelineManager: TimelineManager,
assets: TimelineAsset[],
groupTitle: string,
asset: TimelineAsset,
) => {
const _onClick = (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => {
if (isSelectionMode || assetInteraction.selectionActive) {
assetSelectHandler(timelineManager, asset, assets, groupTitle);
return;
@ -83,12 +73,12 @@
void navigate({ targetRoute: 'current', assetId: asset.id });
};
const handleSelectGroup = (title: string, assets: TimelineAsset[]) => onSelect({ title, assets });
const handleSelectGroup = (title: string, assets: Asset[]) => onSelect({ title, assets });
const assetSelectHandler = (
timelineManager: TimelineManager,
asset: TimelineAsset,
assetsInDayGroup: TimelineAsset[],
asset: Asset,
assetsInDayGroup: Asset[],
groupTitle: string,
) => {
onSelectAssets(asset);
@ -112,7 +102,7 @@
}
};
const assetMouseEventHandler = (groupTitle: string, asset: TimelineAsset | null) => {
const assetMouseEventHandler = (groupTitle: string, asset: Asset | null) => {
// Show multi select icon on hover on date group
hoveredDayGroup = groupTitle;

View file

@ -1,10 +1,10 @@
<script lang="ts">
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import type { OnLink, OnUnlink } from '$lib/utils/actions';
import { handleError } from '$lib/utils/handle-error';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { getAssetInfo, updateAsset } from '@immich/sdk';
import { IconButton } from '@immich/ui';
import { mdiLinkOff, mdiMotionPlayOutline, mdiTimerSand } from '@mdi/js';
@ -31,14 +31,14 @@
const handleLink = async () => {
let [still, motion] = [...getOwnedAssets()];
if ((still as TimelineAsset).isVideo) {
if ((still as Asset).isVideo) {
[still, motion] = [motion, still];
}
try {
loading = true;
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: motion.id } });
onLink({ still: toTimelineAsset(stillResponse), motion: motion as TimelineAsset });
onLink({ still: toAsset(stillResponse), motion: motion as Asset });
clearSelect();
} catch (error) {
handleError(error, $t('errors.unable_to_link_motion_video'));
@ -60,7 +60,7 @@
loading = true;
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: null } });
const motionResponse = await getAssetInfo({ ...authManager.params, id: motionId });
onUnlink({ still: toTimelineAsset(stillResponse), motion: toTimelineAsset(motionResponse) });
onUnlink({ still: toAsset(stillResponse), motion: toAsset(motionResponse) });
clearSelect();
} catch (error) {
handleError(error, $t('errors.unable_to_unlink_motion_video'));

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
import type { OnStack, OnUnstack } from '$lib/utils/actions';
import { deleteStack, stackAssets } from '$lib/utils/asset-utils';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { mdiImageMultipleOutline, mdiImageOffOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
@ -35,7 +35,7 @@
}
const unstackedAssets = await deleteStack([stack.id]);
if (unstackedAssets) {
onUnstack?.(unstackedAssets.map((a) => toTimelineAsset(a)));
onUnstack?.(unstackedAssets.map((a) => toAsset(a)));
}
clearSelect();
};

View file

@ -12,7 +12,7 @@
} from '$lib/components/timeline/actions/focus-actions';
import { AppRoute } from '$lib/constants';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
@ -32,7 +32,7 @@
assetInteraction: AssetInteraction;
isShowDeleteConfirmation: boolean;
onEscape?: () => void;
scrollToAsset: (asset: TimelineAsset) => boolean;
scrollToAsset: (asset: Asset) => boolean;
}
let {

View file

@ -1,5 +1,5 @@
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { moveFocus } from '$lib/utils/focus-util';
import { InvocationTracker } from '$lib/utils/invocationTracker';
import { tick } from 'svelte';
@ -21,7 +21,7 @@ export const focusPreviousAsset = () =>
const queryHTMLElement = (query: string) => document.querySelector(query) as HTMLElement;
export const setFocusToAsset = (scrollToAsset: (asset: TimelineAsset) => boolean, asset: TimelineAsset) => {
export const setFocusToAsset = (scrollToAsset: (asset: Asset) => boolean, asset: Asset) => {
const scrolled = scrollToAsset(asset);
if (scrolled) {
const element = queryHTMLElement(`[data-thumbnail-focus-container][data-asset="${asset.id}"]`);
@ -30,7 +30,7 @@ export const setFocusToAsset = (scrollToAsset: (asset: TimelineAsset) => boolean
};
export const setFocusTo = async (
scrollToAsset: (asset: TimelineAsset) => boolean,
scrollToAsset: (asset: Asset) => boolean,
store: TimelineManager,
direction: 'earlier' | 'later',
interval: 'day' | 'month' | 'year' | 'asset',

View file

@ -2,7 +2,7 @@
import { getAssetThumbnailUrl } from '$lib/utils';
import { getAssetResolution, getFileSize } from '$lib/utils/asset-utils';
import { getAltText } from '$lib/utils/thumbnail-util';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { type AssetResponseDto, getAllAlbums } from '@immich/sdk';
import { Icon } from '@immich/ui';
import { mdiHeart, mdiImageMultipleOutline, mdiMagnifyPlus } from '@mdi/js';
@ -37,7 +37,7 @@
<!-- THUMBNAIL-->
<img
src={getAssetThumbnailUrl(asset.id)}
alt={$getAltText(toTimelineAsset(asset))}
alt={$getAltText(toAsset(asset))}
title={assetData}
class="h-60 object-cover rounded-t-xl w-full"
draggable="false"

View file

@ -1,7 +1,7 @@
<script lang="ts">
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
import { getFileSize } from '$lib/utils/asset-utils';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { type AssetResponseDto } from '@immich/sdk';
interface Props {
@ -22,7 +22,7 @@
title={assetData}
>
<div class="relative w-full h-full overflow-hidden rounded-lg">
<Thumbnail asset={toTimelineAsset(asset)} readonly onClick={() => onViewAsset(asset)} thumbnailSize={boxWidth} />
<Thumbnail asset={toAsset(asset)} readonly onClick={() => onViewAsset(asset)} thumbnailSize={boxWidth} />
{#if !!asset.libraryId}
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-red-500">External</div>

View file

@ -6,7 +6,7 @@ import { plainDateTimeCompare } from '$lib/utils/timeline-util';
import { SvelteSet } from 'svelte/reactivity';
import type { MonthGroup } from './month-group.svelte';
import type { AssetOperation, Direction, MoveAsset, TimelineAsset } from './types';
import type { Asset, AssetOperation, Direction, MoveAsset } from './types';
import { ViewerAsset } from './viewer-asset.svelte';
export class DayGroup {
@ -82,7 +82,7 @@ export class DayGroup {
return this.viewerAssets[0]?.asset;
}
*assetsIterator(options: { startAsset?: TimelineAsset; direction?: Direction } = {}) {
*assetsIterator(options: { startAsset?: Asset; direction?: Direction } = {}) {
const isEarlier = (options?.direction ?? 'earlier') === 'earlier';
let assetIndex = options?.startAsset
? this.viewerAssets.findIndex((viewerAsset) => viewerAsset.asset.id === options.startAsset!.id)

View file

@ -3,13 +3,13 @@ import { AssetOrder } from '@immich/sdk';
import { SvelteSet } from 'svelte/reactivity';
import type { DayGroup } from './day-group.svelte';
import type { MonthGroup } from './month-group.svelte';
import type { TimelineAsset } from './types';
import type { Asset } from './types';
export class GroupInsertionCache {
#lookupCache: {
[year: number]: { [month: number]: { [day: number]: DayGroup } };
} = {};
unprocessedAssets: TimelineAsset[] = [];
unprocessedAssets: Asset[] = [];
changedDayGroups = new SvelteSet<DayGroup>();
newDayGroups = new SvelteSet<DayGroup>();

View file

@ -5,13 +5,13 @@ import { SvelteSet } from 'svelte/reactivity';
import { GroupInsertionCache } from '../group-insertion-cache.svelte';
import { MonthGroup } from '../month-group.svelte';
import type { TimelineManager } from '../timeline-manager.svelte';
import type { AssetOperation, TimelineAsset } from '../types';
import type { Asset, AssetOperation } from '../types';
import { updateGeometry } from './layout-support.svelte';
import { getMonthGroupByDate } from './search-support.svelte';
export function addAssetsToMonthGroups(
timelineManager: TimelineManager,
assets: TimelineAsset[],
assets: Asset[],
options: { order: AssetOrder },
) {
if (assets.length === 0) {
@ -30,7 +30,7 @@ export function addAssetsToMonthGroups(
timelineManager.months.push(month);
}
month.addTimelineAsset(asset, addContext);
month.addAsset(asset, addContext);
updatedMonthGroups.add(month);
}
@ -70,7 +70,7 @@ export function runAssetOperation(
const changedMonthGroups = new SvelteSet<MonthGroup>();
let idsToProcess = new SvelteSet(ids);
const idsProcessed = new SvelteSet<string>();
const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = [];
const combinedMoveAssets: { asset: Asset; date: TimelineDate }[][] = [];
for (const month of timelineManager.months) {
if (idsToProcess.size > 0) {
const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation);

View file

@ -2,14 +2,14 @@ import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timelin
import { AssetOrder } from '@immich/sdk';
import type { MonthGroup } from '../month-group.svelte';
import type { TimelineManager } from '../timeline-manager.svelte';
import type { AssetDescriptor, Direction, TimelineAsset } from '../types';
import type { Asset, AssetDescriptor, Direction } from '../types';
export async function getAssetWithOffset(
timelineManager: TimelineManager,
assetDescriptor: AssetDescriptor,
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
direction: Direction,
): Promise<TimelineAsset | undefined> {
): Promise<Asset | undefined> {
const { asset, monthGroup } = findMonthGroupForAsset(timelineManager, assetDescriptor.id) ?? {};
if (!monthGroup || !asset) {
return;
@ -51,7 +51,7 @@ export function getMonthGroupByDate(
async function getAssetByAssetOffset(
timelineManager: TimelineManager,
asset: TimelineAsset,
asset: Asset,
monthGroup: MonthGroup,
direction: Direction,
) {
@ -70,7 +70,7 @@ async function getAssetByAssetOffset(
async function getAssetByDayOffset(
timelineManager: TimelineManager,
asset: TimelineAsset,
asset: Asset,
monthGroup: MonthGroup,
direction: Direction,
) {
@ -120,7 +120,7 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass
[startMonthGroup, endMonthGroup] = [endMonthGroup, startMonthGroup];
}
const range: TimelineAsset[] = [];
const range: Asset[] = [];
const startDayGroup = startMonthGroup.findDayGroupForAsset(startAsset);
for await (const targetAsset of timelineManager.assetsIterator({
startMonthGroup,

View file

@ -1,7 +1,7 @@
import type { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { PendingChange, TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset, PendingChange } from '$lib/managers/timeline-manager/types';
import { websocketEvents } from '$lib/stores/websocket';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { throttle } from 'lodash-es';
import type { Unsubscriber } from 'svelte/store';
@ -31,11 +31,11 @@ export class WebsocketSupport {
connectWebsocketEvents() {
this.#unsubscribers.push(
websocketEvents.on('on_upload_success', (asset) =>
this.#addPendingChanges({ type: 'add', values: [toTimelineAsset(asset)] }),
this.#addPendingChanges({ type: 'add', values: [toAsset(asset)] }),
),
websocketEvents.on('on_asset_trash', (ids) => this.#addPendingChanges({ type: 'trash', values: ids })),
websocketEvents.on('on_asset_update', (asset) =>
this.#addPendingChanges({ type: 'update', values: [toTimelineAsset(asset)] }),
this.#addPendingChanges({ type: 'update', values: [toAsset(asset)] }),
),
websocketEvents.on('on_asset_delete', (id: string) => this.#addPendingChanges({ type: 'delete', values: [id] })),
);
@ -55,8 +55,8 @@ export class WebsocketSupport {
#getPendingChangeBatches() {
const batch: {
add: TimelineAsset[];
update: TimelineAsset[];
add: Asset[];
update: Asset[];
remove: string[];
} = {
add: [],

View file

@ -21,7 +21,7 @@ import { SvelteSet } from 'svelte/reactivity';
import { DayGroup } from './day-group.svelte';
import { GroupInsertionCache } from './group-insertion-cache.svelte';
import type { TimelineManager } from './timeline-manager.svelte';
import type { AssetDescriptor, AssetOperation, Direction, MoveAsset, TimelineAsset } from './types';
import type { Asset, AssetDescriptor, AssetOperation, Direction, MoveAsset } from './types';
import { ViewerAsset } from './viewer-asset.svelte';
export class MonthGroup {
@ -101,7 +101,7 @@ export class MonthGroup {
getAssets() {
// eslint-disable-next-line unicorn/no-array-reduce
return this.dayGroups.reduce((accumulator: TimelineAsset[], g: DayGroup) => accumulator.concat(g.getAssets()), []);
return this.dayGroups.reduce((accumulator: Asset[], g: DayGroup) => accumulator.concat(g.getAssets()), []);
}
sortDayGroups() {
@ -161,7 +161,7 @@ export class MonthGroup {
bucketAssets.localOffsetHours[i],
);
const timelineAsset: TimelineAsset = {
const timelineAsset: Asset = {
city: bucketAssets.city[i],
country: bucketAssets.country[i],
duration: bucketAssets.duration[i],
@ -192,7 +192,7 @@ export class MonthGroup {
timelineAsset.latitude = bucketAssets.latitude?.[i];
timelineAsset.longitude = bucketAssets.longitude?.[i];
}
this.addTimelineAsset(timelineAsset, addContext);
this.addAsset(timelineAsset, addContext);
}
for (const group of addContext.existingDayGroups) {
@ -208,7 +208,7 @@ export class MonthGroup {
return addContext.unprocessedAssets;
}
addTimelineAsset(timelineAsset: TimelineAsset, addContext: GroupInsertionCache) {
addAsset(timelineAsset: Asset, addContext: GroupInsertionCache) {
const { localDateTime } = timelineAsset;
const { year, month } = this.yearMonth;
@ -294,7 +294,7 @@ export class MonthGroup {
handleError(error, _$t('errors.failed_to_load_assets'));
}
findDayGroupForAsset(asset: TimelineAsset) {
findDayGroupForAsset(asset: Asset) {
for (const group of this.dayGroups) {
if (group.viewerAssets.some((viewerAsset) => viewerAsset.id === asset.id)) {
return group;
@ -321,7 +321,7 @@ export class MonthGroup {
return -1;
}
*assetsIterator(options?: { startDayGroup?: DayGroup; startAsset?: TimelineAsset; direction?: Direction }) {
*assetsIterator(options?: { startDayGroup?: DayGroup; startAsset?: Asset; direction?: Direction }) {
const direction = options?.direction ?? 'earlier';
let { startAsset } = options ?? {};
const isEarlier = direction === 'earlier';

View file

@ -5,7 +5,7 @@ import { fromISODateTimeUTCToObject } from '$lib/utils/timeline-util';
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory';
import { TimelineManager } from './timeline-manager.svelte';
import type { TimelineAsset } from './types';
import type { Asset } from './types';
async function getAssets(timelineManager: TimelineManager) {
const assets = [];
@ -15,7 +15,7 @@ async function getAssets(timelineManager: TimelineManager) {
return assets;
}
function deriveLocalDateTimeFromFileCreatedAt(arg: TimelineAsset): TimelineAsset {
function deriveLocalDateTimeFromFileCreatedAt(arg: Asset): Asset {
return {
...arg,
localDateTime: arg.fileCreatedAt,
@ -29,7 +29,7 @@ describe('TimelineManager', () => {
describe('init', () => {
let timelineManager: TimelineManager;
const bucketAssets: Record<string, TimelineAsset[]> = {
const bucketAssets: Record<string, Asset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
@ -94,7 +94,7 @@ describe('TimelineManager', () => {
describe('loadMonthGroup', () => {
let timelineManager: TimelineManager;
const bucketAssets: Record<string, TimelineAsset[]> = {
const bucketAssets: Record<string, Asset[]> = {
'2024-01-03T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
@ -436,7 +436,7 @@ describe('TimelineManager', () => {
describe('getLaterAsset', () => {
let timelineManager: TimelineManager;
const bucketAssets: Record<string, TimelineAsset[]> = {
const bucketAssets: Record<string, Asset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,
@ -583,7 +583,7 @@ describe('TimelineManager', () => {
describe('getRandomAsset', () => {
let timelineManager: TimelineManager;
const bucketAssets: Record<string, TimelineAsset[]> = {
const bucketAssets: Record<string, Asset[]> = {
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
deriveLocalDateTimeFromFileCreatedAt({
...asset,

View file

@ -3,7 +3,7 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { CancellableTask } from '$lib/utils/cancellable-task';
import { toTimelineAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util';
import { toAsset, type TimelineDateTime, type TimelineYearMonth } from '$lib/utils/timeline-util';
import { clamp, debounce, isEqual } from 'lodash-es';
import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity';
@ -27,11 +27,11 @@ import { DayGroup } from './day-group.svelte';
import { isMismatched, updateObject } from './internal/utils.svelte';
import { MonthGroup } from './month-group.svelte';
import type {
Asset,
AssetDescriptor,
AssetOperation,
Direction,
ScrubberMonth,
TimelineAsset,
TimelineManagerLayoutOptions,
TimelineManagerOptions,
Viewport,
@ -192,7 +192,7 @@ export class TimelineManager {
async *assetsIterator(options?: {
startMonthGroup?: MonthGroup;
startDayGroup?: DayGroup;
startAsset?: TimelineAsset;
startAsset?: Asset;
direction?: Direction;
}) {
const direction = options?.direction ?? 'earlier';
@ -409,7 +409,7 @@ export class TimelineManager {
}
}
addAssets(assets: TimelineAsset[]) {
addAssets(assets: Asset[]) {
const assetsToUpdate = assets.filter((asset) => !this.isExcluded(asset));
const notUpdated = this.updateAssets(assetsToUpdate);
addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc });
@ -430,7 +430,7 @@ export class TimelineManager {
return;
}
const asset = toTimelineAsset(response);
const asset = toAsset(response);
if (!asset || this.isExcluded(asset)) {
return;
}
@ -454,7 +454,7 @@ export class TimelineManager {
// note: the `index` input is expected to be in the range [0, assetCount). This
// value can be passed to make the method deterministic, which is mainly useful
// for testing.
async getRandomAsset(index?: number): Promise<TimelineAsset | undefined> {
async getRandomAsset(index?: number): Promise<Asset | undefined> {
const randomAssetIndex = index ?? Math.floor(Math.random() * this.assetCount);
let accumulatedCount = 0;
@ -493,8 +493,8 @@ export class TimelineManager {
runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc });
}
updateAssets(assets: TimelineAsset[]) {
const lookup = new SvelteMap<string, TimelineAsset>(assets.map((asset) => [asset.id, asset]));
updateAssets(assets: Asset[]) {
const lookup = new SvelteMap<string, Asset>(assets.map((asset) => [asset.id, asset]));
const { unprocessedIds } = runAssetOperation(
this,
new SvelteSet(lookup.keys()),
@ -504,7 +504,7 @@ export class TimelineManager {
},
{ order: this.#options.order ?? AssetOrder.Desc },
);
const result: TimelineAsset[] = [];
const result: Asset[] = [];
for (const id of unprocessedIds.values()) {
result.push(lookup.get(id)!);
}
@ -530,21 +530,21 @@ export class TimelineManager {
this.updateIntersections();
}
getFirstAsset(): TimelineAsset | undefined {
getFirstAsset(): Asset | undefined {
return this.months[0]?.getFirstAsset();
}
async getLaterAsset(
assetDescriptor: AssetDescriptor,
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
): Promise<TimelineAsset | undefined> {
): Promise<Asset | undefined> {
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
}
async getEarlierAsset(
assetDescriptor: AssetDescriptor,
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
): Promise<TimelineAsset | undefined> {
): Promise<Asset | undefined> {
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
}
@ -567,7 +567,7 @@ export class TimelineManager {
return retrieveRangeUtil(this, start, end);
}
isExcluded(asset: TimelineAsset) {
isExcluded(asset: Asset) {
return (
isMismatched(this.#options.visibility, asset.visibility) ||
isMismatched(this.#options.isFavorite, asset.isFavorite) ||

View file

@ -12,7 +12,7 @@ export type AssetDescriptor = { id: string };
export type Direction = 'earlier' | 'later';
export type TimelineAsset = {
export type Asset = {
id: string;
ownerId: string;
ratio: number;
@ -35,9 +35,9 @@ export type TimelineAsset = {
longitude?: number | null;
};
export type AssetOperation = (asset: TimelineAsset) => { remove: boolean };
export type AssetOperation = (asset: Asset) => { remove: boolean };
export type MoveAsset = { asset: TimelineAsset; date: TimelineDate };
export type MoveAsset = { asset: Asset; date: TimelineDate };
export interface Viewport {
width: number;
@ -51,12 +51,12 @@ export type ViewportXY = Viewport & {
export interface AddAsset {
type: 'add';
values: TimelineAsset[];
values: Asset[];
}
export interface UpdateAsset {
type: 'update';
values: TimelineAsset[];
values: Asset[];
}
export interface DeleteAsset {

View file

@ -1,4 +1,4 @@
import type { TimelineAsset } from './types';
import type { Asset } from './types';
export const assetSnapshot = (asset: TimelineAsset): TimelineAsset => $state.snapshot(asset);
export const assetsSnapshot = (assets: TimelineAsset[]) => assets.map((asset) => $state.snapshot(asset));
export const assetSnapshot = (asset: Asset): Asset => $state.snapshot(asset);
export const assetsSnapshot = (assets: Asset[]) => assets.map((asset) => $state.snapshot(asset));

View file

@ -2,7 +2,7 @@ import type { CommonPosition } from '$lib/utils/layout-utils';
import type { DayGroup } from './day-group.svelte';
import { calculateViewerAssetIntersecting } from './internal/intersection-support.svelte';
import type { TimelineAsset } from './types';
import type { Asset } from './types';
export class ViewerAsset {
readonly #group: DayGroup;
@ -19,10 +19,10 @@ export class ViewerAsset {
});
position: CommonPosition | undefined = $state();
asset: TimelineAsset = <TimelineAsset>$state();
asset: Asset = <Asset>$state();
id: string = $derived(this.asset.id);
constructor(group: DayGroup, asset: TimelineAsset) {
constructor(group: DayGroup, asset: Asset) {
this.#group = group;
this.asset = asset;
}

View file

@ -1,20 +1,20 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { user } from '$lib/stores/user.store';
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
import { SvelteSet } from 'svelte/reactivity';
import { fromStore } from 'svelte/store';
export class AssetInteraction {
selectedAssets = $state<TimelineAsset[]>([]);
selectedAssets = $state<Asset[]>([]);
hasSelectedAsset(assetId: string) {
return this.selectedAssets.some((asset) => asset.id === assetId);
}
selectedGroup = new SvelteSet<string>();
assetSelectionCandidates = $state<TimelineAsset[]>([]);
assetSelectionCandidates = $state<Asset[]>([]);
hasSelectionCandidate(assetId: string) {
return this.assetSelectionCandidates.some((asset) => asset.id === assetId);
}
assetSelectionStart = $state<TimelineAsset | null>(null);
assetSelectionStart = $state<Asset | null>(null);
selectionActive = $derived(this.selectedAssets.length > 0);
private user = fromStore<UserAdminResponseDto | undefined>(user);
@ -25,13 +25,13 @@ export class AssetInteraction {
isAllFavorite = $derived(this.selectedAssets.every((asset) => asset.isFavorite));
isAllUserOwned = $derived(this.selectedAssets.every((asset) => asset.ownerId === this.userId));
selectAsset(asset: TimelineAsset) {
selectAsset(asset: Asset) {
if (!this.hasSelectedAsset(asset.id)) {
this.selectedAssets.push(asset);
}
}
selectAssets(assets: TimelineAsset[]) {
selectAssets(assets: Asset[]) {
for (const asset of assets) {
this.selectAsset(asset);
}
@ -52,11 +52,11 @@ export class AssetInteraction {
this.selectedGroup.delete(group);
}
setAssetSelectionStart(asset: TimelineAsset | null) {
setAssetSelectionStart(asset: Asset | null) {
this.assetSelectionStart = asset;
}
setAssetSelectionCandidates(assets: TimelineAsset[]) {
setAssetSelectionCandidates(assets: Asset[]) {
this.assetSelectionCandidates = assets;
}

View file

@ -1,5 +1,5 @@
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { type AssetGridRouteSearchParams } from '$lib/utils/navigation';
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
import { Mutex } from 'async-mutex';
@ -7,12 +7,12 @@ import { readonly, writable } from 'svelte/store';
function createAssetViewingStore() {
const viewingAssetStoreState = writable<AssetResponseDto>();
const preloadAssets = writable<TimelineAsset[]>([]);
const preloadAssets = writable<Asset[]>([]);
const viewState = writable<boolean>(false);
const viewingAssetMutex = new Mutex();
const gridScrollTarget = writable<AssetGridRouteSearchParams | null | undefined>();
const setAsset = (asset: AssetResponseDto, assetsToPreload: TimelineAsset[] = []) => {
const setAsset = (asset: AssetResponseDto, assetsToPreload: Asset[] = []) => {
preloadAssets.set(assetsToPreload);
viewingAssetStoreState.set(asset);
viewState.set(true);

View file

@ -1,7 +1,7 @@
import { eventManager } from '$lib/managers/event-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { asLocalTimeISO } from '$lib/utils/date-time';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { deleteMemory, type MemoryResponseDto, removeMemoryAssets, searchMemories, updateMemory } from '@immich/sdk';
import { DateTime } from 'luxon';
@ -12,7 +12,7 @@ type MemoryIndex = {
export type MemoryAsset = MemoryIndex & {
memory: MemoryResponseDto;
asset: TimelineAsset;
asset: Asset;
previousMemory?: MemoryResponseDto;
previous?: MemoryAsset;
next?: MemoryAsset;
@ -36,7 +36,7 @@ class MemoryStoreSvelte {
memoryIndex,
previousMemory: this.memories[memoryIndex - 1],
nextMemory: this.memories[memoryIndex + 1],
asset: toTimelineAsset(asset),
asset: toAsset(asset),
assetIndex,
previous,
};

View file

@ -1,6 +1,6 @@
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import type { StackResponse } from '$lib/utils/asset-utils';
import { AssetVisibility, deleteAssets as deleteBulk, restoreAssets } from '@immich/sdk';
import { t } from 'svelte-i18n';
@ -8,21 +8,21 @@ import { get } from 'svelte/store';
import { handleError } from './handle-error';
export type OnDelete = (assetIds: string[]) => void;
export type OnUndoDelete = (assets: TimelineAsset[]) => void;
export type OnUndoDelete = (assets: Asset[]) => void;
export type OnRestore = (ids: string[]) => void;
export type OnLink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
export type OnUnlink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
export type OnLink = (assets: { still: Asset; motion: Asset }) => void;
export type OnUnlink = (assets: { still: Asset; motion: Asset }) => void;
export type OnAddToAlbum = (ids: string[], albumId: string) => void;
export type OnArchive = (ids: string[], visibility: AssetVisibility) => void;
export type OnFavorite = (ids: string[], favorite: boolean) => void;
export type OnStack = (result: StackResponse) => void;
export type OnUnstack = (assets: TimelineAsset[]) => void;
export type OnUnstack = (assets: Asset[]) => void;
export type OnSetVisibility = (ids: string[]) => void;
export const deleteAssets = async (
force: boolean,
onAssetDelete: OnDelete,
assets: TimelineAsset[],
assets: Asset[],
onUndoDelete: OnUndoDelete | undefined = undefined,
) => {
const $t = get(t);
@ -47,7 +47,7 @@ export const deleteAssets = async (
}
};
const undoDeleteAssets = async (onUndoDelete: OnUndoDelete, assets: TimelineAsset[]) => {
const undoDeleteAssets = async (onUndoDelete: OnUndoDelete, assets: Asset[]) => {
const $t = get(t);
try {
const ids = assets.map((a) => a.id);
@ -89,7 +89,7 @@ export function updateStackedAssetInTimeline(timelineManager: TimelineManager, {
* @param timelineManager - The timeline manager to update.
* @param assets - The array of asset response DTOs to update in the timeline manager.
*/
export function updateUnstackedAssetInTimeline(timelineManager: TimelineManager, assets: TimelineAsset[]) {
export function updateUnstackedAssetInTimeline(timelineManager: TimelineManager, assets: Asset[]) {
timelineManager.updateAssetOperation(
assets.map((asset) => asset.id),
(asset) => {

View file

@ -4,7 +4,7 @@ import { AppRoute } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { downloadManager } from '$lib/managers/download-manager.svelte';
import type { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
@ -405,7 +405,7 @@ export const getAssetType = (type: AssetTypeEnum) => {
}
};
export const getSelectedAssets = (assets: TimelineAsset[], user: UserResponseDto | null): string[] => {
export const getSelectedAssets = (assets: Asset[], user: UserResponseDto | null): string[] => {
const ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;

View file

@ -2,9 +2,9 @@
// note: it's important that this is not imported in more than one file due to https://github.com/sveltejs/kit/issues/7805
// import { JustifiedLayout, type LayoutOptions } from '@immich/justified-layout-wasm';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { getAssetRatio } from '$lib/utils/asset-utils';
import { isTimelineAsset } from '$lib/utils/timeline-util';
import { isAsset } from '$lib/utils/timeline-util';
import type { AssetResponseDto } from '@immich/sdk';
import createJustifiedLayout from 'justified-layout';
@ -29,7 +29,7 @@ export type CommonLayoutOptions = {
};
export function getJustifiedLayoutFromAssets(
assets: (TimelineAsset | AssetResponseDto)[],
assets: (Asset | AssetResponseDto)[],
options: CommonLayoutOptions,
): CommonJustifiedLayout {
// if (useWasm) {
@ -90,7 +90,7 @@ class Adapter {
}
}
export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], options: CommonLayoutOptions) {
export function justifiedLayout(assets: (Asset | AssetResponseDto)[], options: CommonLayoutOptions) {
const adapter = {
targetRowHeight: options.rowHeight,
containerWidth: options.rowWidth,
@ -100,7 +100,7 @@ export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], op
};
const result = createJustifiedLayout(
assets.map((asset) => (isTimelineAsset(asset) ? asset.ratio : getAssetRatio(asset))),
assets.map((asset) => (isAsset(asset) ? asset.ratio : getAssetRatio(asset))),
adapter,
);
return new Adapter(result);

View file

@ -1,4 +1,4 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { getAltText } from '$lib/utils/thumbnail-util';
import { AssetVisibility } from '@immich/sdk';
import { init, register, waitLocale } from 'svelte-i18n';
@ -57,7 +57,7 @@ describe('getAltText', () => {
expected: string;
}) => {
const testDate = new Date('2024-01-01T12:00:00.000Z');
const asset: TimelineAsset = {
const asset: Asset = {
id: 'test-id',
ownerId: 'test-owner',
ratio: 1,

View file

@ -1,4 +1,4 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { locale } from '$lib/stores/preferences.store';
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
import { t } from 'svelte-i18n';
@ -39,7 +39,7 @@ export function getThumbnailSize(assetCount: number, viewWidth: number): number
}
export const getAltText = derived(t, ($t) => {
return (asset: TimelineAsset) => {
return (asset: Asset) => {
const date = fromTimelinePlainDateTime(asset.localDateTime).toJSDate().toLocaleString(get(locale), {
dateStyle: 'long',
timeZone: 'UTC',

View file

@ -1,4 +1,4 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { locale } from '$lib/stores/preferences.store';
import { getAssetRatio } from '$lib/utils/asset-utils';
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
@ -160,8 +160,8 @@ export const getDateTimeOffsetLocaleString = (date: DateTime, opts?: LocaleOptio
opts,
);
export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): TimelineAsset => {
if (isTimelineAsset(unknownAsset)) {
export const toAsset = (unknownAsset: AssetResponseDto | Asset): Asset => {
if (isAsset(unknownAsset)) {
return unknownAsset;
}
const assetResponse = unknownAsset;
@ -198,8 +198,8 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
};
};
export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset =>
(unknownAsset as TimelineAsset).ratio !== undefined;
export const isAsset = (unknownAsset: AssetResponseDto | Asset): unknownAsset is Asset =>
(unknownAsset as Asset).ratio !== undefined;
export const plainDateTimeCompare = (ascending: boolean, a: TimelineDateTime, b: TimelineDateTime) => {
const [aDateTime, bDateTime] = ascending ? [a, b] : [b, a];

View file

@ -34,7 +34,7 @@
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
import { activityManager } from '$lib/managers/activity-manager.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte';
@ -272,7 +272,7 @@
await refreshAlbum();
};
const handleUndoRemoveAssets = async (assets: TimelineAsset[]) => {
const handleUndoRemoveAssets = async (assets: Asset[]) => {
timelineManager.addAssets(assets);
await refreshAlbum();
};

View file

@ -26,7 +26,7 @@
import { foldersStore } from '$lib/stores/folders.svelte';
import { preferences } from '$lib/stores/user.store';
import { cancelMultiselect } from '$lib/utils/asset-utils';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { joinPaths } from '$lib/utils/tree-utils';
import { IconButton } from '@immich/ui';
import { mdiDotsVertical, mdiFolder, mdiFolderHome, mdiFolderOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
@ -72,7 +72,7 @@
return;
}
assetInteraction.selectAssets(data.pathAssets.map((asset) => toTimelineAsset(asset)));
assetInteraction.selectAssets(data.pathAssets.map((asset) => toAsset(asset)));
}
</script>

View file

@ -31,7 +31,7 @@
import Timeline from '$lib/components/timeline/Timeline.svelte';
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import PersonEditBirthDateModal from '$lib/modals/PersonEditBirthDateModal.svelte';
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
@ -204,7 +204,7 @@
data = { ...data, person };
};
const handleSelectFeaturePhoto = async (asset: TimelineAsset) => {
const handleSelectFeaturePhoto = async (asset: Asset) => {
if (viewMode !== PersonPageViewMode.SELECT_PERSON) {
return;
}
@ -354,7 +354,7 @@
await updateAssetCount();
};
const handleUndoDeleteAssets = async (assets: TimelineAsset[]) => {
const handleUndoDeleteAssets = async (assets: Asset[]) => {
timelineManager.addAssets(assets);
await updateAssetCount();
};

View file

@ -22,7 +22,7 @@
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
import { AppRoute, QueryParameter } from '$lib/constants';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset, Viewport } from '$lib/managers/timeline-manager/types';
import type { Asset, Viewport } from '$lib/managers/timeline-manager/types';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { lang, locale } from '$lib/stores/preferences.store';
@ -33,7 +33,7 @@
import { parseUtcDate } from '$lib/utils/date-time';
import { handleError } from '$lib/utils/handle-error';
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import {
type AlbumResponseDto,
getPerson,
@ -59,7 +59,7 @@
let nextPage = $state(1);
let searchResultAlbums: AlbumResponseDto[] = $state([]);
let searchResultAssets: TimelineAsset[] = $state([]);
let searchResultAssets: Asset[] = $state([]);
let isLoading = $state(true);
let scrollY = $state(0);
let scrollYHistory = 0;
@ -125,7 +125,7 @@
const onAssetDelete = (assetIds: string[]) => {
const assetIdSet = new Set(assetIds);
searchResultAssets = searchResultAssets.filter((asset: TimelineAsset) => !assetIdSet.has(asset.id));
searchResultAssets = searchResultAssets.filter((asset: Asset) => !assetIdSet.has(asset.id));
};
const handleSetVisibility = (assetIds: string[]) => {
@ -167,7 +167,7 @@
: await searchAssets({ metadataSearchDto: searchDto });
searchResultAlbums.push(...albums.items);
searchResultAssets.push(...assets.items.map((asset) => toTimelineAsset(asset)));
searchResultAssets.push(...assets.items.map((asset) => toAsset(asset)));
nextPage = Number(assets.nextPage) || 0;
} catch (error) {

View file

@ -7,12 +7,12 @@
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { cancelMultiselect } from '$lib/utils/asset-utils';
import { setQueryValue } from '$lib/utils/navigation';
import { toTimelineAsset } from '$lib/utils/timeline-util';
import { toAsset } from '$lib/utils/timeline-util';
import { AssetVisibility, getAssetInfo, updateAssets } from '@immich/sdk';
import { Button, LoadingSpinner, modalManager, Text } from '@immich/ui';
import { mdiMapMarkerMultipleOutline, mdiPencilOutline, mdiSelectRemove } from '@mdi/js';
@ -59,7 +59,7 @@
const updatedAssets = await Promise.all(
assetInteraction.selectedAssets.map(async (asset) => {
const updatedAsset = await getAssetInfo({ ...authManager.params, id: asset.id });
return toTimelineAsset(updatedAsset);
return toAsset(updatedAsset);
}),
);
@ -106,20 +106,15 @@
}
};
const hasGps = (asset: TimelineAsset) => {
const hasGps = (asset: Asset) => {
return !!asset.latitude && !!asset.longitude;
};
const handleThumbnailClick = (
asset: TimelineAsset,
asset: Asset,
timelineManager: TimelineManager,
dayGroup: DayGroup,
onClick: (
timelineManager: TimelineManager,
assets: TimelineAsset[],
groupTitle: string,
asset: TimelineAsset,
) => void,
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
) => {
if (hasGps(asset)) {
locationUpdated = true;
@ -195,7 +190,7 @@
withStacked
onThumbnailClick={handleThumbnailClick}
>
{#snippet customLayout(asset: TimelineAsset)}
{#snippet customLayout(asset: Asset)}
{#if hasGps(asset)}
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-success text-black">
{asset.city || $t('gps')}

View file

@ -1,4 +1,4 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import type { Asset } from '$lib/managers/timeline-manager/types';
import { fromISODateTimeUTCToObject, fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
import { faker } from '@faker-js/faker';
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
@ -30,7 +30,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
visibility: AssetVisibility.Timeline,
});
export const timelineAssetFactory = Sync.makeFactory<TimelineAsset>({
export const timelineAssetFactory = Sync.makeFactory<Asset>({
id: Sync.each(() => faker.string.uuid()),
ratio: Sync.each(() => faker.number.int()),
ownerId: Sync.each(() => faker.string.uuid()),
@ -51,7 +51,7 @@ export const timelineAssetFactory = Sync.makeFactory<TimelineAsset>({
people: [faker.person.fullName()],
});
export const toResponseDto = (...timelineAsset: TimelineAsset[]) => {
export const toResponseDto = (...timelineAsset: Asset[]) => {
const bucketAssets: TimeBucketAssetResponseDto = {
city: [],
country: [],