Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): Implement keep this delete others for asset stacks #14217

Merged
merged 11 commits into from
Nov 19, 2024
5 changes: 5 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,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": "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",
Expand Down Expand Up @@ -514,6 +515,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?",
Expand Down Expand Up @@ -610,6 +612,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",
Expand Down Expand Up @@ -1253,8 +1256,10 @@
"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}}",
"up_next": "Up next",
"updated_password": "Updated password",
"upload": "Upload",
Expand Down
1 change: 1 addition & 0 deletions web/src/lib/components/asset-viewer/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import { AssetAction } from '$lib/constants';
import { keepThisDeleteOthers } from '$lib/utils/asset-utils';
import type { AssetResponseDto, StackResponseDto } from '@immich/sdk';
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] });
}
};
</script>

<MenuOption icon={mdiPinOutline} onClick={handleKeepThisDeleteOthers} text={$t('keep_this_delete_others')} />
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -166,6 +167,7 @@
{#if isOwner}
{#if stack}
<UnstackAction {stack} {onAction} />
<KeepThisDeleteOthersAction {stack} {asset} {onAction} />
{/if}
{#if album}
<SetAlbumCoverAction {asset} {album} />
Expand Down
1 change: 1 addition & 0 deletions web/src/lib/components/asset-viewer/asset-viewer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@
break;
}

case AssetAction.KEEP_THIS_DELETE_OTHERS:
case AssetAction.UNSTACK: {
closeViewer();
}
Expand Down
1 change: 1 addition & 0 deletions web/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
22 changes: 22 additions & 0 deletions web/src/lib/utils/asset-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getFormatter } from '$lib/utils/i18n';
import {
addAssetsToAlbum as addAssets,
createStack,
deleteAssets,
deleteStacks,
getAssetInfo,
getBaseUrl,
Expand All @@ -27,6 +28,7 @@ import {
type AssetResponseDto,
type AssetTypeEnum,
type DownloadInfoDto,
type StackResponseDto,
type UserPreferencesResponseDto,
type UserResponseDto,
} from '@immich/sdk';
Expand Down Expand Up @@ -438,6 +440,26 @@ export const deleteStack = async (stackIds: string[]) => {
}
};

export const keepThisDeleteOthers = async (keepAsset: AssetResponseDto, stack: StackResponseDto) => {
const $t = get(t);

try {
const assetsToDeleteIds = stack.assets.filter((asset) => asset.id !== keepAsset.id).map((asset) => asset.id);
await deleteAssets({ assetBulkDeleteDto: { ids: assetsToDeleteIds } });
await deleteStacks({ bulkIdsDto: { ids: [stack.id] } });

notificationController.show({
type: NotificationType.Info,
message: $t('kept_this_deleted_others', { values: { count: assetsToDeleteIds.length } }),
});

keepAsset.stack = null;
return keepAsset;
} 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
Expand Down
Loading