diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte
index fb8d044b1a..bb826110b7 100644
--- a/web/src/lib/components/album-page/albums-list.svelte
+++ b/web/src/lib/components/album-page/albums-list.svelte
@@ -3,6 +3,7 @@
import { resolve } from '$app/paths';
import AlbumCardGroup from '$lib/components/album-page/album-card-group.svelte';
import AlbumsTable from '$lib/components/album-page/albums-table.svelte';
+ import OnEvents from '$lib/components/OnEvents.svelte';
import MenuOption from '$lib/components/shared-components/context-menu/menu-option.svelte';
import RightClickContextMenu from '$lib/components/shared-components/context-menu/right-click-context-menu.svelte';
import ToastAction from '$lib/components/ToastAction.svelte';
@@ -10,7 +11,7 @@
import AlbumEditModal from '$lib/modals/AlbumEditModal.svelte';
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
- import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service';
+ import { handleDeleteAlbum, handleDownloadAlbum } from '$lib/services/album.service';
import {
AlbumFilter,
AlbumGroupBy,
@@ -26,7 +27,7 @@
import type { ContextMenuPosition } from '$lib/utils/context-menu';
import { handleError } from '$lib/utils/handle-error';
import { normalizeSearchString } from '$lib/utils/string-utils';
- import { addUsersToAlbum, deleteAlbum, isHttpError, type AlbumResponseDto, type AlbumUserAddDto } from '@immich/sdk';
+ import { addUsersToAlbum, type AlbumResponseDto, type AlbumUserAddDto } from '@immich/sdk';
import { modalManager, toastManager } from '@immich/ui';
import { mdiDeleteOutline, mdiDownload, mdiRenameOutline, mdiShareVariantOutline } from '@mdi/js';
import { groupBy } from 'lodash-es';
@@ -210,25 +211,6 @@
isOpen = false;
};
- const handleDeleteAlbum = async (albumToDelete: AlbumResponseDto) => {
- try {
- await deleteAlbum({
- id: albumToDelete.id,
- });
- } catch (error) {
- // In rare cases deleting an album completes after the list of albums has been requested,
- // leading to a bad request error.
- // Since the album is already deleted, the error is ignored.
- const isBadRequest = isHttpError(error) && error.status === 400;
- if (!isBadRequest) {
- throw error;
- }
- }
-
- ownedAlbums = ownedAlbums.filter(({ id }) => id !== albumToDelete.id);
- sharedAlbums = sharedAlbums.filter(({ id }) => id !== albumToDelete.id);
- };
-
const handleSelect = async (action: 'edit' | 'share' | 'download' | 'delete') => {
closeAlbumContextMenu();
@@ -272,17 +254,7 @@
}
case 'delete': {
- const isConfirmed = await handleConfirmAlbumDelete(selectedAlbum);
- if (!isConfirmed) {
- return;
- }
-
- try {
- await handleDeleteAlbum(selectedAlbum);
- } catch (error) {
- handleError(error, $t('errors.unable_to_delete_album'));
- }
-
+ await handleDeleteAlbum(selectedAlbum);
break;
}
}
@@ -290,7 +262,7 @@
const removeAlbumsIfEmpty = async () => {
const albumsToRemove = ownedAlbums.filter((album) => album.assetCount === 0 && !album.albumName);
- await Promise.allSettled(albumsToRemove.map((album) => handleDeleteAlbum(album)));
+ await Promise.allSettled(albumsToRemove.map((album) => handleDeleteAlbum(album, { prompt: false, notify: false })));
};
const updateAlbumInfo = (album: AlbumResponseDto) => {
@@ -346,8 +318,15 @@
albumToShare = null;
}
};
+
+ const onAlbumDelete = (album: AlbumResponseDto) => {
+ ownedAlbums = ownedAlbums.filter(({ id }) => id !== album.id);
+ sharedAlbums = sharedAlbums.filter(({ id }) => id !== album.id);
+ };
+
+
{#if albums.length > 0}
{#if userSettings.view === AlbumViewMode.Cover}
diff --git a/web/src/lib/managers/event-manager.svelte.ts b/web/src/lib/managers/event-manager.svelte.ts
index e7d50f026d..1bd437b4cc 100644
--- a/web/src/lib/managers/event-manager.svelte.ts
+++ b/web/src/lib/managers/event-manager.svelte.ts
@@ -1,5 +1,5 @@
import type { ThemeSetting } from '$lib/managers/theme-manager.svelte';
-import type { LoginResponseDto, SharedLinkResponseDto } from '@immich/sdk';
+import type { AlbumResponseDto, LoginResponseDto, SharedLinkResponseDto } from '@immich/sdk';
export type Events = {
AppInit: [];
@@ -8,6 +8,9 @@ export type Events = {
AuthLogout: [];
LanguageChange: [{ name: string; code: string; rtl?: boolean }];
ThemeChange: [ThemeSetting];
+
+ AlbumDelete: [AlbumResponseDto];
+
SharedLinkCreate: [SharedLinkResponseDto];
SharedLinkUpdate: [SharedLinkResponseDto];
SharedLinkDelete: [SharedLinkResponseDto];
diff --git a/web/src/lib/services/album.service.ts b/web/src/lib/services/album.service.ts
index 52fa09d103..6e5583495f 100644
--- a/web/src/lib/services/album.service.ts
+++ b/web/src/lib/services/album.service.ts
@@ -1,7 +1,42 @@
+import { eventManager } from '$lib/managers/event-manager.svelte';
import { downloadArchive } from '$lib/utils/asset-utils';
+import { handleError } from '$lib/utils/handle-error';
import { getFormatter } from '$lib/utils/i18n';
-import type { AlbumResponseDto } from '@immich/sdk';
-import { modalManager } from '@immich/ui';
+import { deleteAlbum, type AlbumResponseDto } from '@immich/sdk';
+import { modalManager, toastManager } from '@immich/ui';
+
+export const handleDeleteAlbum = async (album: AlbumResponseDto, options?: { prompt?: boolean; notify?: boolean }) => {
+ const $t = await getFormatter();
+ const { prompt = true, notify = true } = options ?? {};
+
+ if (prompt) {
+ const confirmation =
+ album.albumName.length > 0
+ ? $t('album_delete_confirmation', { values: { album: album.albumName } })
+ : $t('unnamed_album_delete_confirmation');
+ const description = $t('album_delete_confirmation_description');
+
+ const success = await modalManager.showDialog({ prompt: `${confirmation} ${description}` });
+ if (!success) {
+ return false;
+ }
+ }
+
+ try {
+ await deleteAlbum({ id: album.id });
+
+ eventManager.emit('AlbumDelete', album);
+
+ if (notify) {
+ toastManager.success();
+ }
+
+ return true;
+ } catch (error) {
+ handleError(error, $t('errors.unable_to_delete_album'));
+ return false;
+ }
+};
export const handleDownloadAlbum = async (album: AlbumResponseDto) => {
await downloadArchive(`${album.albumName}.zip`, { albumId: album.id });
diff --git a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
index 6b543e1ecf..9af2b611f2 100644
--- a/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
+++ b/web/src/routes/(user)/albums/[albumId=id]/[[photos=photos]]/[[assetId=id]]/+page.svelte
@@ -36,7 +36,7 @@
import AlbumShareModal from '$lib/modals/AlbumShareModal.svelte';
import AlbumUsersModal from '$lib/modals/AlbumUsersModal.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
- import { handleConfirmAlbumDelete, handleDownloadAlbum } from '$lib/services/album.service';
+ import { handleDeleteAlbum, handleDownloadAlbum } from '$lib/services/album.service';
import { AssetInteraction } from '$lib/stores/asset-interaction.svelte';
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
import { featureFlags } from '$lib/stores/server-config.store';
@@ -59,9 +59,9 @@
AssetVisibility,
addAssetsToAlbum,
addUsersToAlbum,
- deleteAlbum,
getAlbumInfo,
updateAlbumInfo,
+ type AlbumResponseDto,
type AlbumUserAddDto,
} from '@immich/sdk';
import { Button, Icon, IconButton, modalManager, toastManager } from '@immich/ui';
@@ -233,24 +233,6 @@
}
};
- const handleRemoveAlbum = async () => {
- const isConfirmed = await handleConfirmAlbumDelete(album);
-
- if (!isConfirmed) {
- viewMode = AlbumPageViewMode.VIEW;
- return;
- }
-
- try {
- await deleteAlbum({ id: album.id });
- await goto(backUrl);
- } catch (error) {
- handleError(error, $t('errors.unable_to_delete_album'));
- } finally {
- viewMode = AlbumPageViewMode.VIEW;
- }
- };
-
const handleSetVisibility = (assetIds: string[]) => {
timelineManager.removeAssets(assetIds);
assetInteraction.clearMultiselect();
@@ -301,7 +283,7 @@
onNavigate(async ({ to }) => {
if (!isAlbumsRoute(to?.route.id) && album.assetCount === 0 && !album.albumName) {
- await deleteAlbum(album);
+ await handleDeleteAlbum(album, { notify: false, prompt: false });
}
});
@@ -388,6 +370,13 @@
await refreshAlbum();
};
+ const onAlbumDelete = async ({ id }: AlbumResponseDto) => {
+ if (id === album.id) {
+ await goto(backUrl);
+ viewMode = AlbumPageViewMode.VIEW;
+ }
+ };
+
const handleShareLink = async () => {
await modalManager.show(SharedLinkCreateModal, { albumId: album.id });
};
@@ -424,7 +413,7 @@
};
-
+
@@ -672,7 +661,11 @@
{/if}
- handleRemoveAlbum()} />
+ handleDeleteAlbum(album)}
+ />
{/if}