This commit is contained in:
Ray 2026-08-13 14:26:01 +02:00 committed by GitHub
commit b8e32e4568
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 78 additions and 43 deletions

View file

@ -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(

View file

@ -1,54 +1,29 @@
<script lang="ts">
import MenuOption from '$lib/components/shared-components/context-menu/MenuOption.svelte';
import { assetMultiSelectManager } from '$lib/managers/asset-multi-select-manager.svelte';
import { handleError } from '$lib/utils/handle-error';
import { getAlbumInfo, removeAssetFromAlbum, type AlbumResponseDto } from '@immich/sdk';
import { IconButton, modalManager, toastManager } from '@immich/ui';
import { type AlbumResponseDto } from '@immich/sdk';
import AssetRemoveFromAlbumModal from '$lib/modals/AssetRemoveFromAlbumModal.svelte';
import { IconButton, modalManager } from '@immich/ui';
import { mdiDeleteOutline, mdiImageRemoveOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
album: AlbumResponseDto;
onRemove: ((assetIds: string[]) => void) | undefined;
assetIds?: string[];
album?: AlbumResponseDto;
onRemove?: (assetIds: string[]) => void;
menuItem?: boolean;
}
let { album = $bindable(), onRemove, assetIds, menuItem = false }: Props = $props();
let { album, onRemove, menuItem = false }: Props = $props();
const removeFromAlbum = async () => {
const ids = assetIds ?? assetMultiSelectManager.assets.map(({ id }) => id) ?? [];
const isConfirmed = await modalManager.showDialog({
prompt: $t('remove_assets_album_confirmation', { values: { count: ids.length } }),
});
if (!isConfirmed) {
return;
}
try {
const results = await removeAssetFromAlbum({
id: album.id,
bulkIdsDto: { ids },
});
album = await getAlbumInfo({ id: album.id });
onRemove?.(ids);
const count = results.filter(({ success }) => success).length;
toastManager.primary($t('assets_removed_count', { values: { count } }));
assetMultiSelectManager.clear();
} catch (error) {
handleError(error, $t('errors.error_removing_assets_from_album'));
}
const onAction = async () => {
const assetIds = assetMultiSelectManager.assets.map(({ id }) => id) ?? [];
const albumIds = await modalManager.show(AssetRemoveFromAlbumModal, { assetIds });
if (!album || albumIds.includes(album.id)) onRemove?.(assetIds);
};
</script>
{#if menuItem}
<MenuOption text={$t('remove_from_album')} icon={mdiImageRemoveOutline} onClick={removeFromAlbum} />
<MenuOption text={$t('remove_from_album')} icon={mdiImageRemoveOutline} onClick={onAction} />
{:else}
<IconButton
shape="round"
@ -56,6 +31,6 @@
variant="ghost"
aria-label={$t('remove_from_album')}
icon={mdiDeleteOutline}
onclick={removeFromAlbum}
onclick={onAction}
/>
{/if}

View file

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

View file

@ -0,0 +1,33 @@
<script lang="ts">
import AlbumPickerModal from '$lib/modals/AlbumPickerModal.svelte';
import { removeAssetFromAlbum, type AlbumResponseDto } from '@immich/sdk';
import { toastManager } from '@immich/ui';
import { handleError } from '$lib/utils/handle-error';
import { t } from 'svelte-i18n';
type Props = {
assetIds: string[];
onClose: (albumIds: string[]) => void;
};
const { assetIds, onClose }: Props = $props();
const handleClose = async (albums?: AlbumResponseDto[]) => {
if (!albums?.length) return onClose([]);
try {
const albumIds = albums.map(({ id }) => id);
let res;
for (const id of albumIds) {
res = await removeAssetFromAlbum({ id, bulkIdsDto: { ids: assetIds } });
}
const count = res.filter(({ success }) => success).length;
toastManager.primary($t('assets_removed_count', { values: { count } }));
onClose(albumIds);
} catch (e) {
handleError(e, $t('errors.error_removing_assets_from_album'));
}
};
//TODO Only show albums that any selected item is in as option in AlbumPickerModal
</script>
<AlbumPickerModal remove onClose={handleClose} />

View file

@ -158,6 +158,7 @@
const handleRemoveAssets = async (assetIds: string[]) => {
timelineManager.removeAssets(assetIds);
timelineMultiSelectManager.clear();
await refreshAlbum();
};

View file

@ -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 @@
<TagAction menuItem />
{/if}
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets menuItem onAssetDelete={triggerAssetUpdate} onUndoDelete={triggerAssetUpdate} />
<hr />

View file

@ -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 @@
<DownloadAction menuItem />
<ChangeDate menuItem />
<ChangeLocation menuItem />
<RemoveFromAlbum menuItem />
<DeleteAssets menuItem force onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)} />
</ButtonContextMenu>
</AssetSelectControlBar>

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets menuItem onAssetDelete={handleDeleteOrArchiveAssets} />
</ButtonContextMenu>
</AssetSelectControlBar>

View file

@ -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 @@
<TagAction menuItem />
{/if}
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => handleDeleteAssets(assetIds)}

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem onRemove={onSearchQueryUpdate} />
<DeleteAssets menuItem {onAssetDelete} onUndoDelete={onSearchQueryUpdate} />
<hr />
<ActionMenuItem action={Actions.RegenerateThumbnailJob} />

View file

@ -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}
<TagAction menuItem />
{/if}
<RemoveFromAlbum menuItem />
<DeleteAssets
menuItem
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}