mirror of
https://github.com/immich-app/immich.git
synced 2024-11-21 18:16:55 +02:00
feat(web): Implement keep this delete others for asset stacks (#14217)
This commit is contained in:
parent
bcd17c2ebe
commit
1737013e66
@ -465,6 +465,7 @@
|
|||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"confirm_admin_password": "Confirm Admin Password",
|
"confirm_admin_password": "Confirm Admin Password",
|
||||||
"confirm_delete_shared_link": "Are you sure you want to delete this shared link?",
|
"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",
|
"confirm_password": "Confirm password",
|
||||||
"contain": "Contain",
|
"contain": "Contain",
|
||||||
"context": "Context",
|
"context": "Context",
|
||||||
@ -514,6 +515,7 @@
|
|||||||
"delete_key": "Delete key",
|
"delete_key": "Delete key",
|
||||||
"delete_library": "Delete Library",
|
"delete_library": "Delete Library",
|
||||||
"delete_link": "Delete link",
|
"delete_link": "Delete link",
|
||||||
|
"delete_others": "Delete others",
|
||||||
"delete_shared_link": "Delete shared link",
|
"delete_shared_link": "Delete shared link",
|
||||||
"delete_tag": "Delete tag",
|
"delete_tag": "Delete tag",
|
||||||
"delete_tag_confirmation_prompt": "Are you sure you want to delete {tagName} tag?",
|
"delete_tag_confirmation_prompt": "Are you sure you want to delete {tagName} tag?",
|
||||||
@ -610,6 +612,7 @@
|
|||||||
"failed_to_remove_product_key": "Failed to remove product key",
|
"failed_to_remove_product_key": "Failed to remove product key",
|
||||||
"failed_to_stack_assets": "Failed to stack assets",
|
"failed_to_stack_assets": "Failed to stack assets",
|
||||||
"failed_to_unstack_assets": "Failed to un-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.",
|
"import_path_already_exists": "This import path already exists.",
|
||||||
"incorrect_email_or_password": "Incorrect email or password",
|
"incorrect_email_or_password": "Incorrect email or password",
|
||||||
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
"paths_validation_failed": "{paths, plural, one {# path} other {# paths}} failed validation",
|
||||||
@ -1253,8 +1256,10 @@
|
|||||||
"unselect_all_duplicates": "Unselect all duplicates",
|
"unselect_all_duplicates": "Unselect all duplicates",
|
||||||
"unstack": "Un-stack",
|
"unstack": "Un-stack",
|
||||||
"unstacked_assets_count": "Un-stacked {count, plural, one {# asset} other {# assets}}",
|
"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": "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",
|
"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",
|
"up_next": "Up next",
|
||||||
"updated_password": "Updated password",
|
"updated_password": "Updated password",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
|
@ -12,6 +12,7 @@ type ActionMap = {
|
|||||||
[AssetAction.ADD]: { asset: AssetResponseDto };
|
[AssetAction.ADD]: { asset: AssetResponseDto };
|
||||||
[AssetAction.ADD_TO_ALBUM]: { asset: AssetResponseDto; album: AlbumResponseDto };
|
[AssetAction.ADD_TO_ALBUM]: { asset: AssetResponseDto; album: AlbumResponseDto };
|
||||||
[AssetAction.UNSTACK]: { assets: AssetResponseDto[] };
|
[AssetAction.UNSTACK]: { assets: AssetResponseDto[] };
|
||||||
|
[AssetAction.KEEP_THIS_DELETE_OTHERS]: { asset: AssetResponseDto };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action = {
|
export type Action = {
|
||||||
|
@ -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')} />
|
@ -13,6 +13,7 @@
|
|||||||
import ShareAction from '$lib/components/asset-viewer/actions/share-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';
|
import ShowDetailAction from '$lib/components/asset-viewer/actions/show-detail-action.svelte';
|
||||||
import UnstackAction from '$lib/components/asset-viewer/actions/unstack-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 CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
|
||||||
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.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';
|
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
|
||||||
@ -166,6 +167,7 @@
|
|||||||
{#if isOwner}
|
{#if isOwner}
|
||||||
{#if stack}
|
{#if stack}
|
||||||
<UnstackAction {stack} {onAction} />
|
<UnstackAction {stack} {onAction} />
|
||||||
|
<KeepThisDeleteOthersAction {stack} {asset} {onAction} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if album}
|
{#if album}
|
||||||
<SetAlbumCoverAction {asset} {album} />
|
<SetAlbumCoverAction {asset} {album} />
|
||||||
|
@ -379,6 +379,7 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AssetAction.KEEP_THIS_DELETE_OTHERS:
|
||||||
case AssetAction.UNSTACK: {
|
case AssetAction.UNSTACK: {
|
||||||
closeViewer();
|
closeViewer();
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ export enum AssetAction {
|
|||||||
ADD = 'add',
|
ADD = 'add',
|
||||||
ADD_TO_ALBUM = 'add-to-album',
|
ADD_TO_ALBUM = 'add-to-album',
|
||||||
UNSTACK = 'unstack',
|
UNSTACK = 'unstack',
|
||||||
|
KEEP_THIS_DELETE_OTHERS = 'keep-this-delete-others',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AppRoute {
|
export enum AppRoute {
|
||||||
|
@ -14,6 +14,7 @@ import { getFormatter } from '$lib/utils/i18n';
|
|||||||
import {
|
import {
|
||||||
addAssetsToAlbum as addAssets,
|
addAssetsToAlbum as addAssets,
|
||||||
createStack,
|
createStack,
|
||||||
|
deleteAssets,
|
||||||
deleteStacks,
|
deleteStacks,
|
||||||
getAssetInfo,
|
getAssetInfo,
|
||||||
getBaseUrl,
|
getBaseUrl,
|
||||||
@ -27,6 +28,7 @@ import {
|
|||||||
type AssetResponseDto,
|
type AssetResponseDto,
|
||||||
type AssetTypeEnum,
|
type AssetTypeEnum,
|
||||||
type DownloadInfoDto,
|
type DownloadInfoDto,
|
||||||
|
type StackResponseDto,
|
||||||
type UserPreferencesResponseDto,
|
type UserPreferencesResponseDto,
|
||||||
type UserResponseDto,
|
type UserResponseDto,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
@ -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) => {
|
export const selectAllAssets = async (assetStore: AssetStore, assetInteractionStore: AssetInteractionStore) => {
|
||||||
if (get(isSelectingAllAssets)) {
|
if (get(isSelectingAllAssets)) {
|
||||||
// Selection is already ongoing
|
// Selection is already ongoing
|
||||||
|
Loading…
Reference in New Issue
Block a user