From 83ca9bc9a7952cfe1e18031600cc6429b3762d62 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 12:29:14 -0600 Subject: [PATCH 1/9] Implement keep this delete others for asset stacks --- i18n/en.json | 2 ++ .../components/asset-viewer/actions/action.ts | 1 + .../actions/keep-this-delete-others.svelte | 22 +++++++++++++++ .../asset-viewer/asset-viewer-nav-bar.svelte | 2 ++ .../asset-viewer/asset-viewer.svelte | 1 + web/src/lib/constants.ts | 1 + web/src/lib/utils/asset-utils.ts | 27 +++++++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte diff --git a/i18n/en.json b/i18n/en.json index 7a1ada94a65c4..12b286cf47062 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -607,6 +607,7 @@ "failed_to_remove_product_key": "Failed to remove product key", "failed_to_stack_assets": "Failed to stack assets", "failed_to_unstack_assets": "Failed to un-stack assets", + "failed_to_keep_this_delete_others": "Failed to keep this asset and delete the other assets", "import_path_already_exists": "This import path already exists.", "incorrect_email_or_password": "Incorrect email or password", "paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation", @@ -1252,6 +1253,7 @@ "unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}", "untracked_files": "Untracked files", "untracked_files_decription": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug", + "kept_this_deleted_others": "Kept this asset and deleted {count, plural, one {# asset} other {# assets}}", "up_next": "Up next", "updated_password": "Updated password", "upload": "Upload", diff --git a/web/src/lib/components/asset-viewer/actions/action.ts b/web/src/lib/components/asset-viewer/actions/action.ts index d6136f2d1867e..f8cfd447f0b94 100644 --- a/web/src/lib/components/asset-viewer/actions/action.ts +++ b/web/src/lib/components/asset-viewer/actions/action.ts @@ -12,6 +12,7 @@ type ActionMap = { [AssetAction.ADD]: { asset: AssetResponseDto }; [AssetAction.ADD_TO_ALBUM]: { asset: AssetResponseDto; album: AlbumResponseDto }; [AssetAction.UNSTACK]: { assets: AssetResponseDto[] }; + [AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: AssetResponseDto }; }; export type Action = { diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte new file mode 100644 index 0000000000000..c6a1f4df4fa87 --- /dev/null +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -0,0 +1,22 @@ + + + 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 8ef4e861154f0..0192a08abd194 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 ShareAction from '$lib/components/asset-viewer/actions/share-action.svelte'; import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte'; import UnstackAction from '$lib/components/asset-viewer/actions/unstack-action.svelte'; + import KeepThisDeleteOthersAction from '$lib/components/asset-viewer/actions/keep-this-delete-others.svelte'; import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte'; import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte'; import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte'; @@ -146,6 +147,7 @@ {#if isOwner} {#if stack} + {/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 7686494aa787a..e8946036a5258 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -384,6 +384,7 @@ break; } + case AssetAction.KEEP_THIS_DELETE_OTHERS: case AssetAction.UNSTACK: { closeViewer(); } diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index aa1d976b6f44d..4f68a640736a7 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -9,6 +9,7 @@ export enum AssetAction { ADD = 'add', ADD_TO_ALBUM = 'add-to-album', UNSTACK = 'unstack', + KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others', } export enum AppRoute { diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 77aa7b1ecd073..e20adf051bd96 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -14,6 +14,7 @@ import { getFormatter } from '$lib/utils/i18n'; import { addAssetsToAlbum as addAssets, createStack, + deleteAssets, deleteStacks, getAssetInfo, getBaseUrl, @@ -438,6 +439,32 @@ export const deleteStack = async (stackIds: string[]) => { } }; +export const keepThisDeleteOthers = async (keepId: string, stackId: string) => { + const $t = get(t); + + try { + const stack = await getStack({ id: stackId }); + const assetToKeep = stack.assets.find(x => x.id === keepId); + const assetsToDeleteIds = stack.assets.filter(x => x.id !== keepId).map(x => x.id); + if (!assetToKeep) { + return; + } + + await deleteStacks({ bulkIdsDto: { ids: [stack.id] } }); + await deleteAssets({ assetBulkDeleteDto: { ids: assetsToDeleteIds } }); + + notificationController.show({ + type: NotificationType.Info, + message: $t('kept_this_deleted_others', { values: { count: assetsToDeleteIds.length } }), + }); + + assetToKeep.stack = null; + return assetToKeep; + } catch (error) { + handleError(error, $t('errors.failed_to_keep_this_delete_others')); + } +}; + export const selectAllAssets = async (assetStore: AssetStore, assetInteractionStore: AssetInteractionStore) => { if (get(isSelectingAllAssets)) { // Selection is already ongoing From f0e4eff45a8fe0392728a9d3dda09b7a1b555d29 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 12:33:58 -0600 Subject: [PATCH 2/9] Remove unused import --- .../asset-viewer/actions/keep-this-delete-others.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index c6a1f4df4fa87..ce60fda25c379 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -4,7 +4,6 @@ import { keepThisDeleteOthers } from '$lib/utils/asset-utils'; import type { AssetResponseDto, StackResponseDto } from '@immich/sdk'; import { mdiPinOutline } from '@mdi/js'; - import { t } from 'svelte-i18n'; import type { OnAction } from './action'; export let stack: StackResponseDto; From f2ba2f111fcc7cbfe72f96e32c77969110378ba6 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 12:54:21 -0600 Subject: [PATCH 3/9] Code cleanup --- .../asset-viewer/actions/keep-this-delete-others.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index ce60fda25c379..f1b8f79842e7b 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -18,4 +18,4 @@ }; - + From 3e2b8ac4162f0d68b34aa0b144b799512978eb42 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 13:01:39 -0600 Subject: [PATCH 4/9] Internationalize text --- i18n/en.json | 1 + .../asset-viewer/actions/keep-this-delete-others.svelte | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/i18n/en.json b/i18n/en.json index 12b286cf47062..eb8e970e02d69 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1251,6 +1251,7 @@ "unselect_all_duplicates": "Unselect all duplicates", "unstack": "Un-stack", "unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}", + "keep_this_delete_others": "Keep this, delete others", "untracked_files": "Untracked files", "untracked_files_decription": "These files are not tracked by the application. They can be the results of failed moves, interrupted uploads, or left behind due to a bug", "kept_this_deleted_others": "Kept this asset and deleted {count, plural, one {# asset} other {# assets}}", diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index f1b8f79842e7b..c50d38404c3ab 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -5,6 +5,7 @@ import type { AssetResponseDto, StackResponseDto } from '@immich/sdk'; import { mdiPinOutline } from '@mdi/js'; import type { OnAction } from './action'; + import { t } from 'svelte-i18n'; export let stack: StackResponseDto; export let asset: AssetResponseDto; @@ -18,4 +19,4 @@ }; - + From 0672e829dce91b667ebb32a77c7263ddcb1b3ff2 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 13:02:19 -0600 Subject: [PATCH 5/9] Change variable names --- web/src/lib/utils/asset-utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index e20adf051bd96..92328b610786c 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -444,8 +444,8 @@ export const keepThisDeleteOthers = async (keepId: string, stackId: string) => { try { const stack = await getStack({ id: stackId }); - const assetToKeep = stack.assets.find(x => x.id === keepId); - const assetsToDeleteIds = stack.assets.filter(x => x.id !== keepId).map(x => x.id); + const assetToKeep = stack.assets.find((asset) => asset.id === keepId); + const assetsToDeleteIds = stack.assets.filter((asset) => asset.id !== keepId).map((asset) => asset.id); if (!assetToKeep) { return; } From b6abc9241c880559a2b27615c130137fd63c7372 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 13:31:20 -0600 Subject: [PATCH 6/9] Change keepThisDeleteOthers function to accept asset and stack objects instead of ids --- .../actions/keep-this-delete-others.svelte | 2 +- web/src/lib/utils/asset-utils.ts | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index c50d38404c3ab..20430e16a5ae2 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -12,7 +12,7 @@ export let onAction: OnAction; const handleKeepThisDeleteOthers = async () => { - const keptAsset = await keepThisDeleteOthers(asset.id, stack.id); + const keptAsset = await keepThisDeleteOthers(asset, stack); if (keptAsset) { onAction({ type: AssetAction.UNSTACK, assets: [keptAsset] }); } diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 92328b610786c..647fd8d374ea5 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -28,6 +28,7 @@ import { type AssetResponseDto, type AssetTypeEnum, type DownloadInfoDto, + type StackResponseDto, type UserPreferencesResponseDto, type UserResponseDto, } from '@immich/sdk'; @@ -439,17 +440,11 @@ export const deleteStack = async (stackIds: string[]) => { } }; -export const keepThisDeleteOthers = async (keepId: string, stackId: string) => { +export const keepThisDeleteOthers = async (keepAsset: AssetResponseDto, stack: StackResponseDto) => { const $t = get(t); try { - const stack = await getStack({ id: stackId }); - const assetToKeep = stack.assets.find((asset) => asset.id === keepId); - const assetsToDeleteIds = stack.assets.filter((asset) => asset.id !== keepId).map((asset) => asset.id); - if (!assetToKeep) { - return; - } - + const assetsToDeleteIds = stack.assets.filter((asset) => asset.id !== keepAsset.id).map((asset) => asset.id); await deleteStacks({ bulkIdsDto: { ids: [stack.id] } }); await deleteAssets({ assetBulkDeleteDto: { ids: assetsToDeleteIds } }); @@ -458,8 +453,8 @@ export const keepThisDeleteOthers = async (keepId: string, stackId: string) => { message: $t('kept_this_deleted_others', { values: { count: assetsToDeleteIds.length } }), }); - assetToKeep.stack = null; - return assetToKeep; + keepAsset.stack = null; + return keepAsset; } catch (error) { handleError(error, $t('errors.failed_to_keep_this_delete_others')); } From 6420550c1df8fcaeb90741909279d4a4733fcbba Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Wed, 23 Oct 2024 13:47:10 -0600 Subject: [PATCH 7/9] Add confirmation dialog for keep this, delete others --- i18n/en.json | 2 ++ .../actions/keep-this-delete-others.svelte | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/i18n/en.json b/i18n/en.json index eb8e970e02d69..beb2602efb69b 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -462,6 +462,7 @@ "confirm": "Confirm", "confirm_admin_password": "Confirm Admin Password", "confirm_delete_shared_link": "Are you sure you want to delete this shared link?", + "confirm_keep_this_delete_others": "Are you sure you want to keep this asset and delete the others?", "confirm_password": "Confirm password", "contain": "Contain", "context": "Context", @@ -511,6 +512,7 @@ "delete_key": "Delete key", "delete_library": "Delete Library", "delete_link": "Delete link", + "delete_others": "Delete others", "delete_shared_link": "Delete shared link", "delete_tag": "Delete tag", "delete_tag_confirmation_prompt": "Are you sure you want to delete {tagName} tag?", diff --git a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte index 20430e16a5ae2..3d52b5f28dae9 100644 --- a/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte +++ b/web/src/lib/components/asset-viewer/actions/keep-this-delete-others.svelte @@ -6,12 +6,23 @@ import { mdiPinOutline } from '@mdi/js'; import type { OnAction } from './action'; import { t } from 'svelte-i18n'; + import { dialogController } from '$lib/components/shared-components/dialog/dialog'; export let stack: StackResponseDto; export let asset: AssetResponseDto; export let onAction: OnAction; const handleKeepThisDeleteOthers = async () => { + const isConfirmed = await dialogController.show({ + title: $t('keep_this_delete_others'), + prompt: $t('confirm_keep_this_delete_others'), + confirmText: $t('delete_others'), + }); + + if (!isConfirmed) { + return; + } + const keptAsset = await keepThisDeleteOthers(asset, stack); if (keptAsset) { onAction({ type: AssetAction.UNSTACK, assets: [keptAsset] }); From b9fafaea7dff5499418b42c91bd70d45cdd4eb6a Mon Sep 17 00:00:00 2001 From: Braydon Davis <122051388+bdavis2-PCTY@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:23:45 -0700 Subject: [PATCH 8/9] Update i18n/en.json Co-authored-by: Mert <101130780+mertalev@users.noreply.github.com> --- i18n/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/en.json b/i18n/en.json index beb2602efb69b..ae607e9d45682 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -462,7 +462,7 @@ "confirm": "Confirm", "confirm_admin_password": "Confirm Admin Password", "confirm_delete_shared_link": "Are you sure you want to delete this shared link?", - "confirm_keep_this_delete_others": "Are you sure you want to keep this asset and delete the others?", + "confirm_keep_this_delete_others": "All other assets in the stack will be deleted except for this asset. Are you sure you want to continue?", "confirm_password": "Confirm password", "contain": "Contain", "context": "Context", From 05514ed1385eadfe01e26d0efdbed7a8b8eb1041 Mon Sep 17 00:00:00 2001 From: Braydon Davis Date: Mon, 18 Nov 2024 14:26:01 -0700 Subject: [PATCH 9/9] Reverse delete stacks/assets order --- web/src/lib/utils/asset-utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 647fd8d374ea5..37041ecbc43a4 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -445,8 +445,8 @@ export const keepThisDeleteOthers = async (keepAsset: AssetResponseDto, stack: S try { const assetsToDeleteIds = stack.assets.filter((asset) => asset.id !== keepAsset.id).map((asset) => asset.id); - await deleteStacks({ bulkIdsDto: { ids: [stack.id] } }); await deleteAssets({ assetBulkDeleteDto: { ids: assetsToDeleteIds } }); + await deleteStacks({ bulkIdsDto: { ids: [stack.id] } }); notificationController.show({ type: NotificationType.Info,