From c0ac7af1c49e41c867aa007509c9121e0d489304 Mon Sep 17 00:00:00 2001 From: Pecacheu <3608878+Pecacheu@users.noreply.github.com> Date: Fri, 3 Jul 2026 20:15:59 -0400 Subject: [PATCH] feat: Bulk remove from album modal fix: Add RemoveFromAlbum to all multi-select context menus fix: Clear selection after removing from own album in album view --- .../album-selection/album-selection-utils.ts | 5 +- .../actions/RemoveFromAlbumAction.svelte | 49 +++++-------------- web/src/lib/modals/AlbumPickerModal.svelte | 13 +++-- .../modals/AssetRemoveFromAlbumModal.svelte | 33 +++++++++++++ .../[[assetId=id]]/+page.svelte | 1 + .../[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/MapTimelinePanel.svelte | 2 + .../[[assetId=id]]/MemoryViewer.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + .../(user)/photos/[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + .../[[assetId=id]]/+page.svelte | 2 + 15 files changed, 78 insertions(+), 43 deletions(-) create mode 100644 web/src/lib/modals/AssetRemoveFromAlbumModal.svelte diff --git a/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts b/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts index 5d4e46a063..57ca6d7fb3 100644 --- a/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts +++ b/web/src/lib/components/shared-components/album-selection/album-selection-utils.ts @@ -41,10 +41,13 @@ export class AlbumModalRowConverter { albums: AlbumResponseDto[], selectedRowIndex: number, multiSelectedAlbumIds: string[], + allowNew = true ): AlbumModalRow[] { // only show recent albums if no search was entered const recentAlbumsToShow = search.length === 0 ? recentAlbums : []; - const rows: AlbumModalRow[] = [{ type: AlbumModalRowType.NEW_ALBUM, selected: selectedRowIndex === 0 }]; + const rows: AlbumModalRow[] = allowNew + ? [{ type: AlbumModalRowType.NEW_ALBUM, selected: selectedRowIndex === 0 }] + : []; const normalizedSearch = normalizeSearchString(search); const filteredAlbums = sortAlbums( diff --git a/web/src/lib/components/timeline/actions/RemoveFromAlbumAction.svelte b/web/src/lib/components/timeline/actions/RemoveFromAlbumAction.svelte index cc4766150b..45bc3f0f15 100644 --- a/web/src/lib/components/timeline/actions/RemoveFromAlbumAction.svelte +++ b/web/src/lib/components/timeline/actions/RemoveFromAlbumAction.svelte @@ -1,54 +1,29 @@ {#if menuItem} - + {:else} {/if} diff --git a/web/src/lib/modals/AlbumPickerModal.svelte b/web/src/lib/modals/AlbumPickerModal.svelte index 19430e271c..1e88dbb150 100644 --- a/web/src/lib/modals/AlbumPickerModal.svelte +++ b/web/src/lib/modals/AlbumPickerModal.svelte @@ -22,11 +22,12 @@ let selectedRowIndex: number = $state(-1); type Props = { + remove?: boolean; onClose: (albums?: AlbumResponseDto[]) => void; selectedItemsCount?: number; }; - let { onClose, selectedItemsCount }: Props = $props(); + let { remove, onClose, selectedItemsCount }: Props = $props(); onMount(async () => { albums = await getAllAlbums({}); @@ -39,7 +40,7 @@ const rowConverter = new AlbumModalRowConverter($albumViewSettings.sortBy, $albumViewSettings.sortOrder); const albumModalRows = $derived( - rowConverter.toModalRows(search, recentAlbums, albums, selectedRowIndex, multiSelectedAlbumIds), + rowConverter.toModalRows(search, recentAlbums, albums, selectedRowIndex, multiSelectedAlbumIds, !remove), ); const selectableRowCount = $derived(albumModalRows.filter((row) => isSelectableRowType(row.type)).length); @@ -150,9 +151,11 @@ }; const title = $derived( - selectedItemsCount === undefined - ? $t('select_albums') - : $t('add_to_album_item_count', { values: { count: selectedItemsCount } }), + remove + ? 'remove_from_album' + : selectedItemsCount === undefined + ? $t('select_albums') + : $t('add_to_album_item_count', { values: { count: selectedItemsCount } }), ); diff --git a/web/src/lib/modals/AssetRemoveFromAlbumModal.svelte b/web/src/lib/modals/AssetRemoveFromAlbumModal.svelte new file mode 100644 index 0000000000..290ee2786d --- /dev/null +++ b/web/src/lib/modals/AssetRemoveFromAlbumModal.svelte @@ -0,0 +1,33 @@ + + + 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 032a58ad52..d0371d6b3e 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 @@ -158,6 +158,7 @@ const handleRemoveAssets = async (assetIds: string[]) => { timelineManager.removeAssets(assetIds); + timelineMultiSelectManager.clear(); await refreshAlbum(); }; diff --git a/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte index 597a1e43e1..784ede3508 100644 --- a/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/favorites/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -8,6 +8,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte'; @@ -86,6 +87,7 @@ {/if} + timelineManager.removeAssets(assetIds)} diff --git a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte index 8d6cbfeeb0..461771f563 100644 --- a/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/folders/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -15,6 +15,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import SetVisibilityAction from '$lib/components/timeline/actions/SetVisibilityAction.svelte'; @@ -155,6 +156,7 @@ {#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned} {/if} +
diff --git a/web/src/routes/(user)/locked/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/locked/[[photos=photos]]/[[assetId=id]]/+page.svelte index ec0afaeba0..aa959650b7 100644 --- a/web/src/routes/(user)/locked/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/locked/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -7,6 +7,7 @@ import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte'; import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte'; import SetVisibilityAction from '$lib/components/timeline/actions/SetVisibilityAction.svelte'; @@ -83,6 +84,7 @@ + timelineManager.removeAssets(assetIds)} /> diff --git a/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/MapTimelinePanel.svelte b/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/MapTimelinePanel.svelte index 9bec1be1f8..0d9493f2bc 100644 --- a/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/MapTimelinePanel.svelte +++ b/web/src/routes/(user)/map/[[photos=photos]]/[[assetId=id]]/MapTimelinePanel.svelte @@ -8,6 +8,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import LinkLivePhotoAction from '$lib/components/timeline/actions/LinkLivePhotoAction.svelte'; @@ -161,6 +162,7 @@ {#if authManager.preferences.tags.enabled} {/if} + timelineManager.removeAssets(assetIds)} diff --git a/web/src/routes/(user)/memory/[[photos=photos]]/[[assetId=id]]/MemoryViewer.svelte b/web/src/routes/(user)/memory/[[photos=photos]]/[[assetId=id]]/MemoryViewer.svelte index df484fba81..5d5c968706 100644 --- a/web/src/routes/(user)/memory/[[photos=photos]]/[[assetId=id]]/MemoryViewer.svelte +++ b/web/src/routes/(user)/memory/[[photos=photos]]/[[assetId=id]]/MemoryViewer.svelte @@ -11,6 +11,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import TagAction from '$lib/components/timeline/actions/TagAction.svelte'; @@ -357,6 +358,7 @@ {#if authManager.preferences.tags.enabled && assetMultiSelectManager.isAllUserOwned} {/if} + diff --git a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte index 3fe707c6df..8935136215 100644 --- a/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/people/[personId]/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -15,6 +15,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte'; @@ -490,6 +491,7 @@ {/if} + handleDeleteAssets(assetIds)} diff --git a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte index 44c1ac4e65..f886588d18 100644 --- a/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/photos/[[assetId=id]]/+page.svelte @@ -9,6 +9,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import LinkLivePhotoAction from '$lib/components/timeline/actions/LinkLivePhotoAction.svelte'; @@ -153,6 +154,7 @@ {#if authManager.preferences.tags.enabled} {/if} + timelineManager.removeAssets(assetIds)} diff --git a/web/src/routes/(user)/recently-added/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/recently-added/[[photos=photos]]/[[assetId=id]]/+page.svelte index e84f32bed5..7f7d9d85e9 100644 --- a/web/src/routes/(user)/recently-added/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/recently-added/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -9,6 +9,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import LinkLivePhotoAction from '$lib/components/timeline/actions/LinkLivePhotoAction.svelte'; @@ -147,6 +148,7 @@ {#if authManager.preferences.tags.enabled} {/if} + timelineManager.removeAssets(assetIds)} diff --git a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte index e7a18f1d0f..1ba690f6e4 100644 --- a/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/search/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -13,6 +13,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import SetVisibilityAction from '$lib/components/timeline/actions/SetVisibilityAction.svelte'; @@ -377,6 +378,7 @@ {#if authManager.preferences.tags.enabled} {/if} +
diff --git a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte index 6353592404..9e3f2217e6 100644 --- a/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte +++ b/web/src/routes/(user)/tags/[[photos=photos]]/[[assetId=id]]/+page.svelte @@ -15,6 +15,7 @@ import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte'; import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte'; import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte'; + import RemoveFromAlbum from '$lib/components/timeline/actions/RemoveFromAlbumAction.svelte'; import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte'; import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte'; import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte'; @@ -135,6 +136,7 @@ {#if authManager.preferences.tags.enabled} {/if} + timelineManager.removeAssets(assetIds)}