From b5c3a675b2087e6daff8a82dddcfb920c754ad3e Mon Sep 17 00:00:00 2001 From: Arno <46051866+arnolicious@users.noreply.github.com> Date: Mon, 2 Jun 2025 04:45:39 +0200 Subject: [PATCH] feat: upload assets to locked folder (#18806) * feat: upload assets to locked folder * chore: refactor params --- .../components/album-page/album-viewer.svelte | 2 +- .../individual-shared-viewer.svelte | 2 +- .../drag-and-drop-upload-overlay.svelte | 5 +- .../upload-asset-preview.svelte | 2 +- web/src/lib/utils/file-uploader.ts | 49 ++++++++++++++----- web/src/lib/utils/navigation.ts | 1 + 6 files changed, 43 insertions(+), 18 deletions(-) diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte index 0aa4e0ed3c..b540be2ed8 100644 --- a/web/src/lib/components/album-page/album-viewer.svelte +++ b/web/src/lib/components/album-page/album-viewer.svelte @@ -43,7 +43,7 @@ dragAndDropFilesStore.subscribe((value) => { if (value.isDragging && value.files.length > 0) { - handlePromiseError(fileUploadHandler(value.files, album.id)); + handlePromiseError(fileUploadHandler({ files: value.files, albumId: album.id })); dragAndDropFilesStore.set({ isDragging: false, files: [] }); } }); diff --git a/web/src/lib/components/share-page/individual-shared-viewer.svelte b/web/src/lib/components/share-page/individual-shared-viewer.svelte index 72927891e4..4fbbc6b6e3 100644 --- a/web/src/lib/components/share-page/individual-shared-viewer.svelte +++ b/web/src/lib/components/share-page/individual-shared-viewer.svelte @@ -52,7 +52,7 @@ let results: (string | undefined)[] = []; results = await (!files || files.length === 0 || !Array.isArray(files) ? openFileUploadDialog() - : fileUploadHandler(files)); + : fileUploadHandler({ files })); const data = await addSharedLinkAssets({ id: sharedLink.id, assetIdsDto: { diff --git a/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte b/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte index 4226b623cd..3af6a49d0b 100644 --- a/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte +++ b/web/src/lib/components/shared-components/drag-and-drop-upload-overlay.svelte @@ -4,12 +4,13 @@ import { authManager } from '$lib/managers/auth-manager.svelte'; import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store'; import { fileUploadHandler } from '$lib/utils/file-uploader'; - import { isAlbumsRoute } from '$lib/utils/navigation'; + import { isAlbumsRoute, isLockedFolderRoute } from '$lib/utils/navigation'; import { t } from 'svelte-i18n'; import { fade } from 'svelte/transition'; import ImmichLogo from './immich-logo.svelte'; let albumId = $derived(isAlbumsRoute(page.route?.id) ? page.params.albumId : undefined); + let isInLockedFolder = $derived(isLockedFolderRoute(page.route.id)); let dragStartTarget: EventTarget | null = $state(null); @@ -126,7 +127,7 @@ if (authManager.key) { dragAndDropFilesStore.set({ isDragging: true, files: filesArray }); } else { - await fileUploadHandler(filesArray, albumId); + await fileUploadHandler({ files: filesArray, albumId, isLockedAssets: isInLockedFolder }); } }; diff --git a/web/src/lib/components/shared-components/upload-asset-preview.svelte b/web/src/lib/components/shared-components/upload-asset-preview.svelte index 451874ca8b..cf0ccbe736 100644 --- a/web/src/lib/components/shared-components/upload-asset-preview.svelte +++ b/web/src/lib/components/shared-components/upload-asset-preview.svelte @@ -32,7 +32,7 @@ const handleRetry = async (uploadAsset: UploadAsset) => { uploadAssetsStore.removeItem(uploadAsset.id); - await fileUploadHandler([uploadAsset.file], uploadAsset.albumId); + await fileUploadHandler({ files: [uploadAsset.file], albumId: uploadAsset.albumId }); }; const asLink = (asset: UploadAsset) => { diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts index db43e92f94..c5dd3ebd8e 100644 --- a/web/src/lib/utils/file-uploader.ts +++ b/web/src/lib/utils/file-uploader.ts @@ -7,6 +7,7 @@ import { ExecutorQueue } from '$lib/utils/executor-queue'; import { Action, AssetMediaStatus, + AssetVisibility, checkBulkUpload, getAssetOriginalPath, getBaseUrl, @@ -73,7 +74,7 @@ export const openFileUploadDialog = async (options: FileUploadParam = {}) => { } const files = Array.from(target.files); - resolve(fileUploadHandler(files, albumId, assetId)); + resolve(fileUploadHandler({ files, albumId, replaceAssetId: assetId })); }); fileSelector.click(); @@ -84,11 +85,16 @@ export const openFileUploadDialog = async (options: FileUploadParam = {}) => { }); }; -export const fileUploadHandler = async ( - files: File[], - albumId?: string, - replaceAssetId?: string, -): Promise => { +type FileUploadHandlerParams = Omit & { + files: File[]; +}; + +export const fileUploadHandler = async ({ + files, + albumId, + replaceAssetId, + isLockedAssets = false, +}: FileUploadHandlerParams): Promise => { const extensions = await getExtensions(); const promises = []; for (const file of files) { @@ -96,7 +102,11 @@ export const fileUploadHandler = async ( if (extensions.some((extension) => name.endsWith(extension))) { const deviceAssetId = getDeviceAssetId(file); uploadAssetsStore.addItem({ id: deviceAssetId, file, albumId }); - promises.push(uploadExecutionQueue.addTask(() => fileUploader(file, deviceAssetId, albumId, replaceAssetId))); + promises.push( + uploadExecutionQueue.addTask(() => + fileUploader({ assetFile: file, deviceAssetId, albumId, replaceAssetId, isLockedAssets }), + ), + ); } } @@ -108,13 +118,22 @@ function getDeviceAssetId(asset: File) { return 'web' + '-' + asset.name + '-' + asset.lastModified; } +type FileUploaderParams = { + assetFile: File; + albumId?: string; + replaceAssetId?: string; + isLockedAssets?: boolean; + deviceAssetId: string; +}; + // TODO: should probably use the @api SDK -async function fileUploader( - assetFile: File, - deviceAssetId: string, - albumId?: string, - replaceAssetId?: string, -): Promise { +async function fileUploader({ + assetFile, + deviceAssetId, + albumId, + replaceAssetId, + isLockedAssets = false, +}: FileUploaderParams): Promise { const fileCreatedAt = new Date(assetFile.lastModified).toISOString(); const $t = get(t); @@ -134,6 +153,10 @@ async function fileUploader( formData.append(key, value); } + if (isLockedAssets) { + formData.append('visibility', AssetVisibility.Locked); + } + let responseData: { id: string; status: AssetMediaStatus; isTrashed?: boolean } | undefined; const key = authManager.key; if (crypto?.subtle?.digest && !key) { diff --git a/web/src/lib/utils/navigation.ts b/web/src/lib/utils/navigation.ts index 41eb5ee73b..c3b4d83f38 100644 --- a/web/src/lib/utils/navigation.ts +++ b/web/src/lib/utils/navigation.ts @@ -17,6 +17,7 @@ export const isSharedLinkRoute = (route?: string | null) => !!route?.startsWith( export const isSearchRoute = (route?: string | null) => !!route?.startsWith('/(user)/search'); export const isAlbumsRoute = (route?: string | null) => !!route?.startsWith('/(user)/albums/[albumId=id]'); export const isPeopleRoute = (route?: string | null) => !!route?.startsWith('/(user)/people/[personId]'); +export const isLockedFolderRoute = (route?: string | null) => !!route?.startsWith('/(user)/locked'); export const isAssetViewerRoute = (target?: NavigationTarget | null) => !!(target?.route.id?.endsWith('/[[assetId=id]]') && 'assetId' in (target?.params || {}));