mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 02:27:08 -04:00 
			
		
		
		
	feat(web): Add upload to stack action (#19842)
* feat(web): Add upload to stack action * Event handling and translation * Update asset viewer instead * lint, improve upload return type * Add suggestions from code review * Resolve merge conflicts * Apply suggestions from code review
This commit is contained in:
		
							parent
							
								
									d764a59011
								
							
						
					
					
						commit
						cf60f4cdcd
					
				| @ -33,6 +33,7 @@ | ||||
|   "add_to_albums": "Add to albums", | ||||
|   "add_to_albums_count": "Add to albums ({count})", | ||||
|   "add_to_shared_album": "Add to shared album", | ||||
|   "add_upload_to_stack": "Add upload to stack", | ||||
|   "add_url": "Add URL", | ||||
|   "added_to_archive": "Added to archive", | ||||
|   "added_to_favorites": "Added to favorites", | ||||
|  | ||||
| @ -12,6 +12,7 @@ type ActionMap = { | ||||
|   [AssetAction.RESTORE]: { asset: TimelineAsset }; | ||||
|   [AssetAction.ADD]: { asset: TimelineAsset }; | ||||
|   [AssetAction.ADD_TO_ALBUM]: { asset: TimelineAsset; album: AlbumResponseDto }; | ||||
|   [AssetAction.STACK]: { stack: StackResponseDto }; | ||||
|   [AssetAction.UNSTACK]: { assets: TimelineAsset[] }; | ||||
|   [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: TimelineAsset }; | ||||
|   [AssetAction.SET_STACK_PRIMARY_ASSET]: { stack: StackResponseDto }; | ||||
|  | ||||
| @ -0,0 +1,37 @@ | ||||
| <script lang="ts"> | ||||
|   import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; | ||||
|   import { AssetAction } from '$lib/constants'; | ||||
|   import { openFileUploadDialog } from '$lib/utils/file-uploader'; | ||||
|   import { createStack, type AssetResponseDto, type StackResponseDto } from '@immich/sdk'; | ||||
|   import { mdiUploadMultiple } from '@mdi/js'; | ||||
|   import { t } from 'svelte-i18n'; | ||||
|   import type { OnAction } from './action'; | ||||
| 
 | ||||
|   interface Props { | ||||
|     asset: AssetResponseDto; | ||||
|     stack: StackResponseDto | null; | ||||
|     onAction: OnAction; | ||||
|   } | ||||
| 
 | ||||
|   let { asset, stack, onAction }: Props = $props(); | ||||
| 
 | ||||
|   const handleAddUploadToStack = async () => { | ||||
|     const newAssetIds = await openFileUploadDialog({ multiple: true }); | ||||
|     // Including the old stacks primary asset ID ensures that all assets of the | ||||
|     // old stack are automatically included in the new stack. | ||||
|     const primaryAssetId = stack?.primaryAssetId ?? asset.id; | ||||
| 
 | ||||
|     // First asset in the list will become the new primary asset. | ||||
|     const assetIds = [primaryAssetId, ...newAssetIds]; | ||||
| 
 | ||||
|     const newStack = await createStack({ | ||||
|       stackCreateDto: { | ||||
|         assetIds, | ||||
|       }, | ||||
|     }); | ||||
| 
 | ||||
|     onAction({ type: AssetAction.STACK, stack: newStack }); | ||||
|   }; | ||||
| </script> | ||||
| 
 | ||||
| <MenuOption icon={mdiUploadMultiple} onClick={handleAddUploadToStack} text={$t('add_upload_to_stack')} /> | ||||
| @ -4,6 +4,7 @@ | ||||
|   import CastButton from '$lib/cast/cast-button.svelte'; | ||||
|   import type { OnAction, PreAction } from '$lib/components/asset-viewer/actions/action'; | ||||
|   import AddToAlbumAction from '$lib/components/asset-viewer/actions/add-to-album-action.svelte'; | ||||
|   import AddToStackAction from '$lib/components/asset-viewer/actions/add-to-stack-action.svelte'; | ||||
|   import ArchiveAction from '$lib/components/asset-viewer/actions/archive-action.svelte'; | ||||
|   import CloseAction from '$lib/components/asset-viewer/actions/close-action.svelte'; | ||||
|   import DeleteAction from '$lib/components/asset-viewer/actions/delete-action.svelte'; | ||||
| @ -196,6 +197,7 @@ | ||||
|         {/if} | ||||
| 
 | ||||
|         {#if isOwner} | ||||
|           <AddToStackAction {asset} {stack} {onAction} /> | ||||
|           {#if stack} | ||||
|             <UnstackAction {stack} {onAction} /> | ||||
|             <KeepThisDeleteOthersAction {stack} {asset} {onAction} /> | ||||
|  | ||||
| @ -336,6 +336,7 @@ | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       case AssetAction.STACK: | ||||
|       case AssetAction.SET_STACK_PRIMARY_ASSET: { | ||||
|         stack = action.stack; | ||||
|         break; | ||||
|  | ||||
| @ -120,6 +120,16 @@ | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|       case AssetAction.STACK: { | ||||
|         updateStackedAssetInTimeline(timelineManager, { | ||||
|           stack: action.stack, | ||||
|           toDeleteIds: action.stack.assets | ||||
|             .filter((asset) => asset.id !== action.stack.primaryAssetId) | ||||
|             .map((asset) => asset.id), | ||||
|         }); | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|       case AssetAction.UNSTACK: { | ||||
|         updateUnstackedAssetInTimeline(timelineManager, action.assets); | ||||
|         break; | ||||
|  | ||||
| @ -8,6 +8,7 @@ export enum AssetAction { | ||||
|   RESTORE = 'restore', | ||||
|   ADD = 'add', | ||||
|   ADD_TO_ALBUM = 'add-to-album', | ||||
|   STACK = 'stack', | ||||
|   UNSTACK = 'unstack', | ||||
|   KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', | ||||
|   SET_STACK_PRIMARY_ASSET = 'set-stack-primary-asset', | ||||
|  | ||||
| @ -52,7 +52,7 @@ export const openFileUploadDialog = async (options: FileUploadParam = {}) => { | ||||
|   const { albumId, multiple = true, assetId } = options; | ||||
|   const extensions = uploadManager.getExtensions(); | ||||
| 
 | ||||
|   return new Promise<(string | undefined)[]>((resolve, reject) => { | ||||
|   return new Promise<string[]>((resolve, reject) => { | ||||
|     try { | ||||
|       const fileSelector = document.createElement('input'); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user