mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
refactor: rename TimelineAsset -> Asset
This commit is contained in:
parent
53680d9643
commit
c6e1170e6d
57 changed files with 260 additions and 281 deletions
|
|
@ -1,24 +1,24 @@
|
||||||
import type { AssetAction } from '$lib/constants';
|
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';
|
import type { AlbumResponseDto, AssetResponseDto, PersonResponseDto, StackResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
type ActionMap = {
|
type ActionMap = {
|
||||||
[AssetAction.ARCHIVE]: { asset: TimelineAsset };
|
[AssetAction.ARCHIVE]: { asset: Asset };
|
||||||
[AssetAction.UNARCHIVE]: { asset: TimelineAsset };
|
[AssetAction.UNARCHIVE]: { asset: Asset };
|
||||||
[AssetAction.FAVORITE]: { asset: TimelineAsset };
|
[AssetAction.FAVORITE]: { asset: Asset };
|
||||||
[AssetAction.UNFAVORITE]: { asset: TimelineAsset };
|
[AssetAction.UNFAVORITE]: { asset: Asset };
|
||||||
[AssetAction.TRASH]: { asset: TimelineAsset };
|
[AssetAction.TRASH]: { asset: Asset };
|
||||||
[AssetAction.DELETE]: { asset: TimelineAsset };
|
[AssetAction.DELETE]: { asset: Asset };
|
||||||
[AssetAction.RESTORE]: { asset: TimelineAsset };
|
[AssetAction.RESTORE]: { asset: Asset };
|
||||||
[AssetAction.ADD]: { asset: TimelineAsset };
|
[AssetAction.ADD]: { asset: Asset };
|
||||||
[AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto };
|
[AssetAction.ADD_TO_ALBUM]: { asset: Asset; album: AlbumResponseDto };
|
||||||
[AssetAction.STACK]: { stack: StackResponseDto };
|
[AssetAction.STACK]: { stack: StackResponseDto };
|
||||||
[AssetAction.UNSTACK]: { assets: TimelineAsset[] };
|
[AssetAction.UNSTACK]: { assets: Asset[] };
|
||||||
[AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset };
|
[AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: Asset };
|
||||||
[AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto };
|
[AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto };
|
||||||
[AssetAction.REMOVE_ASSET_FROM_STACK]: { stack: StackResponseDto | null; asset: AssetResponseDto };
|
[AssetAction.REMOVE_ASSET_FROM_STACK]: { stack: StackResponseDto | null; asset: AssetResponseDto };
|
||||||
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset };
|
[AssetAction.SET_VISIBILITY_LOCKED]: { asset: Asset };
|
||||||
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset };
|
[AssetAction.SET_VISIBILITY_TIMELINE]: { asset: Asset };
|
||||||
[AssetAction.SET_PERSON_FEATURED_PHOTO]: { asset: AssetResponseDto; person: PersonResponseDto };
|
[AssetAction.SET_PERSON_FEATURED_PHOTO]: { asset: AssetResponseDto; person: PersonResponseDto };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
|
||||||
import { addAssetsToAlbum, addAssetsToAlbums } from '$lib/utils/asset-utils';
|
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 type { AssetResponseDto } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js';
|
import { mdiImageAlbum, mdiShareVariantOutline } from '@mdi/js';
|
||||||
|
|
@ -29,13 +29,13 @@
|
||||||
if (albums.length === 1) {
|
if (albums.length === 1) {
|
||||||
const album = albums[0];
|
const album = albums[0];
|
||||||
await addAssetsToAlbum(album.id, [asset.id]);
|
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 {
|
} else {
|
||||||
await addAssetsToAlbums(
|
await addAssetsToAlbums(
|
||||||
albums.map((a) => a.id),
|
albums.map((a) => a.id),
|
||||||
[asset.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>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { toggleArchive } from '$lib/utils/asset-utils';
|
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 type { AssetResponseDto } from '@immich/sdk';
|
||||||
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline } from '@mdi/js';
|
import { mdiArchiveArrowDownOutline, mdiArchiveArrowUpOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -19,11 +19,11 @@
|
||||||
|
|
||||||
const onArchive = async () => {
|
const onArchive = async () => {
|
||||||
if (!asset.isArchived) {
|
if (!asset.isArchived) {
|
||||||
preAction({ type: AssetAction.ARCHIVE, asset: toTimelineAsset(asset) });
|
preAction({ type: AssetAction.ARCHIVE, asset: toAsset(asset) });
|
||||||
}
|
}
|
||||||
const updatedAsset = await toggleArchive(asset);
|
const updatedAsset = await toggleArchive(asset);
|
||||||
if (updatedAsset) {
|
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>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
import { showDeleteModal } from '$lib/stores/preferences.store';
|
import { showDeleteModal } from '$lib/stores/preferences.store';
|
||||||
import { featureFlags } from '$lib/stores/server-config.store';
|
import { featureFlags } from '$lib/stores/server-config.store';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
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 { deleteAssets, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiDeleteForeverOutline, mdiDeleteOutline } from '@mdi/js';
|
import { mdiDeleteForeverOutline, mdiDeleteOutline } from '@mdi/js';
|
||||||
|
|
@ -43,9 +43,9 @@
|
||||||
|
|
||||||
const trashAsset = async () => {
|
const trashAsset = async () => {
|
||||||
try {
|
try {
|
||||||
preAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
|
preAction({ type: AssetAction.TRASH, asset: toAsset(asset) });
|
||||||
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
|
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id] } });
|
||||||
onAction({ type: AssetAction.TRASH, asset: toTimelineAsset(asset) });
|
onAction({ type: AssetAction.TRASH, asset: toAsset(asset) });
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: $t('moved_to_trash'),
|
message: $t('moved_to_trash'),
|
||||||
|
|
@ -58,9 +58,9 @@
|
||||||
|
|
||||||
const deleteAsset = async () => {
|
const deleteAsset = async () => {
|
||||||
try {
|
try {
|
||||||
preAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
|
preAction({ type: AssetAction.DELETE, asset: toAsset(asset) });
|
||||||
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
|
await deleteAssets({ assetBulkDeleteDto: { ids: [asset.id], force: true } });
|
||||||
onAction({ type: AssetAction.DELETE, asset: toTimelineAsset(asset) });
|
onAction({ type: AssetAction.DELETE, asset: toAsset(asset) });
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
message: $t('permanently_deleted_asset'),
|
message: $t('permanently_deleted_asset'),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { shortcut } from '$lib/actions/shortcut';
|
import { shortcut } from '$lib/actions/shortcut';
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-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 { downloadFile } from '$lib/utils/asset-utils';
|
import { downloadFile } from '$lib/utils/asset-utils';
|
||||||
import { getAssetInfo } from '@immich/sdk';
|
import { getAssetInfo } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
menuItem?: boolean;
|
menuItem?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
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 { updateAsset, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiHeart, mdiHeartOutline } from '@mdi/js';
|
import { mdiHeart, mdiHeartOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { OnAction } from './action';
|
import type { OnAction } from './action';
|
||||||
import { IconButton } from '@immich/ui';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: AssetResponseDto;
|
asset: AssetResponseDto;
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
onAction({
|
onAction({
|
||||||
type: asset.isFavorite ? AssetAction.FAVORITE : AssetAction.UNFAVORITE,
|
type: asset.isFavorite ? AssetAction.FAVORITE : AssetAction.UNFAVORITE,
|
||||||
asset: toTimelineAsset(asset),
|
asset: toAsset(asset),
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { keepThisDeleteOthers } from '$lib/utils/asset-utils';
|
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 type { AssetResponseDto, StackResponseDto } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
import { mdiPinOutline } from '@mdi/js';
|
import { mdiPinOutline } from '@mdi/js';
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
const keptAsset = await keepThisDeleteOthers(asset, stack);
|
const keptAsset = await keepThisDeleteOthers(asset, stack);
|
||||||
if (keptAsset) {
|
if (keptAsset) {
|
||||||
onAction({ type: AssetAction.UNSTACK, assets: [toTimelineAsset(keptAsset)] });
|
onAction({ type: AssetAction.UNSTACK, assets: [toAsset(keptAsset)] });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
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 { restoreAssets, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { mdiHistory } from '@mdi/js';
|
import { mdiHistory } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
await restoreAssets({ bulkIdsDto: { ids: [asset.id] } });
|
await restoreAssets({ bulkIdsDto: { ids: [asset.id] } });
|
||||||
asset.isTrashed = false;
|
asset.isTrashed = false;
|
||||||
|
|
||||||
onAction({ type: AssetAction.RESTORE, asset: toTimelineAsset(asset) });
|
onAction({ type: AssetAction.RESTORE, asset: toAsset(asset) });
|
||||||
|
|
||||||
notificationController.show({
|
notificationController.show({
|
||||||
type: NotificationType.Info,
|
type: NotificationType.Info,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
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 { handleError } from '$lib/utils/handle-error';
|
||||||
import { AssetVisibility, updateAssets } from '@immich/sdk';
|
import { AssetVisibility, updateAssets } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
import type { OnAction, PreAction } from './action';
|
import type { OnAction, PreAction } from './action';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
onAction: OnAction;
|
onAction: OnAction;
|
||||||
preAction: PreAction;
|
preAction: PreAction;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import { deleteStack } from '$lib/utils/asset-utils';
|
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 type { StackResponseDto } from '@immich/sdk';
|
||||||
import { mdiImageOffOutline } from '@mdi/js';
|
import { mdiImageOffOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
const handleUnstack = async () => {
|
const handleUnstack = async () => {
|
||||||
const unstackedAssets = await deleteStack([stack.id]);
|
const unstackedAssets = await deleteStack([stack.id]);
|
||||||
if (unstackedAssets) {
|
if (unstackedAssets) {
|
||||||
onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets.map((asset) => toTimelineAsset(asset)) });
|
onAction({ type: AssetAction.UNSTACK, assets: unstackedAssets.map((asset) => toAsset(asset)) });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
import { getAssetJobName, getSharedLink } from '$lib/utils';
|
||||||
import { canCopyImageToClipboard } from '$lib/utils/asset-utils';
|
import { canCopyImageToClipboard } from '$lib/utils/asset-utils';
|
||||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toAsset } from '$lib/utils/timeline-util';
|
||||||
import {
|
import {
|
||||||
AssetJobName,
|
AssetJobName,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !isOwner && showDownloadButton}
|
{#if !isOwner && showDownloadButton}
|
||||||
<DownloadAction asset={toTimelineAsset(asset)} />
|
<DownloadAction asset={toAsset(asset)} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showDetailButton}
|
{#if showDetailButton}
|
||||||
|
|
@ -184,7 +184,7 @@
|
||||||
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
|
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if showDownloadButton}
|
{#if showDownloadButton}
|
||||||
<DownloadAction asset={toTimelineAsset(asset)} menuItem />
|
<DownloadAction asset={toAsset(asset)} menuItem />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !isLocked}
|
{#if !isLocked}
|
||||||
|
|
@ -243,7 +243,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !asset.isTrashed}
|
{#if !asset.isTrashed}
|
||||||
<SetVisibilityAction asset={toTimelineAsset(asset)} {onAction} {preAction} />
|
<SetVisibilityAction asset={toAsset(asset)} {onAction} {preAction} />
|
||||||
{/if}
|
{/if}
|
||||||
<hr />
|
<hr />
|
||||||
<MenuOption
|
<MenuOption
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
import { AssetAction, ProjectionType } from '$lib/constants';
|
import { AssetAction, ProjectionType } from '$lib/constants';
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-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 { closeEditorCofirm } from '$lib/stores/asset-editor.store';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { isShowDetail } from '$lib/stores/preferences.store';
|
import { isShowDetail } from '$lib/stores/preferences.store';
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
import { getAssetJobMessage, getSharedLink, handlePromiseError } from '$lib/utils';
|
import { getAssetJobMessage, getSharedLink, handlePromiseError } from '$lib/utils';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { SlideshowHistory } from '$lib/utils/slideshow-history';
|
import { SlideshowHistory } from '$lib/utils/slideshow-history';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toAsset } from '$lib/utils/timeline-util';
|
||||||
import {
|
import {
|
||||||
AssetJobName,
|
AssetJobName,
|
||||||
AssetTypeEnum,
|
AssetTypeEnum,
|
||||||
getAssetInfo,
|
|
||||||
getAllAlbums,
|
getAllAlbums,
|
||||||
|
getAssetInfo,
|
||||||
getStack,
|
getStack,
|
||||||
runAssetJobs,
|
runAssetJobs,
|
||||||
type AlbumResponseDto,
|
type AlbumResponseDto,
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: AssetResponseDto;
|
asset: AssetResponseDto;
|
||||||
preloadAssets?: TimelineAsset[];
|
preloadAssets?: Asset[];
|
||||||
showNavigation?: boolean;
|
showNavigation?: boolean;
|
||||||
withStacked?: boolean;
|
withStacked?: boolean;
|
||||||
isShared?: boolean;
|
isShared?: boolean;
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
|
|
||||||
untrack(() => {
|
untrack(() => {
|
||||||
if (stack && stack?.assets.length > 1) {
|
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) => {
|
slideshowStateUnsubscribe = slideshowState.subscribe((value) => {
|
||||||
if (value === SlideshowState.PlaySlideshow) {
|
if (value === SlideshowState.PlaySlideshow) {
|
||||||
slideshowHistory.reset();
|
slideshowHistory.reset();
|
||||||
slideshowHistory.queue(toTimelineAsset(asset));
|
slideshowHistory.queue(toAsset(asset));
|
||||||
handlePromiseError(handlePlaySlideshow());
|
handlePromiseError(handlePlaySlideshow());
|
||||||
} else if (value === SlideshowState.StopSlideshow) {
|
} else if (value === SlideshowState.StopSlideshow) {
|
||||||
handlePromiseError(handleStopSlideshow());
|
handlePromiseError(handleStopSlideshow());
|
||||||
|
|
@ -166,7 +166,7 @@
|
||||||
shuffleSlideshowUnsubscribe = slideshowNavigation.subscribe((value) => {
|
shuffleSlideshowUnsubscribe = slideshowNavigation.subscribe((value) => {
|
||||||
if (value === SlideshowNavigation.Shuffle) {
|
if (value === SlideshowNavigation.Shuffle) {
|
||||||
slideshowHistory.reset();
|
slideshowHistory.reset();
|
||||||
slideshowHistory.queue(toTimelineAsset(asset));
|
slideshowHistory.queue(toAsset(asset));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -571,7 +571,7 @@
|
||||||
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
|
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
|
||||||
brokenAssetClass="text-xs"
|
brokenAssetClass="text-xs"
|
||||||
dimmed={stackedAsset.id !== asset.id}
|
dimmed={stackedAsset.id !== asset.id}
|
||||||
asset={toTimelineAsset(stackedAsset)}
|
asset={toAsset(stackedAsset)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
asset = stackedAsset;
|
asset = stackedAsset;
|
||||||
previewStackedAsset = undefined;
|
previewStackedAsset = undefined;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
resetGlobalCropStore,
|
resetGlobalCropStore,
|
||||||
rotateDegrees,
|
rotateDegrees,
|
||||||
} from '$lib/stores/asset-editor.store';
|
} 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 type { AssetResponseDto } from '@immich/sdk';
|
||||||
import { animateCropChange, recalculateCrop } from './crop-settings';
|
import { animateCropChange, recalculateCrop } from './crop-settings';
|
||||||
import { cropAreaEl, cropFrame, imgElement, isResizingOrDragging, overlayEl, resetCropStore } from './crop-store';
|
import { cropAreaEl, cropFrame, imgElement, isResizingOrDragging, overlayEl, resetCropStore } from './crop-store';
|
||||||
|
|
@ -80,7 +80,7 @@
|
||||||
aria-label="Crop area"
|
aria-label="Crop area"
|
||||||
type="button"
|
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={`${$isResizingOrDragging ? 'resizing' : ''} crop-frame`} bind:this={$cropFrame}>
|
||||||
<div class="grid"></div>
|
<div class="grid"></div>
|
||||||
<div class="corner top-left"></div>
|
<div class="corner top-left"></div>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
|
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
|
||||||
import { assetViewerFadeDuration } from '$lib/constants';
|
import { assetViewerFadeDuration } from '$lib/constants';
|
||||||
import { castManager } from '$lib/managers/cast-manager.svelte';
|
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 { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
||||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
import { getBoundingBox } from '$lib/utils/people-utils';
|
import { getBoundingBox } from '$lib/utils/people-utils';
|
||||||
import { cancelImageUrl } from '$lib/utils/sw-messaging';
|
import { cancelImageUrl } from '$lib/utils/sw-messaging';
|
||||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
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 { AssetMediaSize, type AssetResponseDto, type SharedLinkResponseDto } from '@immich/sdk';
|
||||||
import { LoadingSpinner } from '@immich/ui';
|
import { LoadingSpinner } from '@immich/ui';
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: AssetResponseDto;
|
asset: AssetResponseDto;
|
||||||
preloadAssets?: TimelineAsset[] | undefined;
|
preloadAssets?: Asset[] | undefined;
|
||||||
element?: HTMLDivElement | undefined;
|
element?: HTMLDivElement | undefined;
|
||||||
haveFadeTransition?: boolean;
|
haveFadeTransition?: boolean;
|
||||||
sharedLink?: SharedLinkResponseDto | undefined;
|
sharedLink?: SharedLinkResponseDto | undefined;
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
$boundingBoxesArray = [];
|
$boundingBoxesArray = [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: TimelineAsset[]) => {
|
const preload = (targetSize: AssetMediaSize | 'original', preloadAssets?: Asset[]) => {
|
||||||
for (const preloadAsset of preloadAssets || []) {
|
for (const preloadAsset of preloadAssets || []) {
|
||||||
if (preloadAsset.isImage) {
|
if (preloadAsset.isImage) {
|
||||||
let img = new Image();
|
let img = new Image();
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
<img
|
<img
|
||||||
bind:this={$photoViewerImgElement}
|
bind:this={$photoViewerImgElement}
|
||||||
src={assetFileUrl}
|
src={assetFileUrl}
|
||||||
alt={$getAltText(toTimelineAsset(asset))}
|
alt={$getAltText(toAsset(asset))}
|
||||||
class="h-full w-full {$slideshowState === SlideshowState.None
|
class="h-full w-full {$slideshowState === SlideshowState.None
|
||||||
? 'object-contain'
|
? 'object-contain'
|
||||||
: slideshowLookCssMapping[$slideshowLook]}"
|
: slideshowLookCssMapping[$slideshowLook]}"
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import { thumbhash } from '$lib/actions/thumbhash';
|
import { thumbhash } from '$lib/actions/thumbhash';
|
||||||
import { authManager } from '$lib/managers/auth-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 { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||||
import { moveFocus } from '$lib/utils/focus-util';
|
import { moveFocus } from '$lib/utils/focus-util';
|
||||||
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
|
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
import VideoThumbnail from './video-thumbnail.svelte';
|
import VideoThumbnail from './video-thumbnail.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
groupIndex?: number;
|
groupIndex?: number;
|
||||||
thumbnailSize?: number;
|
thumbnailSize?: number;
|
||||||
thumbnailWidth?: number;
|
thumbnailWidth?: number;
|
||||||
|
|
@ -45,8 +45,8 @@
|
||||||
imageClass?: ClassValue;
|
imageClass?: ClassValue;
|
||||||
brokenAssetClass?: ClassValue;
|
brokenAssetClass?: ClassValue;
|
||||||
dimmed?: boolean;
|
dimmed?: boolean;
|
||||||
onClick?: (asset: TimelineAsset) => void;
|
onClick?: (asset: Asset) => void;
|
||||||
onSelect?: (asset: TimelineAsset) => void;
|
onSelect?: (asset: Asset) => void;
|
||||||
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
|
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { assetViewerFadeDuration } from '$lib/constants';
|
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 { getAssetThumbnailUrl } from '$lib/utils';
|
||||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||||
import { AssetMediaSize } from '@immich/sdk';
|
import { AssetMediaSize } from '@immich/sdk';
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
onImageLoad: () => void;
|
onImageLoad: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { assetViewerFadeDuration } from '$lib/constants';
|
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 { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||||
import { AssetMediaSize } from '@immich/sdk';
|
import { AssetMediaSize } from '@immich/sdk';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
videoPlayer: HTMLVideoElement | undefined;
|
videoPlayer: HTMLVideoElement | undefined;
|
||||||
videoViewerMuted?: boolean;
|
videoViewerMuted?: boolean;
|
||||||
videoViewerVolume?: number;
|
videoViewerVolume?: number;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
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 { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { type MemoryAsset, memoryStore } from '$lib/stores/memory.store.svelte';
|
import { type MemoryAsset, memoryStore } from '$lib/stores/memory.store.svelte';
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { preferences } from '$lib/stores/user.store';
|
||||||
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-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 { AssetMediaSize, getAssetInfo } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import {
|
import {
|
||||||
|
|
@ -71,7 +71,7 @@
|
||||||
let currentMemoryAssetFull = $derived.by(async () =>
|
let currentMemoryAssetFull = $derived.by(async () =>
|
||||||
current?.asset ? await getAssetInfo({ ...authManager.params, id: current.asset.id }) : undefined,
|
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 isSaved = $derived(current?.memory.isSaved);
|
||||||
let viewerHeight = $state(0);
|
let viewerHeight = $state(0);
|
||||||
|
|
@ -97,7 +97,7 @@
|
||||||
await goto(asHref(asset));
|
await goto(asHref(asset));
|
||||||
};
|
};
|
||||||
|
|
||||||
const setProgressDuration = (asset: TimelineAsset) => {
|
const setProgressDuration = (asset: Asset) => {
|
||||||
if (asset.isVideo) {
|
if (asset.isVideo) {
|
||||||
const timeParts = asset.duration!.split(':').map(Number);
|
const timeParts = asset.duration!.split(':').map(Number);
|
||||||
const durationInMilliseconds = (timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]) * 1000;
|
const durationInMilliseconds = (timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]) * 1000;
|
||||||
|
|
@ -116,8 +116,7 @@
|
||||||
const handleNextMemory = () => handleNavigate(current?.nextMemory?.assets[0]);
|
const handleNextMemory = () => handleNavigate(current?.nextMemory?.assets[0]);
|
||||||
const handlePreviousMemory = () => handleNavigate(current?.previousMemory?.assets[0]);
|
const handlePreviousMemory = () => handleNavigate(current?.previousMemory?.assets[0]);
|
||||||
const handleEscape = async () => goto(AppRoute.PHOTOS);
|
const handleEscape = async () => goto(AppRoute.PHOTOS);
|
||||||
const handleSelectAll = () =>
|
const handleSelectAll = () => assetInteraction.selectAssets(current?.memory.assets.map((a) => toAsset(a)) || []);
|
||||||
assetInteraction.selectAssets(current?.memory.assets.map((a) => toTimelineAsset(a)) || []);
|
|
||||||
|
|
||||||
const handleAction = async (callingContext: string, action: 'reset' | 'pause' | 'play') => {
|
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!
|
// leaving these log statements here as comments. Very useful to figure out what's going on during dev!
|
||||||
|
|
@ -658,7 +657,7 @@
|
||||||
<GalleryViewer
|
<GalleryViewer
|
||||||
onNext={handleNextAsset}
|
onNext={handleNextAsset}
|
||||||
onPrevious={handlePreviousAsset}
|
onPrevious={handlePreviousAsset}
|
||||||
assets={currentTimelineAssets}
|
assets={currentAssets}
|
||||||
viewport={galleryViewport}
|
viewport={galleryViewport}
|
||||||
{assetInteraction}
|
{assetInteraction}
|
||||||
slidingWindowOffset={viewerHeight}
|
slidingWindowOffset={viewerHeight}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import { memoryStore } from '$lib/stores/memory.store.svelte';
|
import { memoryStore } from '$lib/stores/memory.store.svelte';
|
||||||
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
|
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
|
||||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
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 { Icon } from '@immich/ui';
|
||||||
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
|
@ -89,7 +89,7 @@
|
||||||
<img
|
<img
|
||||||
class="h-full w-full rounded-xl object-cover"
|
class="h-full w-full rounded-xl object-cover"
|
||||||
src={getAssetThumbnailUrl(memory.assets[0].id)}
|
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"
|
draggable="false"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
import { cancelMultiselect, downloadArchive } from '$lib/utils/asset-utils';
|
import { cancelMultiselect, downloadArchive } from '$lib/utils/asset-utils';
|
||||||
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
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 { addSharedLinkAssets, getAssetInfo, type SharedLinkResponseDto } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiArrowLeft, mdiDownload, mdiFileImagePlusOutline, mdiSelectAll } from '@mdi/js';
|
import { mdiArrowLeft, mdiDownload, mdiFileImagePlusOutline, mdiSelectAll } from '@mdi/js';
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
const viewport: Viewport = $state({ width: 0, height: 0 });
|
const viewport: Viewport = $state({ width: 0, height: 0 });
|
||||||
const assetInteraction = new AssetInteraction();
|
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) => {
|
dragAndDropFilesStore.subscribe((value) => {
|
||||||
if (value.isDragging && value.files.length > 0) {
|
if (value.isDragging && value.files.length > 0) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
||||||
import { AppRoute, AssetAction } from '$lib/constants';
|
import { AppRoute, AssetAction } from '$lib/constants';
|
||||||
import Portal from '$lib/elements/Portal.svelte';
|
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 ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { getJustifiedLayoutFromAssets, type CommonJustifiedLayout } from '$lib/utils/layout-utils';
|
import { getJustifiedLayoutFromAssets, type CommonJustifiedLayout } from '$lib/utils/layout-utils';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
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 { AssetVisibility, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { modalManager } from '@immich/ui';
|
import { modalManager } from '@immich/ui';
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from 'lodash-es';
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
initialAssetId?: string;
|
initialAssetId?: string;
|
||||||
assets: (TimelineAsset | AssetResponseDto)[];
|
assets: (Asset | AssetResponseDto)[];
|
||||||
assetInteraction: AssetInteraction;
|
assetInteraction: AssetInteraction;
|
||||||
disableAssetSelect?: boolean;
|
disableAssetSelect?: boolean;
|
||||||
showArchiveIcon?: boolean;
|
showArchiveIcon?: boolean;
|
||||||
|
|
@ -130,7 +130,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let shiftKeyIsDown = $state(false);
|
let shiftKeyIsDown = $state(false);
|
||||||
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
|
let lastAssetMouseEvent: Asset | null = $state(null);
|
||||||
let slidingWindow = $state({ top: 0, bottom: 0 });
|
let slidingWindow = $state({ top: 0, bottom: 0 });
|
||||||
|
|
||||||
const updateSlidingWindow = () => {
|
const updateSlidingWindow = () => {
|
||||||
|
|
@ -157,14 +157,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const viewAssetHandler = async (asset: TimelineAsset) => {
|
const viewAssetHandler = async (asset: Asset) => {
|
||||||
currentIndex = assets.findIndex((a) => a.id == asset.id);
|
currentIndex = assets.findIndex((a) => a.id == asset.id);
|
||||||
await setAssetId(assets[currentIndex].id);
|
await setAssetId(assets[currentIndex].id);
|
||||||
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
|
await navigate({ targetRoute: 'current', assetId: $viewingAsset.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAllAssets = () => {
|
const selectAllAssets = () => {
|
||||||
assetInteraction.selectAssets(assets.map((a) => toTimelineAsset(a)));
|
assetInteraction.selectAssets(assets.map((a) => toAsset(a)));
|
||||||
};
|
};
|
||||||
|
|
||||||
const deselectAllAssets = () => {
|
const deselectAllAssets = () => {
|
||||||
|
|
@ -186,7 +186,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAssets = (asset: TimelineAsset) => {
|
const handleSelectAssets = (asset: Asset) => {
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -209,14 +209,14 @@
|
||||||
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
|
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
|
const handleSelectAssetCandidates = (asset: Asset | null) => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
selectAssetCandidates(asset);
|
selectAssetCandidates(asset);
|
||||||
}
|
}
|
||||||
lastAssetMouseEvent = asset;
|
lastAssetMouseEvent = asset;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAssetCandidates = (endAsset: TimelineAsset) => {
|
const selectAssetCandidates = (endAsset: Asset) => {
|
||||||
if (!shiftKeyIsDown) {
|
if (!shiftKeyIsDown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@
|
||||||
[start, end] = [end, start];
|
[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) => {
|
const onSelectStart = (event: Event) => {
|
||||||
|
|
@ -434,7 +434,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const assetMouseEventHandler = (asset: TimelineAsset | null) => {
|
const assetMouseEventHandler = (asset: Asset | null) => {
|
||||||
if (assetInteraction.selectionActive) {
|
if (assetInteraction.selectionActive) {
|
||||||
handleSelectAssetCandidates(asset);
|
handleSelectAssetCandidates(asset);
|
||||||
}
|
}
|
||||||
|
|
@ -496,21 +496,21 @@
|
||||||
readonly={disableAssetSelect}
|
readonly={disableAssetSelect}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (assetInteraction.selectionActive) {
|
if (assetInteraction.selectionActive) {
|
||||||
handleSelectAssets(toTimelineAsset(currentAsset));
|
handleSelectAssets(toAsset(currentAsset));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void viewAssetHandler(toTimelineAsset(currentAsset));
|
void viewAssetHandler(toAsset(currentAsset));
|
||||||
}}
|
}}
|
||||||
onSelect={() => handleSelectAssets(toTimelineAsset(currentAsset))}
|
onSelect={() => handleSelectAssets(toAsset(currentAsset))}
|
||||||
onMouseEvent={() => assetMouseEventHandler(toTimelineAsset(currentAsset))}
|
onMouseEvent={() => assetMouseEventHandler(toAsset(currentAsset))}
|
||||||
{showArchiveIcon}
|
{showArchiveIcon}
|
||||||
asset={toTimelineAsset(currentAsset)}
|
asset={toAsset(currentAsset)}
|
||||||
selected={assetInteraction.hasSelectedAsset(currentAsset.id)}
|
selected={assetInteraction.hasSelectedAsset(currentAsset.id)}
|
||||||
selectionCandidate={assetInteraction.hasSelectionCandidate(currentAsset.id)}
|
selectionCandidate={assetInteraction.hasSelectionCandidate(currentAsset.id)}
|
||||||
thumbnailWidth={layout.width}
|
thumbnailWidth={layout.width}
|
||||||
thumbnailHeight={layout.height}
|
thumbnailHeight={layout.height}
|
||||||
/>
|
/>
|
||||||
{#if showAssetName && !isTimelineAsset(currentAsset)}
|
{#if showAssetName && !isAsset(currentAsset)}
|
||||||
<div
|
<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"
|
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"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
export interface AssetControlContext {
|
export interface AssetControlContext {
|
||||||
// Wrap assets in a function, because context isn't reactive.
|
// Wrap assets in a function, because context isn't reactive.
|
||||||
getAssets: () => TimelineAsset[]; // All assets includes partners' assets
|
getAssets: () => Asset[]; // All assets includes partners' assets
|
||||||
getOwnedAssets: () => TimelineAsset[]; // Only assets owned by the user
|
getOwnedAssets: () => Asset[]; // Only assets owned by the user
|
||||||
clearSelect: () => void;
|
clearSelect: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<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 { mdiClose } from '@mdi/js';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
import ControlAppBar from '../shared-components/control-app-bar.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
assets: TimelineAsset[];
|
assets: Asset[];
|
||||||
clearSelect: () => void;
|
clearSelect: () => void;
|
||||||
ownerId?: string | undefined;
|
ownerId?: string | undefined;
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
import { resizeObserver, type OnResizeCallback } from '$lib/actions/resize-observer';
|
||||||
import Scrubber from '$lib/components/timeline/Scrubber.svelte';
|
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 TimelineKeyboardActions from '$lib/components/timeline/actions/TimelineKeyboardActions.svelte';
|
||||||
import { AssetAction } from '$lib/constants';
|
import { AssetAction } from '$lib/constants';
|
||||||
import HotModuleReload from '$lib/elements/HotModuleReload.svelte';
|
import HotModuleReload from '$lib/elements/HotModuleReload.svelte';
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||||
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
|
import type { MonthGroup } from '$lib/managers/timeline-manager/month-group.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
|
@ -48,21 +48,16 @@
|
||||||
album?: AlbumResponseDto | null;
|
album?: AlbumResponseDto | null;
|
||||||
person?: PersonResponseDto | null;
|
person?: PersonResponseDto | null;
|
||||||
isShowDeleteConfirmation?: boolean;
|
isShowDeleteConfirmation?: boolean;
|
||||||
onSelect?: (asset: TimelineAsset) => void;
|
onSelect?: (asset: Asset) => void;
|
||||||
onEscape?: () => void;
|
onEscape?: () => void;
|
||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
empty?: Snippet;
|
empty?: Snippet;
|
||||||
customLayout?: Snippet<[TimelineAsset]>;
|
customLayout?: Snippet<[Asset]>;
|
||||||
onThumbnailClick?: (
|
onThumbnailClick?: (
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
dayGroup: DayGroup,
|
dayGroup: DayGroup,
|
||||||
onClick: (
|
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
|
||||||
timelineManager: TimelineManager,
|
|
||||||
assets: TimelineAsset[],
|
|
||||||
groupTitle: string,
|
|
||||||
asset: TimelineAsset,
|
|
||||||
) => void,
|
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +177,7 @@
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollToAsset = (asset: TimelineAsset) => {
|
const scrollToAsset = (asset: Asset) => {
|
||||||
const monthGroup = timelineManager.getMonthGroupByAssetId(asset.id);
|
const monthGroup = timelineManager.getMonthGroupByAssetId(asset.id);
|
||||||
if (!monthGroup) {
|
if (!monthGroup) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -384,13 +379,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAsset = (asset: TimelineAsset) => {
|
const handleSelectAsset = (asset: Asset) => {
|
||||||
if (!timelineManager.albumAssets.has(asset.id)) {
|
if (!timelineManager.albumAssets.has(asset.id)) {
|
||||||
assetInteraction.selectAsset(asset);
|
assetInteraction.selectAsset(asset);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
|
let lastAssetMouseEvent: Asset | null = $state(null);
|
||||||
|
|
||||||
let shiftKeyIsDown = $state(false);
|
let shiftKeyIsDown = $state(false);
|
||||||
|
|
||||||
|
|
@ -407,14 +402,14 @@
|
||||||
shiftKeyIsDown = false;
|
shiftKeyIsDown = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
|
const handleSelectAssetCandidates = (asset: Asset | null) => {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
void selectAssetCandidates(asset);
|
void selectAssetCandidates(asset);
|
||||||
}
|
}
|
||||||
lastAssetMouseEvent = asset;
|
lastAssetMouseEvent = asset;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGroupSelect = (timelineManager: TimelineManager, group: string, assets: TimelineAsset[]) => {
|
const handleGroupSelect = (timelineManager: TimelineManager, group: string, assets: Asset[]) => {
|
||||||
if (assetInteraction.selectedGroup.has(group)) {
|
if (assetInteraction.selectedGroup.has(group)) {
|
||||||
assetInteraction.removeGroupFromMultiselectGroup(group);
|
assetInteraction.removeGroupFromMultiselectGroup(group);
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
|
|
@ -434,7 +429,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectAssets = async (asset: TimelineAsset) => {
|
const handleSelectAssets = async (asset: Asset) => {
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -518,7 +513,7 @@
|
||||||
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
|
assetInteraction.setAssetSelectionStart(deselect ? null : asset);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
|
const selectAssetCandidates = async (endAsset: Asset) => {
|
||||||
if (!shiftKeyIsDown) {
|
if (!shiftKeyIsDown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -686,7 +681,7 @@
|
||||||
|
|
||||||
<Portal target="body">
|
<Portal target="body">
|
||||||
{#if $showAssetViewer}
|
{#if $showAssetViewer}
|
||||||
<TimelineAssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
|
<AssetViewer bind:showSkeleton {timelineManager} {removeAction} {withStacked} {isShared} {album} {person} />
|
||||||
{/if}
|
{/if}
|
||||||
</Portal>
|
</Portal>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
|
import { updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions';
|
||||||
import { navigate } from '$lib/utils/navigation';
|
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';
|
import { getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
let { asset: viewingAsset, gridScrollTarget, mutex, preloadAssets } = assetViewingStore;
|
let { asset: viewingAsset, gridScrollTarget, mutex, preloadAssets } = assetViewingStore;
|
||||||
|
|
@ -135,12 +135,12 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetAction.REMOVE_ASSET_FROM_STACK: {
|
case AssetAction.REMOVE_ASSET_FROM_STACK: {
|
||||||
timelineManager.addAssets([toTimelineAsset(action.asset)]);
|
timelineManager.addAssets([toAsset(action.asset)]);
|
||||||
if (action.stack) {
|
if (action.stack) {
|
||||||
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
|
//Have to unstack then restack assets in timeline in order to update the stack count in the timeline.
|
||||||
updateUnstackedAssetInTimeline(
|
updateUnstackedAssetInTimeline(
|
||||||
timelineManager,
|
timelineManager,
|
||||||
action.stack.assets.map((asset) => toTimelineAsset(asset)),
|
action.stack.assets.map((asset) => toAsset(asset)),
|
||||||
);
|
);
|
||||||
updateStackedAssetInTimeline(timelineManager, {
|
updateStackedAssetInTimeline(timelineManager, {
|
||||||
stack: action.stack,
|
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.
|
//Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible.
|
||||||
updateUnstackedAssetInTimeline(
|
updateUnstackedAssetInTimeline(
|
||||||
timelineManager,
|
timelineManager,
|
||||||
action.stack.assets.map((asset) => toTimelineAsset(asset)),
|
action.stack.assets.map((asset) => toAsset(asset)),
|
||||||
);
|
);
|
||||||
updateStackedAssetInTimeline(timelineManager, {
|
updateStackedAssetInTimeline(timelineManager, {
|
||||||
stack: action.stack,
|
stack: action.stack,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||||
import type { MonthGroup } from '$lib/managers/timeline-manager/month-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 { 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 { assetSnapshot, assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
|
import { isSelectingAllAssets } from '$lib/stores/assets-store.svelte';
|
||||||
|
|
@ -28,22 +28,17 @@
|
||||||
monthGroup: MonthGroup;
|
monthGroup: MonthGroup;
|
||||||
timelineManager: TimelineManager;
|
timelineManager: TimelineManager;
|
||||||
assetInteraction: AssetInteraction;
|
assetInteraction: AssetInteraction;
|
||||||
customLayout?: Snippet<[TimelineAsset]>;
|
customLayout?: Snippet<[Asset]>;
|
||||||
|
|
||||||
onSelect: ({ title, assets }: { title: string; assets: TimelineAsset[] }) => void;
|
onSelect: ({ title, assets }: { title: string; assets: Asset[] }) => void;
|
||||||
onSelectAssets: (asset: TimelineAsset) => void;
|
onSelectAssets: (asset: Asset) => void;
|
||||||
onSelectAssetCandidates: (asset: TimelineAsset | null) => void;
|
onSelectAssetCandidates: (asset: Asset | null) => void;
|
||||||
onScrollCompensation: (compensation: { heightDelta?: number; scrollTop?: number }) => void;
|
onScrollCompensation: (compensation: { heightDelta?: number; scrollTop?: number }) => void;
|
||||||
onThumbnailClick?: (
|
onThumbnailClick?: (
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
dayGroup: DayGroup,
|
dayGroup: DayGroup,
|
||||||
onClick: (
|
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
|
||||||
timelineManager: TimelineManager,
|
|
||||||
assets: TimelineAsset[],
|
|
||||||
groupTitle: string,
|
|
||||||
asset: TimelineAsset,
|
|
||||||
) => void,
|
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,12 +65,7 @@
|
||||||
monthGroup.timelineManager.suspendTransitions && !$isUploading ? 0 : 150,
|
monthGroup.timelineManager.suspendTransitions && !$isUploading ? 0 : 150,
|
||||||
);
|
);
|
||||||
const scaleDuration = $derived(transitionDuration === 0 ? 0 : transitionDuration + 100);
|
const scaleDuration = $derived(transitionDuration === 0 ? 0 : transitionDuration + 100);
|
||||||
const _onClick = (
|
const _onClick = (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => {
|
||||||
timelineManager: TimelineManager,
|
|
||||||
assets: TimelineAsset[],
|
|
||||||
groupTitle: string,
|
|
||||||
asset: TimelineAsset,
|
|
||||||
) => {
|
|
||||||
if (isSelectionMode || assetInteraction.selectionActive) {
|
if (isSelectionMode || assetInteraction.selectionActive) {
|
||||||
assetSelectHandler(timelineManager, asset, assets, groupTitle);
|
assetSelectHandler(timelineManager, asset, assets, groupTitle);
|
||||||
return;
|
return;
|
||||||
|
|
@ -83,12 +73,12 @@
|
||||||
void navigate({ targetRoute: 'current', assetId: asset.id });
|
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 = (
|
const assetSelectHandler = (
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
assetsInDayGroup: TimelineAsset[],
|
assetsInDayGroup: Asset[],
|
||||||
groupTitle: string,
|
groupTitle: string,
|
||||||
) => {
|
) => {
|
||||||
onSelectAssets(asset);
|
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
|
// Show multi select icon on hover on date group
|
||||||
hoveredDayGroup = groupTitle;
|
hoveredDayGroup = groupTitle;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { authManager } from '$lib/managers/auth-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 type { OnLink, OnUnlink } from '$lib/utils/actions';
|
import type { OnLink, OnUnlink } from '$lib/utils/actions';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
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 { getAssetInfo, updateAsset } from '@immich/sdk';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiLinkOff, mdiMotionPlayOutline, mdiTimerSand } from '@mdi/js';
|
import { mdiLinkOff, mdiMotionPlayOutline, mdiTimerSand } from '@mdi/js';
|
||||||
|
|
@ -31,14 +31,14 @@
|
||||||
|
|
||||||
const handleLink = async () => {
|
const handleLink = async () => {
|
||||||
let [still, motion] = [...getOwnedAssets()];
|
let [still, motion] = [...getOwnedAssets()];
|
||||||
if ((still as TimelineAsset).isVideo) {
|
if ((still as Asset).isVideo) {
|
||||||
[still, motion] = [motion, still];
|
[still, motion] = [motion, still];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading = true;
|
loading = true;
|
||||||
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: motion.id } });
|
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();
|
clearSelect();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_link_motion_video'));
|
handleError(error, $t('errors.unable_to_link_motion_video'));
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
loading = true;
|
loading = true;
|
||||||
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: null } });
|
const stillResponse = await updateAsset({ id: still.id, updateAssetDto: { livePhotoVideoId: null } });
|
||||||
const motionResponse = await getAssetInfo({ ...authManager.params, id: motionId });
|
const motionResponse = await getAssetInfo({ ...authManager.params, id: motionId });
|
||||||
onUnlink({ still: toTimelineAsset(stillResponse), motion: toTimelineAsset(motionResponse) });
|
onUnlink({ still: toAsset(stillResponse), motion: toAsset(motionResponse) });
|
||||||
clearSelect();
|
clearSelect();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, $t('errors.unable_to_unlink_motion_video'));
|
handleError(error, $t('errors.unable_to_unlink_motion_video'));
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getAssetControlContext } from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
|
||||||
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.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 type { OnStack, OnUnstack } from '$lib/utils/actions';
|
||||||
import { deleteStack, stackAssets } from '$lib/utils/asset-utils';
|
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 { mdiImageMultipleOutline, mdiImageOffOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
}
|
}
|
||||||
const unstackedAssets = await deleteStack([stack.id]);
|
const unstackedAssets = await deleteStack([stack.id]);
|
||||||
if (unstackedAssets) {
|
if (unstackedAssets) {
|
||||||
onUnstack?.(unstackedAssets.map((a) => toTimelineAsset(a)));
|
onUnstack?.(unstackedAssets.map((a) => toAsset(a)));
|
||||||
}
|
}
|
||||||
clearSelect();
|
clearSelect();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
} from '$lib/components/timeline/actions/focus-actions';
|
} from '$lib/components/timeline/actions/focus-actions';
|
||||||
import { AppRoute } from '$lib/constants';
|
import { AppRoute } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
assetInteraction: AssetInteraction;
|
assetInteraction: AssetInteraction;
|
||||||
isShowDeleteConfirmation: boolean;
|
isShowDeleteConfirmation: boolean;
|
||||||
onEscape?: () => void;
|
onEscape?: () => void;
|
||||||
scrollToAsset: (asset: TimelineAsset) => boolean;
|
scrollToAsset: (asset: Asset) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 { moveFocus } from '$lib/utils/focus-util';
|
import { moveFocus } from '$lib/utils/focus-util';
|
||||||
import { InvocationTracker } from '$lib/utils/invocationTracker';
|
import { InvocationTracker } from '$lib/utils/invocationTracker';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
|
|
@ -21,7 +21,7 @@ export const focusPreviousAsset = () =>
|
||||||
|
|
||||||
const queryHTMLElement = (query: string) => document.querySelector(query) as HTMLElement;
|
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);
|
const scrolled = scrollToAsset(asset);
|
||||||
if (scrolled) {
|
if (scrolled) {
|
||||||
const element = queryHTMLElement(`[data-thumbnail-focus-container][data-asset="${asset.id}"]`);
|
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 (
|
export const setFocusTo = async (
|
||||||
scrollToAsset: (asset: TimelineAsset) => boolean,
|
scrollToAsset: (asset: Asset) => boolean,
|
||||||
store: TimelineManager,
|
store: TimelineManager,
|
||||||
direction: 'earlier' | 'later',
|
direction: 'earlier' | 'later',
|
||||||
interval: 'day' | 'month' | 'year' | 'asset',
|
interval: 'day' | 'month' | 'year' | 'asset',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||||
import { getAssetResolution, getFileSize } from '$lib/utils/asset-utils';
|
import { getAssetResolution, getFileSize } from '$lib/utils/asset-utils';
|
||||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
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 { type AssetResponseDto, getAllAlbums } from '@immich/sdk';
|
||||||
import { Icon } from '@immich/ui';
|
import { Icon } from '@immich/ui';
|
||||||
import { mdiHeart, mdiImageMultipleOutline, mdiMagnifyPlus } from '@mdi/js';
|
import { mdiHeart, mdiImageMultipleOutline, mdiMagnifyPlus } from '@mdi/js';
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
<!-- THUMBNAIL-->
|
<!-- THUMBNAIL-->
|
||||||
<img
|
<img
|
||||||
src={getAssetThumbnailUrl(asset.id)}
|
src={getAssetThumbnailUrl(asset.id)}
|
||||||
alt={$getAltText(toTimelineAsset(asset))}
|
alt={$getAltText(toAsset(asset))}
|
||||||
title={assetData}
|
title={assetData}
|
||||||
class="h-60 object-cover rounded-t-xl w-full"
|
class="h-60 object-cover rounded-t-xl w-full"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
import Thumbnail from '$lib/components/assets/thumbnail/thumbnail.svelte';
|
||||||
import { getFileSize } from '$lib/utils/asset-utils';
|
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';
|
import { type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
title={assetData}
|
title={assetData}
|
||||||
>
|
>
|
||||||
<div class="relative w-full h-full overflow-hidden rounded-lg">
|
<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}
|
{#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>
|
<div class="absolute bottom-1 end-3 px-4 py-1 rounded-xl text-xs transition-colors bg-red-500">External</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { plainDateTimeCompare } from '$lib/utils/timeline-util';
|
||||||
|
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import type { MonthGroup } from './month-group.svelte';
|
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';
|
import { ViewerAsset } from './viewer-asset.svelte';
|
||||||
|
|
||||||
export class DayGroup {
|
export class DayGroup {
|
||||||
|
|
@ -82,7 +82,7 @@ export class DayGroup {
|
||||||
return this.viewerAssets[0]?.asset;
|
return this.viewerAssets[0]?.asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
*assetsIterator(options: { startAsset?: TimelineAsset; direction?: Direction } = {}) {
|
*assetsIterator(options: { startAsset?: Asset; direction?: Direction } = {}) {
|
||||||
const isEarlier = (options?.direction ?? 'earlier') === 'earlier';
|
const isEarlier = (options?.direction ?? 'earlier') === 'earlier';
|
||||||
let assetIndex = options?.startAsset
|
let assetIndex = options?.startAsset
|
||||||
? this.viewerAssets.findIndex((viewerAsset) => viewerAsset.asset.id === options.startAsset!.id)
|
? this.viewerAssets.findIndex((viewerAsset) => viewerAsset.asset.id === options.startAsset!.id)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ import { AssetOrder } from '@immich/sdk';
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import type { DayGroup } from './day-group.svelte';
|
import type { DayGroup } from './day-group.svelte';
|
||||||
import type { MonthGroup } from './month-group.svelte';
|
import type { MonthGroup } from './month-group.svelte';
|
||||||
import type { TimelineAsset } from './types';
|
import type { Asset } from './types';
|
||||||
|
|
||||||
export class GroupInsertionCache {
|
export class GroupInsertionCache {
|
||||||
#lookupCache: {
|
#lookupCache: {
|
||||||
[year: number]: { [month: number]: { [day: number]: DayGroup } };
|
[year: number]: { [month: number]: { [day: number]: DayGroup } };
|
||||||
} = {};
|
} = {};
|
||||||
unprocessedAssets: TimelineAsset[] = [];
|
unprocessedAssets: Asset[] = [];
|
||||||
changedDayGroups = new SvelteSet<DayGroup>();
|
changedDayGroups = new SvelteSet<DayGroup>();
|
||||||
newDayGroups = new SvelteSet<DayGroup>();
|
newDayGroups = new SvelteSet<DayGroup>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { GroupInsertionCache } from '../group-insertion-cache.svelte';
|
import { GroupInsertionCache } from '../group-insertion-cache.svelte';
|
||||||
import { MonthGroup } from '../month-group.svelte';
|
import { MonthGroup } from '../month-group.svelte';
|
||||||
import type { TimelineManager } from '../timeline-manager.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 { updateGeometry } from './layout-support.svelte';
|
||||||
import { getMonthGroupByDate } from './search-support.svelte';
|
import { getMonthGroupByDate } from './search-support.svelte';
|
||||||
|
|
||||||
export function addAssetsToMonthGroups(
|
export function addAssetsToMonthGroups(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
assets: TimelineAsset[],
|
assets: Asset[],
|
||||||
options: { order: AssetOrder },
|
options: { order: AssetOrder },
|
||||||
) {
|
) {
|
||||||
if (assets.length === 0) {
|
if (assets.length === 0) {
|
||||||
|
|
@ -30,7 +30,7 @@ export function addAssetsToMonthGroups(
|
||||||
timelineManager.months.push(month);
|
timelineManager.months.push(month);
|
||||||
}
|
}
|
||||||
|
|
||||||
month.addTimelineAsset(asset, addContext);
|
month.addAsset(asset, addContext);
|
||||||
updatedMonthGroups.add(month);
|
updatedMonthGroups.add(month);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ export function runAssetOperation(
|
||||||
const changedMonthGroups = new SvelteSet<MonthGroup>();
|
const changedMonthGroups = new SvelteSet<MonthGroup>();
|
||||||
let idsToProcess = new SvelteSet(ids);
|
let idsToProcess = new SvelteSet(ids);
|
||||||
const idsProcessed = new SvelteSet<string>();
|
const idsProcessed = new SvelteSet<string>();
|
||||||
const combinedMoveAssets: { asset: TimelineAsset; date: TimelineDate }[][] = [];
|
const combinedMoveAssets: { asset: Asset; date: TimelineDate }[][] = [];
|
||||||
for (const month of timelineManager.months) {
|
for (const month of timelineManager.months) {
|
||||||
if (idsToProcess.size > 0) {
|
if (idsToProcess.size > 0) {
|
||||||
const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation);
|
const { moveAssets, processedIds, changedGeometry } = month.runAssetOperation(idsToProcess, operation);
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timelin
|
||||||
import { AssetOrder } from '@immich/sdk';
|
import { AssetOrder } from '@immich/sdk';
|
||||||
import type { MonthGroup } from '../month-group.svelte';
|
import type { MonthGroup } from '../month-group.svelte';
|
||||||
import type { TimelineManager } from '../timeline-manager.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(
|
export async function getAssetWithOffset(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<Asset | undefined> {
|
||||||
const { asset, monthGroup } = findMonthGroupForAsset(timelineManager, assetDescriptor.id) ?? {};
|
const { asset, monthGroup } = findMonthGroupForAsset(timelineManager, assetDescriptor.id) ?? {};
|
||||||
if (!monthGroup || !asset) {
|
if (!monthGroup || !asset) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -51,7 +51,7 @@ export function getMonthGroupByDate(
|
||||||
|
|
||||||
async function getAssetByAssetOffset(
|
async function getAssetByAssetOffset(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
monthGroup: MonthGroup,
|
monthGroup: MonthGroup,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) {
|
) {
|
||||||
|
|
@ -70,7 +70,7 @@ async function getAssetByAssetOffset(
|
||||||
|
|
||||||
async function getAssetByDayOffset(
|
async function getAssetByDayOffset(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
monthGroup: MonthGroup,
|
monthGroup: MonthGroup,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) {
|
) {
|
||||||
|
|
@ -120,7 +120,7 @@ export async function retrieveRange(timelineManager: TimelineManager, start: Ass
|
||||||
[startMonthGroup, endMonthGroup] = [endMonthGroup, startMonthGroup];
|
[startMonthGroup, endMonthGroup] = [endMonthGroup, startMonthGroup];
|
||||||
}
|
}
|
||||||
|
|
||||||
const range: TimelineAsset[] = [];
|
const range: Asset[] = [];
|
||||||
const startDayGroup = startMonthGroup.findDayGroupForAsset(startAsset);
|
const startDayGroup = startMonthGroup.findDayGroupForAsset(startAsset);
|
||||||
for await (const targetAsset of timelineManager.assetsIterator({
|
for await (const targetAsset of timelineManager.assetsIterator({
|
||||||
startMonthGroup,
|
startMonthGroup,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
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 { 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 { throttle } from 'lodash-es';
|
||||||
import type { Unsubscriber } from 'svelte/store';
|
import type { Unsubscriber } from 'svelte/store';
|
||||||
|
|
||||||
|
|
@ -31,11 +31,11 @@ export class WebsocketSupport {
|
||||||
connectWebsocketEvents() {
|
connectWebsocketEvents() {
|
||||||
this.#unsubscribers.push(
|
this.#unsubscribers.push(
|
||||||
websocketEvents.on('on_upload_success', (asset) =>
|
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_trash', (ids) => this.#addPendingChanges({ type: 'trash', values: ids })),
|
||||||
websocketEvents.on('on_asset_update', (asset) =>
|
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] })),
|
websocketEvents.on('on_asset_delete', (id: string) => this.#addPendingChanges({ type: 'delete', values: [id] })),
|
||||||
);
|
);
|
||||||
|
|
@ -55,8 +55,8 @@ export class WebsocketSupport {
|
||||||
|
|
||||||
#getPendingChangeBatches() {
|
#getPendingChangeBatches() {
|
||||||
const batch: {
|
const batch: {
|
||||||
add: TimelineAsset[];
|
add: Asset[];
|
||||||
update: TimelineAsset[];
|
update: Asset[];
|
||||||
remove: string[];
|
remove: string[];
|
||||||
} = {
|
} = {
|
||||||
add: [],
|
add: [],
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { DayGroup } from './day-group.svelte';
|
import { DayGroup } from './day-group.svelte';
|
||||||
import { GroupInsertionCache } from './group-insertion-cache.svelte';
|
import { GroupInsertionCache } from './group-insertion-cache.svelte';
|
||||||
import type { TimelineManager } from './timeline-manager.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';
|
import { ViewerAsset } from './viewer-asset.svelte';
|
||||||
|
|
||||||
export class MonthGroup {
|
export class MonthGroup {
|
||||||
|
|
@ -101,7 +101,7 @@ export class MonthGroup {
|
||||||
|
|
||||||
getAssets() {
|
getAssets() {
|
||||||
// eslint-disable-next-line unicorn/no-array-reduce
|
// 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() {
|
sortDayGroups() {
|
||||||
|
|
@ -161,7 +161,7 @@ export class MonthGroup {
|
||||||
bucketAssets.localOffsetHours[i],
|
bucketAssets.localOffsetHours[i],
|
||||||
);
|
);
|
||||||
|
|
||||||
const timelineAsset: TimelineAsset = {
|
const timelineAsset: Asset = {
|
||||||
city: bucketAssets.city[i],
|
city: bucketAssets.city[i],
|
||||||
country: bucketAssets.country[i],
|
country: bucketAssets.country[i],
|
||||||
duration: bucketAssets.duration[i],
|
duration: bucketAssets.duration[i],
|
||||||
|
|
@ -192,7 +192,7 @@ export class MonthGroup {
|
||||||
timelineAsset.latitude = bucketAssets.latitude?.[i];
|
timelineAsset.latitude = bucketAssets.latitude?.[i];
|
||||||
timelineAsset.longitude = bucketAssets.longitude?.[i];
|
timelineAsset.longitude = bucketAssets.longitude?.[i];
|
||||||
}
|
}
|
||||||
this.addTimelineAsset(timelineAsset, addContext);
|
this.addAsset(timelineAsset, addContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const group of addContext.existingDayGroups) {
|
for (const group of addContext.existingDayGroups) {
|
||||||
|
|
@ -208,7 +208,7 @@ export class MonthGroup {
|
||||||
return addContext.unprocessedAssets;
|
return addContext.unprocessedAssets;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTimelineAsset(timelineAsset: TimelineAsset, addContext: GroupInsertionCache) {
|
addAsset(timelineAsset: Asset, addContext: GroupInsertionCache) {
|
||||||
const { localDateTime } = timelineAsset;
|
const { localDateTime } = timelineAsset;
|
||||||
|
|
||||||
const { year, month } = this.yearMonth;
|
const { year, month } = this.yearMonth;
|
||||||
|
|
@ -294,7 +294,7 @@ export class MonthGroup {
|
||||||
handleError(error, _$t('errors.failed_to_load_assets'));
|
handleError(error, _$t('errors.failed_to_load_assets'));
|
||||||
}
|
}
|
||||||
|
|
||||||
findDayGroupForAsset(asset: TimelineAsset) {
|
findDayGroupForAsset(asset: Asset) {
|
||||||
for (const group of this.dayGroups) {
|
for (const group of this.dayGroups) {
|
||||||
if (group.viewerAssets.some((viewerAsset) => viewerAsset.id === asset.id)) {
|
if (group.viewerAssets.some((viewerAsset) => viewerAsset.id === asset.id)) {
|
||||||
return group;
|
return group;
|
||||||
|
|
@ -321,7 +321,7 @@ export class MonthGroup {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*assetsIterator(options?: { startDayGroup?: DayGroup; startAsset?: TimelineAsset; direction?: Direction }) {
|
*assetsIterator(options?: { startDayGroup?: DayGroup; startAsset?: Asset; direction?: Direction }) {
|
||||||
const direction = options?.direction ?? 'earlier';
|
const direction = options?.direction ?? 'earlier';
|
||||||
let { startAsset } = options ?? {};
|
let { startAsset } = options ?? {};
|
||||||
const isEarlier = direction === 'earlier';
|
const isEarlier = direction === 'earlier';
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { fromISODateTimeUTCToObject } from '$lib/utils/timeline-util';
|
||||||
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
import { type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
||||||
import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory';
|
import { timelineAssetFactory, toResponseDto } from '@test-data/factories/asset-factory';
|
||||||
import { TimelineManager } from './timeline-manager.svelte';
|
import { TimelineManager } from './timeline-manager.svelte';
|
||||||
import type { TimelineAsset } from './types';
|
import type { Asset } from './types';
|
||||||
|
|
||||||
async function getAssets(timelineManager: TimelineManager) {
|
async function getAssets(timelineManager: TimelineManager) {
|
||||||
const assets = [];
|
const assets = [];
|
||||||
|
|
@ -15,7 +15,7 @@ async function getAssets(timelineManager: TimelineManager) {
|
||||||
return assets;
|
return assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deriveLocalDateTimeFromFileCreatedAt(arg: TimelineAsset): TimelineAsset {
|
function deriveLocalDateTimeFromFileCreatedAt(arg: Asset): Asset {
|
||||||
return {
|
return {
|
||||||
...arg,
|
...arg,
|
||||||
localDateTime: arg.fileCreatedAt,
|
localDateTime: arg.fileCreatedAt,
|
||||||
|
|
@ -29,7 +29,7 @@ describe('TimelineManager', () => {
|
||||||
|
|
||||||
describe('init', () => {
|
describe('init', () => {
|
||||||
let timelineManager: TimelineManager;
|
let timelineManager: TimelineManager;
|
||||||
const bucketAssets: Record<string, TimelineAsset[]> = {
|
const bucketAssets: Record<string, Asset[]> = {
|
||||||
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
||||||
deriveLocalDateTimeFromFileCreatedAt({
|
deriveLocalDateTimeFromFileCreatedAt({
|
||||||
...asset,
|
...asset,
|
||||||
|
|
@ -94,7 +94,7 @@ describe('TimelineManager', () => {
|
||||||
|
|
||||||
describe('loadMonthGroup', () => {
|
describe('loadMonthGroup', () => {
|
||||||
let timelineManager: TimelineManager;
|
let timelineManager: TimelineManager;
|
||||||
const bucketAssets: Record<string, TimelineAsset[]> = {
|
const bucketAssets: Record<string, Asset[]> = {
|
||||||
'2024-01-03T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
'2024-01-03T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
||||||
deriveLocalDateTimeFromFileCreatedAt({
|
deriveLocalDateTimeFromFileCreatedAt({
|
||||||
...asset,
|
...asset,
|
||||||
|
|
@ -436,7 +436,7 @@ describe('TimelineManager', () => {
|
||||||
|
|
||||||
describe('getLaterAsset', () => {
|
describe('getLaterAsset', () => {
|
||||||
let timelineManager: TimelineManager;
|
let timelineManager: TimelineManager;
|
||||||
const bucketAssets: Record<string, TimelineAsset[]> = {
|
const bucketAssets: Record<string, Asset[]> = {
|
||||||
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
||||||
deriveLocalDateTimeFromFileCreatedAt({
|
deriveLocalDateTimeFromFileCreatedAt({
|
||||||
...asset,
|
...asset,
|
||||||
|
|
@ -583,7 +583,7 @@ describe('TimelineManager', () => {
|
||||||
|
|
||||||
describe('getRandomAsset', () => {
|
describe('getRandomAsset', () => {
|
||||||
let timelineManager: TimelineManager;
|
let timelineManager: TimelineManager;
|
||||||
const bucketAssets: Record<string, TimelineAsset[]> = {
|
const bucketAssets: Record<string, Asset[]> = {
|
||||||
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
'2024-03-01T00:00:00.000Z': timelineAssetFactory.buildList(1).map((asset) =>
|
||||||
deriveLocalDateTimeFromFileCreatedAt({
|
deriveLocalDateTimeFromFileCreatedAt({
|
||||||
...asset,
|
...asset,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
|
|
||||||
import { CancellableTask } from '$lib/utils/cancellable-task';
|
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 { clamp, debounce, isEqual } from 'lodash-es';
|
||||||
import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity';
|
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 { isMismatched, updateObject } from './internal/utils.svelte';
|
||||||
import { MonthGroup } from './month-group.svelte';
|
import { MonthGroup } from './month-group.svelte';
|
||||||
import type {
|
import type {
|
||||||
|
Asset,
|
||||||
AssetDescriptor,
|
AssetDescriptor,
|
||||||
AssetOperation,
|
AssetOperation,
|
||||||
Direction,
|
Direction,
|
||||||
ScrubberMonth,
|
ScrubberMonth,
|
||||||
TimelineAsset,
|
|
||||||
TimelineManagerLayoutOptions,
|
TimelineManagerLayoutOptions,
|
||||||
TimelineManagerOptions,
|
TimelineManagerOptions,
|
||||||
Viewport,
|
Viewport,
|
||||||
|
|
@ -192,7 +192,7 @@ export class TimelineManager {
|
||||||
async *assetsIterator(options?: {
|
async *assetsIterator(options?: {
|
||||||
startMonthGroup?: MonthGroup;
|
startMonthGroup?: MonthGroup;
|
||||||
startDayGroup?: DayGroup;
|
startDayGroup?: DayGroup;
|
||||||
startAsset?: TimelineAsset;
|
startAsset?: Asset;
|
||||||
direction?: Direction;
|
direction?: Direction;
|
||||||
}) {
|
}) {
|
||||||
const direction = options?.direction ?? 'earlier';
|
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 assetsToUpdate = assets.filter((asset) => !this.isExcluded(asset));
|
||||||
const notUpdated = this.updateAssets(assetsToUpdate);
|
const notUpdated = this.updateAssets(assetsToUpdate);
|
||||||
addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc });
|
addAssetsToMonthGroups(this, [...notUpdated], { order: this.#options.order ?? AssetOrder.Desc });
|
||||||
|
|
@ -430,7 +430,7 @@ export class TimelineManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const asset = toTimelineAsset(response);
|
const asset = toAsset(response);
|
||||||
if (!asset || this.isExcluded(asset)) {
|
if (!asset || this.isExcluded(asset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +454,7 @@ export class TimelineManager {
|
||||||
// note: the `index` input is expected to be in the range [0, assetCount). This
|
// 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
|
// value can be passed to make the method deterministic, which is mainly useful
|
||||||
// for testing.
|
// 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);
|
const randomAssetIndex = index ?? Math.floor(Math.random() * this.assetCount);
|
||||||
|
|
||||||
let accumulatedCount = 0;
|
let accumulatedCount = 0;
|
||||||
|
|
@ -493,8 +493,8 @@ export class TimelineManager {
|
||||||
runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc });
|
runAssetOperation(this, new SvelteSet(ids), operation, { order: this.#options.order ?? AssetOrder.Desc });
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAssets(assets: TimelineAsset[]) {
|
updateAssets(assets: Asset[]) {
|
||||||
const lookup = new SvelteMap<string, TimelineAsset>(assets.map((asset) => [asset.id, asset]));
|
const lookup = new SvelteMap<string, Asset>(assets.map((asset) => [asset.id, asset]));
|
||||||
const { unprocessedIds } = runAssetOperation(
|
const { unprocessedIds } = runAssetOperation(
|
||||||
this,
|
this,
|
||||||
new SvelteSet(lookup.keys()),
|
new SvelteSet(lookup.keys()),
|
||||||
|
|
@ -504,7 +504,7 @@ export class TimelineManager {
|
||||||
},
|
},
|
||||||
{ order: this.#options.order ?? AssetOrder.Desc },
|
{ order: this.#options.order ?? AssetOrder.Desc },
|
||||||
);
|
);
|
||||||
const result: TimelineAsset[] = [];
|
const result: Asset[] = [];
|
||||||
for (const id of unprocessedIds.values()) {
|
for (const id of unprocessedIds.values()) {
|
||||||
result.push(lookup.get(id)!);
|
result.push(lookup.get(id)!);
|
||||||
}
|
}
|
||||||
|
|
@ -530,21 +530,21 @@ export class TimelineManager {
|
||||||
this.updateIntersections();
|
this.updateIntersections();
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstAsset(): TimelineAsset | undefined {
|
getFirstAsset(): Asset | undefined {
|
||||||
return this.months[0]?.getFirstAsset();
|
return this.months[0]?.getFirstAsset();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLaterAsset(
|
async getLaterAsset(
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<Asset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEarlierAsset(
|
async getEarlierAsset(
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<Asset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -567,7 +567,7 @@ export class TimelineManager {
|
||||||
return retrieveRangeUtil(this, start, end);
|
return retrieveRangeUtil(this, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
isExcluded(asset: TimelineAsset) {
|
isExcluded(asset: Asset) {
|
||||||
return (
|
return (
|
||||||
isMismatched(this.#options.visibility, asset.visibility) ||
|
isMismatched(this.#options.visibility, asset.visibility) ||
|
||||||
isMismatched(this.#options.isFavorite, asset.isFavorite) ||
|
isMismatched(this.#options.isFavorite, asset.isFavorite) ||
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export type AssetDescriptor = { id: string };
|
||||||
|
|
||||||
export type Direction = 'earlier' | 'later';
|
export type Direction = 'earlier' | 'later';
|
||||||
|
|
||||||
export type TimelineAsset = {
|
export type Asset = {
|
||||||
id: string;
|
id: string;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
ratio: number;
|
ratio: number;
|
||||||
|
|
@ -35,9 +35,9 @@ export type TimelineAsset = {
|
||||||
longitude?: number | null;
|
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 {
|
export interface Viewport {
|
||||||
width: number;
|
width: number;
|
||||||
|
|
@ -51,12 +51,12 @@ export type ViewportXY = Viewport & {
|
||||||
|
|
||||||
export interface AddAsset {
|
export interface AddAsset {
|
||||||
type: 'add';
|
type: 'add';
|
||||||
values: TimelineAsset[];
|
values: Asset[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateAsset {
|
export interface UpdateAsset {
|
||||||
type: 'update';
|
type: 'update';
|
||||||
values: TimelineAsset[];
|
values: Asset[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteAsset {
|
export interface DeleteAsset {
|
||||||
|
|
|
||||||
|
|
@ -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 assetSnapshot = (asset: Asset): Asset => $state.snapshot(asset);
|
||||||
export const assetsSnapshot = (assets: TimelineAsset[]) => assets.map((asset) => $state.snapshot(asset));
|
export const assetsSnapshot = (assets: Asset[]) => assets.map((asset) => $state.snapshot(asset));
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import type { CommonPosition } from '$lib/utils/layout-utils';
|
||||||
|
|
||||||
import type { DayGroup } from './day-group.svelte';
|
import type { DayGroup } from './day-group.svelte';
|
||||||
import { calculateViewerAssetIntersecting } from './internal/intersection-support.svelte';
|
import { calculateViewerAssetIntersecting } from './internal/intersection-support.svelte';
|
||||||
import type { TimelineAsset } from './types';
|
import type { Asset } from './types';
|
||||||
|
|
||||||
export class ViewerAsset {
|
export class ViewerAsset {
|
||||||
readonly #group: DayGroup;
|
readonly #group: DayGroup;
|
||||||
|
|
@ -19,10 +19,10 @@ export class ViewerAsset {
|
||||||
});
|
});
|
||||||
|
|
||||||
position: CommonPosition | undefined = $state();
|
position: CommonPosition | undefined = $state();
|
||||||
asset: TimelineAsset = <TimelineAsset>$state();
|
asset: Asset = <Asset>$state();
|
||||||
id: string = $derived(this.asset.id);
|
id: string = $derived(this.asset.id);
|
||||||
|
|
||||||
constructor(group: DayGroup, asset: TimelineAsset) {
|
constructor(group: DayGroup, asset: Asset) {
|
||||||
this.#group = group;
|
this.#group = group;
|
||||||
this.asset = asset;
|
this.asset = asset;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 { user } from '$lib/stores/user.store';
|
||||||
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
|
import { AssetVisibility, type UserAdminResponseDto } from '@immich/sdk';
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { fromStore } from 'svelte/store';
|
import { fromStore } from 'svelte/store';
|
||||||
|
|
||||||
export class AssetInteraction {
|
export class AssetInteraction {
|
||||||
selectedAssets = $state<TimelineAsset[]>([]);
|
selectedAssets = $state<Asset[]>([]);
|
||||||
hasSelectedAsset(assetId: string) {
|
hasSelectedAsset(assetId: string) {
|
||||||
return this.selectedAssets.some((asset) => asset.id === assetId);
|
return this.selectedAssets.some((asset) => asset.id === assetId);
|
||||||
}
|
}
|
||||||
selectedGroup = new SvelteSet<string>();
|
selectedGroup = new SvelteSet<string>();
|
||||||
assetSelectionCandidates = $state<TimelineAsset[]>([]);
|
assetSelectionCandidates = $state<Asset[]>([]);
|
||||||
hasSelectionCandidate(assetId: string) {
|
hasSelectionCandidate(assetId: string) {
|
||||||
return this.assetSelectionCandidates.some((asset) => asset.id === assetId);
|
return this.assetSelectionCandidates.some((asset) => asset.id === assetId);
|
||||||
}
|
}
|
||||||
assetSelectionStart = $state<TimelineAsset | null>(null);
|
assetSelectionStart = $state<Asset | null>(null);
|
||||||
selectionActive = $derived(this.selectedAssets.length > 0);
|
selectionActive = $derived(this.selectedAssets.length > 0);
|
||||||
|
|
||||||
private user = fromStore<UserAdminResponseDto | undefined>(user);
|
private user = fromStore<UserAdminResponseDto | undefined>(user);
|
||||||
|
|
@ -25,13 +25,13 @@ export class AssetInteraction {
|
||||||
isAllFavorite = $derived(this.selectedAssets.every((asset) => asset.isFavorite));
|
isAllFavorite = $derived(this.selectedAssets.every((asset) => asset.isFavorite));
|
||||||
isAllUserOwned = $derived(this.selectedAssets.every((asset) => asset.ownerId === this.userId));
|
isAllUserOwned = $derived(this.selectedAssets.every((asset) => asset.ownerId === this.userId));
|
||||||
|
|
||||||
selectAsset(asset: TimelineAsset) {
|
selectAsset(asset: Asset) {
|
||||||
if (!this.hasSelectedAsset(asset.id)) {
|
if (!this.hasSelectedAsset(asset.id)) {
|
||||||
this.selectedAssets.push(asset);
|
this.selectedAssets.push(asset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAssets(assets: TimelineAsset[]) {
|
selectAssets(assets: Asset[]) {
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
this.selectAsset(asset);
|
this.selectAsset(asset);
|
||||||
}
|
}
|
||||||
|
|
@ -52,11 +52,11 @@ export class AssetInteraction {
|
||||||
this.selectedGroup.delete(group);
|
this.selectedGroup.delete(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAssetSelectionStart(asset: TimelineAsset | null) {
|
setAssetSelectionStart(asset: Asset | null) {
|
||||||
this.assetSelectionStart = asset;
|
this.assetSelectionStart = asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
setAssetSelectionCandidates(assets: TimelineAsset[]) {
|
setAssetSelectionCandidates(assets: Asset[]) {
|
||||||
this.assetSelectionCandidates = assets;
|
this.assetSelectionCandidates = assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { authManager } from '$lib/managers/auth-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 { type AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
import { type AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
||||||
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
import { getAssetInfo, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { Mutex } from 'async-mutex';
|
import { Mutex } from 'async-mutex';
|
||||||
|
|
@ -7,12 +7,12 @@ import { readonly, writable } from 'svelte/store';
|
||||||
|
|
||||||
function createAssetViewingStore() {
|
function createAssetViewingStore() {
|
||||||
const viewingAssetStoreState = writable<AssetResponseDto>();
|
const viewingAssetStoreState = writable<AssetResponseDto>();
|
||||||
const preloadAssets = writable<TimelineAsset[]>([]);
|
const preloadAssets = writable<Asset[]>([]);
|
||||||
const viewState = writable<boolean>(false);
|
const viewState = writable<boolean>(false);
|
||||||
const viewingAssetMutex = new Mutex();
|
const viewingAssetMutex = new Mutex();
|
||||||
const gridScrollTarget = writable<AssetGridRouteSearchParams | null | undefined>();
|
const gridScrollTarget = writable<AssetGridRouteSearchParams | null | undefined>();
|
||||||
|
|
||||||
const setAsset = (asset: AssetResponseDto, assetsToPreload: TimelineAsset[] = []) => {
|
const setAsset = (asset: AssetResponseDto, assetsToPreload: Asset[] = []) => {
|
||||||
preloadAssets.set(assetsToPreload);
|
preloadAssets.set(assetsToPreload);
|
||||||
viewingAssetStoreState.set(asset);
|
viewingAssetStoreState.set(asset);
|
||||||
viewState.set(true);
|
viewState.set(true);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
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 { 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 { deleteMemory, type MemoryResponseDto, removeMemoryAssets, searchMemories, updateMemory } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ type MemoryIndex = {
|
||||||
|
|
||||||
export type MemoryAsset = MemoryIndex & {
|
export type MemoryAsset = MemoryIndex & {
|
||||||
memory: MemoryResponseDto;
|
memory: MemoryResponseDto;
|
||||||
asset: TimelineAsset;
|
asset: Asset;
|
||||||
previousMemory?: MemoryResponseDto;
|
previousMemory?: MemoryResponseDto;
|
||||||
previous?: MemoryAsset;
|
previous?: MemoryAsset;
|
||||||
next?: MemoryAsset;
|
next?: MemoryAsset;
|
||||||
|
|
@ -36,7 +36,7 @@ class MemoryStoreSvelte {
|
||||||
memoryIndex,
|
memoryIndex,
|
||||||
previousMemory: this.memories[memoryIndex - 1],
|
previousMemory: this.memories[memoryIndex - 1],
|
||||||
nextMemory: this.memories[memoryIndex + 1],
|
nextMemory: this.memories[memoryIndex + 1],
|
||||||
asset: toTimelineAsset(asset),
|
asset: toAsset(asset),
|
||||||
assetIndex,
|
assetIndex,
|
||||||
previous,
|
previous,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 type { StackResponse } from '$lib/utils/asset-utils';
|
import type { StackResponse } from '$lib/utils/asset-utils';
|
||||||
import { AssetVisibility, deleteAssets as deleteBulk, restoreAssets } from '@immich/sdk';
|
import { AssetVisibility, deleteAssets as deleteBulk, restoreAssets } from '@immich/sdk';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -8,21 +8,21 @@ import { get } from 'svelte/store';
|
||||||
import { handleError } from './handle-error';
|
import { handleError } from './handle-error';
|
||||||
|
|
||||||
export type OnDelete = (assetIds: string[]) => void;
|
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 OnRestore = (ids: string[]) => void;
|
||||||
export type OnLink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
export type OnLink = (assets: { still: Asset; motion: Asset }) => void;
|
||||||
export type OnUnlink = (assets: { still: TimelineAsset; motion: TimelineAsset }) => void;
|
export type OnUnlink = (assets: { still: Asset; motion: Asset }) => void;
|
||||||
export type OnAddToAlbum = (ids: string[], albumId: string) => void;
|
export type OnAddToAlbum = (ids: string[], albumId: string) => void;
|
||||||
export type OnArchive = (ids: string[], visibility: AssetVisibility) => void;
|
export type OnArchive = (ids: string[], visibility: AssetVisibility) => void;
|
||||||
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
export type OnFavorite = (ids: string[], favorite: boolean) => void;
|
||||||
export type OnStack = (result: StackResponse) => 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 type OnSetVisibility = (ids: string[]) => void;
|
||||||
|
|
||||||
export const deleteAssets = async (
|
export const deleteAssets = async (
|
||||||
force: boolean,
|
force: boolean,
|
||||||
onAssetDelete: OnDelete,
|
onAssetDelete: OnDelete,
|
||||||
assets: TimelineAsset[],
|
assets: Asset[],
|
||||||
onUndoDelete: OnUndoDelete | undefined = undefined,
|
onUndoDelete: OnUndoDelete | undefined = undefined,
|
||||||
) => {
|
) => {
|
||||||
const $t = get(t);
|
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);
|
const $t = get(t);
|
||||||
try {
|
try {
|
||||||
const ids = assets.map((a) => a.id);
|
const ids = assets.map((a) => a.id);
|
||||||
|
|
@ -89,7 +89,7 @@ export function updateStackedAssetInTimeline(timelineManager: TimelineManager, {
|
||||||
* @param timelineManager - The timeline manager to update.
|
* @param timelineManager - The timeline manager to update.
|
||||||
* @param assets - The array of asset response DTOs to update in the timeline manager.
|
* @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(
|
timelineManager.updateAssetOperation(
|
||||||
assets.map((asset) => asset.id),
|
assets.map((asset) => asset.id),
|
||||||
(asset) => {
|
(asset) => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { AppRoute } from '$lib/constants';
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
import { downloadManager } from '$lib/managers/download-manager.svelte';
|
||||||
import type { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
|
||||||
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import type { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { isSelectingAllAssets } from '$lib/stores/assets-store.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 ids = [...assets].filter((a) => user && a.ownerId === user.id).map((a) => a.id);
|
||||||
|
|
||||||
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
const numberOfIssues = [...assets].filter((a) => user && a.ownerId !== user.id).length;
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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 { 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 { 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 type { AssetResponseDto } from '@immich/sdk';
|
||||||
import createJustifiedLayout from 'justified-layout';
|
import createJustifiedLayout from 'justified-layout';
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ export type CommonLayoutOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getJustifiedLayoutFromAssets(
|
export function getJustifiedLayoutFromAssets(
|
||||||
assets: (TimelineAsset | AssetResponseDto)[],
|
assets: (Asset | AssetResponseDto)[],
|
||||||
options: CommonLayoutOptions,
|
options: CommonLayoutOptions,
|
||||||
): CommonJustifiedLayout {
|
): CommonJustifiedLayout {
|
||||||
// if (useWasm) {
|
// 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 = {
|
const adapter = {
|
||||||
targetRowHeight: options.rowHeight,
|
targetRowHeight: options.rowHeight,
|
||||||
containerWidth: options.rowWidth,
|
containerWidth: options.rowWidth,
|
||||||
|
|
@ -100,7 +100,7 @@ export function justifiedLayout(assets: (TimelineAsset | AssetResponseDto)[], op
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = createJustifiedLayout(
|
const result = createJustifiedLayout(
|
||||||
assets.map((asset) => (isTimelineAsset(asset) ? asset.ratio : getAssetRatio(asset))),
|
assets.map((asset) => (isAsset(asset) ? asset.ratio : getAssetRatio(asset))),
|
||||||
adapter,
|
adapter,
|
||||||
);
|
);
|
||||||
return new Adapter(result);
|
return new Adapter(result);
|
||||||
|
|
|
||||||
|
|
@ -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 { getAltText } from '$lib/utils/thumbnail-util';
|
||||||
import { AssetVisibility } from '@immich/sdk';
|
import { AssetVisibility } from '@immich/sdk';
|
||||||
import { init, register, waitLocale } from 'svelte-i18n';
|
import { init, register, waitLocale } from 'svelte-i18n';
|
||||||
|
|
@ -57,7 +57,7 @@ describe('getAltText', () => {
|
||||||
expected: string;
|
expected: string;
|
||||||
}) => {
|
}) => {
|
||||||
const testDate = new Date('2024-01-01T12:00:00.000Z');
|
const testDate = new Date('2024-01-01T12:00:00.000Z');
|
||||||
const asset: TimelineAsset = {
|
const asset: Asset = {
|
||||||
id: 'test-id',
|
id: 'test-id',
|
||||||
ownerId: 'test-owner',
|
ownerId: 'test-owner',
|
||||||
ratio: 1,
|
ratio: 1,
|
||||||
|
|
|
||||||
|
|
@ -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 { locale } from '$lib/stores/preferences.store';
|
||||||
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
|
import { fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
@ -39,7 +39,7 @@ export function getThumbnailSize(assetCount: number, viewWidth: number): number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAltText = derived(t, ($t) => {
|
export const getAltText = derived(t, ($t) => {
|
||||||
return (asset: TimelineAsset) => {
|
return (asset: Asset) => {
|
||||||
const date = fromTimelinePlainDateTime(asset.localDateTime).toJSDate().toLocaleString(get(locale), {
|
const date = fromTimelinePlainDateTime(asset.localDateTime).toJSDate().toLocaleString(get(locale), {
|
||||||
dateStyle: 'long',
|
dateStyle: 'long',
|
||||||
timeZone: 'UTC',
|
timeZone: 'UTC',
|
||||||
|
|
|
||||||
|
|
@ -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 { locale } from '$lib/stores/preferences.store';
|
||||||
import { getAssetRatio } from '$lib/utils/asset-utils';
|
import { getAssetRatio } from '$lib/utils/asset-utils';
|
||||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -160,8 +160,8 @@ export const getDateTimeOffsetLocaleString = (date: DateTime, opts?: LocaleOptio
|
||||||
opts,
|
opts,
|
||||||
);
|
);
|
||||||
|
|
||||||
export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): TimelineAsset => {
|
export const toAsset = (unknownAsset: AssetResponseDto | Asset): Asset => {
|
||||||
if (isTimelineAsset(unknownAsset)) {
|
if (isAsset(unknownAsset)) {
|
||||||
return unknownAsset;
|
return unknownAsset;
|
||||||
}
|
}
|
||||||
const assetResponse = unknownAsset;
|
const assetResponse = unknownAsset;
|
||||||
|
|
@ -198,8 +198,8 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset =>
|
export const isAsset = (unknownAsset: AssetResponseDto | Asset): unknownAsset is Asset =>
|
||||||
(unknownAsset as TimelineAsset).ratio !== undefined;
|
(unknownAsset as Asset).ratio !== undefined;
|
||||||
|
|
||||||
export const plainDateTimeCompare = (ascending: boolean, a: TimelineDateTime, b: TimelineDateTime) => {
|
export const plainDateTimeCompare = (ascending: boolean, a: TimelineDateTime, b: TimelineDateTime) => {
|
||||||
const [aDateTime, bDateTime] = ascending ? [a, b] : [b, a];
|
const [aDateTime, bDateTime] = ascending ? [a, b] : [b, a];
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
|
import { AlbumPageViewMode, AppRoute } from '$lib/constants';
|
||||||
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
import { activityManager } from '$lib/managers/activity-manager.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 AlbumOptionsModal from '$lib/modals/AlbumOptionsModal.svelte';
|
||||||
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
|
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
|
||||||
import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte';
|
import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte';
|
||||||
|
|
@ -272,7 +272,7 @@
|
||||||
await refreshAlbum();
|
await refreshAlbum();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUndoRemoveAssets = async (assets: TimelineAsset[]) => {
|
const handleUndoRemoveAssets = async (assets: Asset[]) => {
|
||||||
timelineManager.addAssets(assets);
|
timelineManager.addAssets(assets);
|
||||||
await refreshAlbum();
|
await refreshAlbum();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
import { foldersStore } from '$lib/stores/folders.svelte';
|
import { foldersStore } from '$lib/stores/folders.svelte';
|
||||||
import { preferences } from '$lib/stores/user.store';
|
import { preferences } from '$lib/stores/user.store';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
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 { joinPaths } from '$lib/utils/tree-utils';
|
||||||
import { IconButton } from '@immich/ui';
|
import { IconButton } from '@immich/ui';
|
||||||
import { mdiDotsVertical, mdiFolder, mdiFolderHome, mdiFolderOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
|
import { mdiDotsVertical, mdiFolder, mdiFolderHome, mdiFolderOutline, mdiPlus, mdiSelectAll } from '@mdi/js';
|
||||||
|
|
@ -72,7 +72,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assetInteraction.selectAssets(data.pathAssets.map((asset) => toTimelineAsset(asset)));
|
assetInteraction.selectAssets(data.pathAssets.map((asset) => toAsset(asset)));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
import Timeline from '$lib/components/timeline/Timeline.svelte';
|
||||||
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
import { AppRoute, PersonPageViewMode, QueryParameter, SessionStorageKey } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 PersonEditBirthDateModal from '$lib/modals/PersonEditBirthDateModal.svelte';
|
import PersonEditBirthDateModal from '$lib/modals/PersonEditBirthDateModal.svelte';
|
||||||
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
|
import PersonMergeSuggestionModal from '$lib/modals/PersonMergeSuggestionModal.svelte';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
|
|
@ -204,7 +204,7 @@
|
||||||
data = { ...data, person };
|
data = { ...data, person };
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectFeaturePhoto = async (asset: TimelineAsset) => {
|
const handleSelectFeaturePhoto = async (asset: Asset) => {
|
||||||
if (viewMode !== PersonPageViewMode.SELECT_PERSON) {
|
if (viewMode !== PersonPageViewMode.SELECT_PERSON) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +354,7 @@
|
||||||
await updateAssetCount();
|
await updateAssetCount();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUndoDeleteAssets = async (assets: TimelineAsset[]) => {
|
const handleUndoDeleteAssets = async (assets: Asset[]) => {
|
||||||
timelineManager.addAssets(assets);
|
timelineManager.addAssets(assets);
|
||||||
await updateAssetCount();
|
await updateAssetCount();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
|
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 { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||||
import { lang, locale } from '$lib/stores/preferences.store';
|
import { lang, locale } from '$lib/stores/preferences.store';
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
import { parseUtcDate } from '$lib/utils/date-time';
|
import { parseUtcDate } from '$lib/utils/date-time';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
|
import { isAlbumsRoute, isPeopleRoute } from '$lib/utils/navigation';
|
||||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
import { toAsset } from '$lib/utils/timeline-util';
|
||||||
import {
|
import {
|
||||||
type AlbumResponseDto,
|
type AlbumResponseDto,
|
||||||
getPerson,
|
getPerson,
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
let nextPage = $state(1);
|
let nextPage = $state(1);
|
||||||
let searchResultAlbums: AlbumResponseDto[] = $state([]);
|
let searchResultAlbums: AlbumResponseDto[] = $state([]);
|
||||||
let searchResultAssets: TimelineAsset[] = $state([]);
|
let searchResultAssets: Asset[] = $state([]);
|
||||||
let isLoading = $state(true);
|
let isLoading = $state(true);
|
||||||
let scrollY = $state(0);
|
let scrollY = $state(0);
|
||||||
let scrollYHistory = 0;
|
let scrollYHistory = 0;
|
||||||
|
|
@ -125,7 +125,7 @@
|
||||||
|
|
||||||
const onAssetDelete = (assetIds: string[]) => {
|
const onAssetDelete = (assetIds: string[]) => {
|
||||||
const assetIdSet = new Set(assetIds);
|
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[]) => {
|
const handleSetVisibility = (assetIds: string[]) => {
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
: await searchAssets({ metadataSearchDto: searchDto });
|
: await searchAssets({ metadataSearchDto: searchDto });
|
||||||
|
|
||||||
searchResultAlbums.push(...albums.items);
|
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;
|
nextPage = Number(assets.nextPage) || 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
import type { DayGroup } from '$lib/managers/timeline-manager/day-group.svelte';
|
||||||
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-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 GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
|
import GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
|
||||||
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
|
||||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||||
import { setQueryValue } from '$lib/utils/navigation';
|
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 { AssetVisibility, getAssetInfo, updateAssets } from '@immich/sdk';
|
||||||
import { Button, LoadingSpinner, modalManager, Text } from '@immich/ui';
|
import { Button, LoadingSpinner, modalManager, Text } from '@immich/ui';
|
||||||
import { mdiMapMarkerMultipleOutline, mdiPencilOutline, mdiSelectRemove } from '@mdi/js';
|
import { mdiMapMarkerMultipleOutline, mdiPencilOutline, mdiSelectRemove } from '@mdi/js';
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
const updatedAssets = await Promise.all(
|
const updatedAssets = await Promise.all(
|
||||||
assetInteraction.selectedAssets.map(async (asset) => {
|
assetInteraction.selectedAssets.map(async (asset) => {
|
||||||
const updatedAsset = await getAssetInfo({ ...authManager.params, id: asset.id });
|
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;
|
return !!asset.latitude && !!asset.longitude;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleThumbnailClick = (
|
const handleThumbnailClick = (
|
||||||
asset: TimelineAsset,
|
asset: Asset,
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
dayGroup: DayGroup,
|
dayGroup: DayGroup,
|
||||||
onClick: (
|
onClick: (timelineManager: TimelineManager, assets: Asset[], groupTitle: string, asset: Asset) => void,
|
||||||
timelineManager: TimelineManager,
|
|
||||||
assets: TimelineAsset[],
|
|
||||||
groupTitle: string,
|
|
||||||
asset: TimelineAsset,
|
|
||||||
) => void,
|
|
||||||
) => {
|
) => {
|
||||||
if (hasGps(asset)) {
|
if (hasGps(asset)) {
|
||||||
locationUpdated = true;
|
locationUpdated = true;
|
||||||
|
|
@ -195,7 +190,7 @@
|
||||||
withStacked
|
withStacked
|
||||||
onThumbnailClick={handleThumbnailClick}
|
onThumbnailClick={handleThumbnailClick}
|
||||||
>
|
>
|
||||||
{#snippet customLayout(asset: TimelineAsset)}
|
{#snippet customLayout(asset: Asset)}
|
||||||
{#if hasGps(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">
|
<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')}
|
{asset.city || $t('gps')}
|
||||||
|
|
|
||||||
|
|
@ -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 { fromISODateTimeUTCToObject, fromTimelinePlainDateTime } from '$lib/utils/timeline-util';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
import { AssetTypeEnum, AssetVisibility, type AssetResponseDto, type TimeBucketAssetResponseDto } from '@immich/sdk';
|
||||||
|
|
@ -30,7 +30,7 @@ export const assetFactory = Sync.makeFactory<AssetResponseDto>({
|
||||||
visibility: AssetVisibility.Timeline,
|
visibility: AssetVisibility.Timeline,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const timelineAssetFactory = Sync.makeFactory<TimelineAsset>({
|
export const timelineAssetFactory = Sync.makeFactory<Asset>({
|
||||||
id: Sync.each(() => faker.string.uuid()),
|
id: Sync.each(() => faker.string.uuid()),
|
||||||
ratio: Sync.each(() => faker.number.int()),
|
ratio: Sync.each(() => faker.number.int()),
|
||||||
ownerId: Sync.each(() => faker.string.uuid()),
|
ownerId: Sync.each(() => faker.string.uuid()),
|
||||||
|
|
@ -51,7 +51,7 @@ export const timelineAssetFactory = Sync.makeFactory<TimelineAsset>({
|
||||||
people: [faker.person.fullName()],
|
people: [faker.person.fullName()],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const toResponseDto = (...timelineAsset: TimelineAsset[]) => {
|
export const toResponseDto = (...timelineAsset: Asset[]) => {
|
||||||
const bucketAssets: TimeBucketAssetResponseDto = {
|
const bucketAssets: TimeBucketAssetResponseDto = {
|
||||||
city: [],
|
city: [],
|
||||||
country: [],
|
country: [],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue