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