From 389356149aeed73c70702b7aaf8c04e6b5cef598 Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:18:06 +0100 Subject: [PATCH] refactor: actionable toasts (#27203) --- pnpm-lock.yaml | 10 +++---- web/package.json | 2 +- web/src/lib/components/ToastAction.svelte | 33 ----------------------- web/src/lib/services/album.service.ts | 28 ++++--------------- web/src/lib/utils/actions.ts | 26 ++++++------------ web/src/lib/utils/asset-utils.ts | 16 ++++------- 6 files changed, 24 insertions(+), 91 deletions(-) delete mode 100644 web/src/lib/components/ToastAction.svelte diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 320815b631..0ffd7e02c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -744,8 +744,8 @@ importers: specifier: workspace:* version: link:../open-api/typescript-sdk '@immich/ui': - specifier: ^0.65.3 - version: 0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.53.13)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13) + specifier: ^0.67.2 + version: 0.67.2(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.53.13)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13) '@mapbox/mapbox-gl-rtl-text': specifier: 0.3.0 version: 0.3.0 @@ -3038,8 +3038,8 @@ packages: peerDependencies: svelte: ^5.0.0 - '@immich/ui@0.65.3': - resolution: {integrity: sha512-jMXzCzMNTcCdWXt9IUP7GkALE5oEvPQk/jCOuI2bfxsxCZFzMkUfUS+AV83Vg1vQ6l+g39PbKSPKBEzv125ATQ==} + '@immich/ui@0.67.2': + resolution: {integrity: sha512-GsaoJRiRORJ34CT+W3pAOdhbLr61nNlgFaOzDcnVnSWFonu7+HR3CXdCxbSdyU4+r3xEmcawwo6rMuLgRplHfw==} peerDependencies: svelte: ^5.0.0 @@ -15123,7 +15123,7 @@ snapshots: node-emoji: 2.2.0 svelte: 5.53.13 - '@immich/ui@0.65.3(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.53.13)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)': + '@immich/ui@0.67.2(@sveltejs/kit@2.53.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.53.13)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)(typescript@5.9.3)(vite@8.0.0(@types/node@25.4.0)(esbuild@0.27.3)(jiti@2.6.1)(sass@1.97.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(svelte@5.53.13)': dependencies: '@immich/svelte-markdown-preprocess': 0.2.1(svelte@5.53.13) '@internationalized/date': 3.10.0 diff --git a/web/package.json b/web/package.json index 53d95906cb..b6f4ca6c4e 100644 --- a/web/package.json +++ b/web/package.json @@ -27,7 +27,7 @@ "@formatjs/icu-messageformat-parser": "^3.0.0", "@immich/justified-layout-wasm": "^0.4.3", "@immich/sdk": "workspace:*", - "@immich/ui": "^0.65.3", + "@immich/ui": "^0.67.2", "@mapbox/mapbox-gl-rtl-text": "0.3.0", "@mdi/js": "^7.4.47", "@photo-sphere-viewer/core": "^5.14.0", diff --git a/web/src/lib/components/ToastAction.svelte b/web/src/lib/components/ToastAction.svelte deleted file mode 100644 index 5dc430f323..0000000000 --- a/web/src/lib/components/ToastAction.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {#if button} -
- -
- {/if} -
-
diff --git a/web/src/lib/services/album.service.ts b/web/src/lib/services/album.service.ts index 6ccd67584e..3a70d72478 100644 --- a/web/src/lib/services/album.service.ts +++ b/web/src/lib/services/album.service.ts @@ -1,5 +1,4 @@ import { goto } from '$app/navigation'; -import ToastAction from '$lib/components/ToastAction.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { eventManager } from '$lib/managers/event-manager.svelte'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; @@ -138,16 +137,8 @@ const notifyAddToAlbum = ($t: MessageFormatter, albumId: string, assetIds: strin description = $t('assets_were_part_of_album_count', { values: { count: duplicateCount } }); } - toastManager.custom( - { - component: ToastAction, - props: { - title: $t('info'), - color: 'primary', - description, - button: { text: $t('view_album'), color: 'primary', onClick: () => goto(Route.viewAlbum({ id: albumId })) }, - }, - }, + toastManager.primary( + { description, button: { label: $t('view_album'), onclick: () => goto(Route.viewAlbum({ id: albumId })) } }, { timeout: 5000 }, ); }; @@ -229,18 +220,9 @@ export const handleUpdateAlbum = async ({ id }: { id: string }, dto: UpdateAlbum try { const response = await updateAlbumInfo({ id, updateAlbumDto: dto }); eventManager.emit('AlbumUpdate', response); - toastManager.custom({ - component: ToastAction, - props: { - color: 'primary', - title: $t('success'), - description: $t('album_info_updated'), - button: { - text: $t('view_album'), - color: 'primary', - onClick: () => goto(Route.viewAlbum({ id })), - }, - }, + toastManager.primary({ + description: $t('album_info_updated'), + button: { label: $t('view_album'), onclick: () => goto(Route.viewAlbum({ id })) }, }); return true; diff --git a/web/src/lib/utils/actions.ts b/web/src/lib/utils/actions.ts index 05de75d3bc..46265a78bb 100644 --- a/web/src/lib/utils/actions.ts +++ b/web/src/lib/utils/actions.ts @@ -1,4 +1,3 @@ -import ToastAction from '$lib/components/ToastAction.svelte'; import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; import type { TimelineAsset } from '$lib/managers/timeline-manager/types'; import type { StackResponse } from '$lib/utils/asset-utils'; @@ -32,24 +31,15 @@ export const deleteAssets = async ( await deleteBulk({ assetBulkDeleteDto: { ids, force } }); onAssetDelete(ids); - toastManager.custom( + toastManager.primary( { - component: ToastAction, - props: { - title: $t('success'), - description: force - ? $t('assets_permanently_deleted_count', { values: { count: ids.length } }) - : $t('assets_trashed_count', { values: { count: ids.length } }), - color: 'success', - button: - onUndoDelete && !force - ? { - color: 'secondary', - text: $t('undo'), - onClick: () => undoDeleteAssets(onUndoDelete, assets), - } - : undefined, - }, + description: force + ? $t('assets_permanently_deleted_count', { values: { count: ids.length } }) + : $t('assets_trashed_count', { values: { count: ids.length } }), + button: + onUndoDelete && !force + ? { label: $t('undo'), color: 'secondary', onclick: () => undoDeleteAssets(onUndoDelete, assets) } + : undefined, }, { timeout: 5000 }, ); diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index f82377b63f..284f540d1a 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -1,4 +1,3 @@ -import ToastAction from '$lib/components/ToastAction.svelte'; import { authManager } from '$lib/managers/auth-manager.svelte'; import { downloadManager } from '$lib/managers/download-manager.svelte'; import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte'; @@ -326,16 +325,11 @@ export const stackAssets = async (assets: { id: string }[], showNotification = t try { const stack = await createStack({ stackCreateDto: { assetIds: assets.map(({ id }) => id) } }); if (showNotification) { - toastManager.custom({ - component: ToastAction, - props: { - title: $t('success'), - description: $t('stacked_assets_count', { values: { count: stack.assets.length } }), - color: 'success', - button: { - text: $t('view_stack'), - onClick: () => navigate({ targetRoute: 'current', assetId: stack.primaryAssetId }), - }, + toastManager.primary({ + description: $t('stacked_assets_count', { values: { count: stack.assets.length } }), + button: { + label: $t('view_stack'), + onclick: () => navigate({ targetRoute: 'current', assetId: stack.primaryAssetId }), }, }); }