diff --git a/i18n/en.json b/i18n/en.json index 3bb0ce1ad8..3a887782f4 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1631,6 +1631,7 @@ "set_date_of_birth": "Set date of birth", "set_profile_picture": "Set profile picture", "set_slideshow_to_fullscreen": "Set Slideshow to fullscreen", + "set_stack_primary_asset": "Set as primary asset", "setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).", "setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).", "setting_image_viewer_original_title": "Load original image", diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index fd37d253aa..d823f17df4 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -1,6 +1,6 @@ import type { AssetAction } from '$lib/constants'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; -import type { AlbumResponseDto } from '@immich/sdk'; +import type { AlbumResponseDto, StackResponseDto } from '@immich/sdk'; type ActionMap = { [AssetAction.ARCHIVE]: { asset: TimelineAsset }; @@ -14,6 +14,7 @@ type ActionMap = { [AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto }; [AssetAction.UNSTACK]: { assets: TimelineAsset[] }; [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset }; + [AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto }; [AssetAction.SET_VISIBILITY_LOCKED]: { asset: TimelineAsset }; [AssetAction.SET_VISIBILITY_TIMELINE]: { asset: TimelineAsset }; }; diff --git a/web/src/lib/components/asset-viewer/actions/set-stack-primary-asset.svelte b/web/src/lib/components/asset-viewer/actions/set-stack-primary-asset.svelte new file mode 100644 index 0000000000..6ca1548f4b --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/set-stack-primary-asset.svelte @@ -0,0 +1,26 @@ + + + diff --git a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte index e562f60319..a376c37139 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer-nav-bar.svelte @@ -13,6 +13,7 @@ import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/set-album-cover-action.svelte'; import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/set-person-featured-action.svelte'; import SetProfilePictureAction from '$lib/components/asset-viewer/actions/set-profile-picture-action.svelte'; + import SetStackPrimaryAsset from '$lib/components/asset-viewer/actions/set-stack-primary-asset.svelte'; import SetVisibilityAction from '$lib/components/asset-viewer/actions/set-visibility-action.svelte'; import ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte'; import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte'; @@ -192,6 +193,9 @@ {#if stack} + {#if stack?.primaryAssetId !== asset.id} + + {/if} {/if} {#if album} diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index e5d2554e2c..a448f96c32 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -338,7 +338,10 @@ await handleGetAllAlbums(); break; } - + case AssetAction.SET_STACK_PRIMARY_ASSET: { + stack = action.stack; + break; + } case AssetAction.KEEP_THIS_DELETE_OTHERS: case AssetAction.UNSTACK: { closeViewer(); diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte index dec0c0096c..1b96353d37 100644 --- a/web/src/lib/components/photos-page/asset-grid.svelte +++ b/web/src/lib/components/photos-page/asset-grid.svelte @@ -31,7 +31,7 @@ import { deleteAssets, updateStackedAssetInTimeline, updateUnstackedAssetInTimeline } from '$lib/utils/actions'; import { archiveAssets, cancelMultiselect, selectAllAssets, stackAssets } from '$lib/utils/asset-utils'; import { navigate } from '$lib/utils/navigation'; - import { type ScrubberListener, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; + import { toTimelineAsset, type ScrubberListener, type TimelinePlainYearMonth } from '$lib/utils/timeline-util'; import { AssetVisibility, getAssetInfo, type AlbumResponseDto, type PersonResponseDto } from '@immich/sdk'; import { DateTime } from 'luxon'; import { onMount, type Snippet } from 'svelte'; @@ -511,6 +511,20 @@ updateUnstackedAssetInTimeline(assetStore, action.assets); break; } + case AssetAction.SET_STACK_PRIMARY_ASSET: { + //Have to unstack then restack assets in timeline in order for the currently removed new primary asset to be made visible. + updateUnstackedAssetInTimeline( + assetStore, + action.stack.assets.map((asset) => toTimelineAsset(asset)), + ); + updateStackedAssetInTimeline(assetStore, { + stack: action.stack, + toDeleteIds: action.stack.assets + .filter((asset) => asset.id !== action.stack.primaryAssetId) + .map((asset) => asset.id), + }); + break; + } } }; diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index ae53b4e7f3..c27727d4b3 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -10,6 +10,7 @@ export enum AssetAction { ADD_TO_ALBUM = 'add-to-album', UNSTACK = 'unstack', KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', + SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset', SET_VISIBILITY_LOCKED = 'set-visibility-locked', SET_VISIBILITY_TIMELINE = 'set-visibility-timeline', }