diff --git a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte index 5d1254f8d9..fc233c755e 100644 --- a/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte +++ b/web/src/lib/components/shared-components/gallery-viewer/gallery-viewer.svelte @@ -228,9 +228,11 @@ }; const toggleArchive = async () => { + const onUndoArchive = assetInteraction.isAllArchived ? undefined : () => onReload?.(); const ids = await archiveAssets( assetInteraction.selectedAssets, assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive, + onUndoArchive, ); if (ids) { assets = assets.filter((asset) => !ids.includes(asset.id)); diff --git a/web/src/lib/components/timeline/actions/ArchiveAction.svelte b/web/src/lib/components/timeline/actions/ArchiveAction.svelte index 95c586ede4..297605f1bf 100644 --- a/web/src/lib/components/timeline/actions/ArchiveAction.svelte +++ b/web/src/lib/components/timeline/actions/ArchiveAction.svelte @@ -26,8 +26,12 @@ const handleArchive = async () => { const visibility = unarchive ? AssetVisibility.Timeline : AssetVisibility.Archive; const assets = [...getOwnedAssets()].filter((asset) => asset.visibility !== visibility); + const onUndoArchive = + visibility === AssetVisibility.Archive && onArchive && onArchive.length > 1 + ? (ids: string[]) => onArchive(ids, AssetVisibility.Timeline) + : undefined; loading = true; - const ids = await archiveAssets(assets, visibility as AssetVisibility); + const ids = await archiveAssets(assets, visibility as AssetVisibility, onUndoArchive); if (ids) { onArchive?.(ids, visibility); clearSelect(); diff --git a/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte index a5fa34289b..86178703e4 100644 --- a/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte +++ b/web/src/lib/components/timeline/actions/TimelineKeyboardActions.svelte @@ -72,7 +72,11 @@ const toggleArchive = async () => { const visibility = assetInteraction.isAllArchived ? AssetVisibility.Timeline : AssetVisibility.Archive; - const ids = await archiveAssets(assetInteraction.selectedAssets, visibility); + const onUndoArchive = + visibility === AssetVisibility.Archive + ? (ids: string[]) => timelineManager.update(ids, (asset) => (asset.visibility = AssetVisibility.Timeline)) + : undefined; + const ids = await archiveAssets(assetInteraction.selectedAssets, visibility, onUndoArchive); timelineManager.update(ids, (asset) => (asset.visibility = visibility)); eventManager.emit('AssetsArchive', ids); deselectAllAssets(); diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index f82377b63f..8605b7ffd0 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -440,7 +440,29 @@ export const toggleArchive = async (asset: AssetResponseDto) => { }); asset.isArchived = data.isArchived; - toastManager.primary(asset.isArchived ? $t(`added_to_archive`) : $t(`removed_from_archive`)); + if (asset.isArchived) { + toastManager.custom( + { + component: ToastAction, + props: { + title: $t('success'), + description: $t('added_to_archive'), + color: 'success', + button: { + color: 'secondary', + text: $t('undo'), + onClick: () => + undoArchiveAssets([asset.id], () => { + asset.isArchived = false; + }), + }, + }, + }, + { timeout: 5000 }, + ); + } else { + toastManager.primary($t('removed_from_archive')); + } } catch (error) { handleError(error, $t('errors.unable_to_add_remove_archive', { values: { archived: asset.isArchived } })); } @@ -448,7 +470,29 @@ export const toggleArchive = async (asset: AssetResponseDto) => { return asset; }; -export const archiveAssets = async (assets: { id: string }[], visibility: AssetVisibility) => { +const undoArchiveAssets = async (ids: string[], onUndo: ((ids: string[]) => void) | undefined = undefined) => { + const $t = get(t); + try { + if (ids.length > 0) { + await updateAssets({ + assetBulkUpdateDto: { + ids, + visibility: AssetVisibility.Timeline, + }, + }); + } + + onUndo?.(ids); + } catch (error) { + handleError(error, $t('errors.unable_to_archive_unarchive', { values: { archived: false } })); + } +}; + +export const archiveAssets = async ( + assets: { id: string }[], + visibility: AssetVisibility, + onUndoArchive: ((ids: string[]) => void) | undefined = undefined, +) => { const ids = assets.map(({ id }) => id); const $t = get(t); @@ -459,11 +503,26 @@ export const archiveAssets = async (assets: { id: string }[], visibility: AssetV }); } - toastManager.primary( - visibility === AssetVisibility.Archive - ? $t('archived_count', { values: { count: ids.length } }) - : $t('unarchived_count', { values: { count: ids.length } }), - ); + if (visibility === AssetVisibility.Archive) { + toastManager.custom( + { + component: ToastAction, + props: { + title: $t('success'), + description: $t('archived_count', { values: { count: ids.length } }), + color: 'success', + button: { + color: 'secondary', + text: $t('undo'), + onClick: () => undoArchiveAssets(ids, onUndoArchive), + }, + }, + }, + { timeout: 5000 }, + ); + } else { + toastManager.primary($t('unarchived_count', { values: { count: ids.length } })); + } } catch (error) { handleError( error,