refactor: album delete (#23773)

This commit is contained in:
Jason Rasmussen 2025-11-10 16:10:29 -05:00 committed by GitHub
parent dea95ac2e6
commit d5c5bdffcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 59 deletions

View File

@ -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);
};
</script>
<OnEvents {onAlbumDelete} />
{#if albums.length > 0}
{#if userSettings.view === AlbumViewMode.Cover}
<!-- Album Cards -->

View File

@ -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];

View File

@ -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 });

View File

@ -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 @@
};
</script>
<OnEvents {onSharedLinkCreate} />
<OnEvents {onSharedLinkCreate} {onAlbumDelete} />
<div class="flex overflow-hidden" use:scrollMemoryClearer={{ routeStartsWith: AppRoute.ALBUMS }}>
<div class="relative w-full shrink">
@ -672,7 +661,11 @@
<MenuOption icon={mdiCogOutline} text={$t('options')} onClick={handleOptions} />
{/if}
<MenuOption icon={mdiDeleteOutline} text={$t('delete_album')} onClick={() => handleRemoveAlbum()} />
<MenuOption
icon={mdiDeleteOutline}
text={$t('delete_album')}
onClick={() => handleDeleteAlbum(album)}
/>
</ButtonContextMenu>
{/if}