fix(web): album reactivity (#32111)

fix(web): album reactivity (#32095)

(cherry picked from commit f81bbc782c4206407c88a06a5e8e74fc5dbe9a8d)

Co-authored-by: Mees Frensel <33722705+meesfrensel@users.noreply.github.com>
This commit is contained in:
immich-push-o-matic[bot]
2026-10-05 16:40:03 +00:00
committed by GitHub
co-authored by Mees Frensel
parent 81de979a35
commit 07b54cf9b4
3 changed files with 33 additions and 34 deletions
@@ -107,21 +107,12 @@
};
const onAlbumRemoveAssets = async ({ assetIds, albumIds }: { assetIds: string[]; albumIds: string[] }) => {
if (!album || !albumIds.includes(album.id)) {
return;
if (!!album && albumIds.includes(album.id) && assetIds.includes(assetCursor.current.id)) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
(await navigateToAsset(assetCursor?.nextAsset)) ||
(await navigateToAsset(assetCursor?.previousAsset)) ||
(await handleClose(assetCursor.current.id));
}
timelineManager.removeAssets(assetIds);
if (!assetIds.includes(assetCursor.current.id)) {
return;
}
// keep the cleanup workflow in viewer by moving to adjacent asset first
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
(await navigateToAsset(assetCursor?.nextAsset)) ||
(await navigateToAsset(assetCursor?.previousAsset)) ||
(await handleClose(assetCursor.current.id));
};
const handlePreAction = async (action: Action) => {
@@ -123,6 +123,11 @@ export class TimelineManager extends VirtualScrollManager {
}
},
AssetsUnarchive: (assets) => this.upsertAssets(assets),
AlbumRemoveAssets: ({ assetIds, albumIds }) => {
if (this.#options.albumId && albumIds.includes(this.#options.albumId)) {
this.removeAssets(assetIds);
}
},
StackCreate: (stack) => {
if (this.#options.withStacked) {
updateStackedAssetInTimeline(this, stack);
@@ -46,7 +46,7 @@
import { handlePromiseError } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { isAlbumsRoute, navigate, type AssetGridRouteSearchParams } from '$lib/utils/navigation';
import { AlbumUserRole, AssetVisibility, getAlbumInfo, updateAlbumInfo, type AlbumResponseDto } from '@immich/sdk';
import { AlbumUserRole, AssetVisibility, updateAlbumInfo, type AlbumResponseDto } from '@immich/sdk';
import { ActionButton, CommandPaletteDefaultProvider, Icon, IconButton, toastManager } from '@immich/ui';
import {
mdiAccountEye,
@@ -67,6 +67,7 @@
import AlbumDescription from './AlbumDescription.svelte';
import AlbumTitle from './AlbumTitle.svelte';
import ActionMenuItem from '$lib/components/ActionMenuItem.svelte';
import { delay } from '$lib/utils/asset-utils';
interface Props {
data: PageData;
@@ -121,8 +122,17 @@
await goto(Route.albums());
};
const refreshAlbum = async () => {
album = await getAlbumInfo({ id: album.id });
const refreshAlbum = async (updated?: AlbumResponseDto) => {
if (updated) {
if (updated.id !== album.id) {
return;
}
album = updated;
}
// invalidating during navigation causes an infinite page load
await navigating.complete;
await invalidate('album:data');
};
const setModeToView = async () => {
@@ -145,9 +155,10 @@
assetMultiSelectManager.clear();
};
const onAlbumRemoveAssets = async ({ assetIds, albumIds }: { assetIds: string[]; albumIds: string[] }) => {
const onAlbumRemoveAssets = async ({ albumIds }: { assetIds: string[]; albumIds: string[] }) => {
if (albumIds.includes(album.id)) {
await handleRemoveAssets(assetIds);
await delay(1000);
await refreshAlbum();
}
};
@@ -203,8 +214,8 @@
}
});
let album = $state(data.album);
let albumId = $derived(album.id);
let album = $derived(data.album);
const albumId = $derived(album.id);
const albumHasViewers = $derived(album.albumUsers.some(({ role }) => role === AlbumUserRole.Viewer));
const containsEditors = $derived(album?.shared && album.albumUsers.some(({ role }) => role === AlbumUserRole.Editor));
@@ -300,15 +311,6 @@
album = { ...album, albumUsers };
};
const onAlbumUpdate = async (newAlbum: AlbumResponseDto) => {
album = newAlbum;
// invalidating during navigation causes an infinite page load
await navigating.complete;
await invalidate('album:data');
};
const { Cast } = $derived(getGlobalActions($t));
const Actions = $derived(getAlbumActions($t, album));
const { AddAssets, Upload } = $derived(getAlbumAssetsActions($t, album, timelineMultiSelectManager.assets));
@@ -323,15 +325,16 @@
</script>
<OnEvents
onSharedLinkCreate={refreshAlbum}
onSharedLinkDelete={refreshAlbum}
onSharedLinkCreate={(link) => refreshAlbum(link.album)}
onSharedLinkDelete={() => refreshAlbum()}
{onAlbumDelete}
{onAlbumAddAssets}
{onAlbumRemoveAssets}
{onAlbumShare}
{onAlbumUserUpdate}
onAlbumUserDelete={refreshAlbum}
{onAlbumUpdate}
onAlbumUserDelete={() => refreshAlbum()}
onAlbumUpdate={(album) => refreshAlbum(album)}
onAssetsDelete={() => refreshAlbum()}
/>
<CommandPaletteDefaultProvider name={$t('album')} actions={[AddAssets, Upload, Close, ...Object.values(Actions)]} />