From 2152191c242ff22caa50d85f7d5652f2e2269c96 Mon Sep 17 00:00:00 2001 From: Yaros Date: Fri, 11 Sep 2026 20:06:31 +0200 Subject: [PATCH] fix(web): asset remains in timeline after archive (#31348) --- web/src/lib/utils/asset-utils.spec.ts | 45 +++++++++++++++++++++++++-- web/src/lib/utils/asset-utils.ts | 1 + 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/web/src/lib/utils/asset-utils.spec.ts b/web/src/lib/utils/asset-utils.spec.ts index 52517975e2..459ba228d6 100644 --- a/web/src/lib/utils/asset-utils.spec.ts +++ b/web/src/lib/utils/asset-utils.spec.ts @@ -1,5 +1,13 @@ -import type { AssetResponseDto } from '@immich/sdk'; -import { canCopyImageToClipboard, getAssetFilename, getFilenameExtension } from './asset-utils'; +import { AssetVisibility, updateAsset, type AssetResponseDto } from '@immich/sdk'; +import { canCopyImageToClipboard, getAssetFilename, getFilenameExtension, toggleArchive } from './asset-utils'; + +vi.mock('@immich/sdk', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + updateAsset: vi.fn(), + }; +}); describe('get file extension from filename', () => { it('returns the extension without including the dot', () => { @@ -63,3 +71,36 @@ describe('copy image to clipboard', () => { expect(canCopyImageToClipboard()).toEqual(true); }); }); + +describe('toggleArchive', () => { + beforeEach(() => { + vi.mocked(updateAsset).mockReset(); + }); + + it('updates both isArchived and visibility when archiving', async () => { + vi.mocked(updateAsset).mockResolvedValue({ + isArchived: true, + visibility: AssetVisibility.Archive, + } as AssetResponseDto); + + const asset = { id: '1', isArchived: false, visibility: AssetVisibility.Timeline } as AssetResponseDto; + await toggleArchive(asset); + + expect(asset.isArchived).toBe(true); + // regression: visibility must be refreshed so the timeline correctly excludes the archived asset + expect(asset.visibility).toBe(AssetVisibility.Archive); + }); + + it('updates both isArchived and visibility when unarchiving', async () => { + vi.mocked(updateAsset).mockResolvedValue({ + isArchived: false, + visibility: AssetVisibility.Timeline, + } as AssetResponseDto); + + const asset = { id: '1', isArchived: true, visibility: AssetVisibility.Archive } as AssetResponseDto; + await toggleArchive(asset); + + expect(asset.isArchived).toBe(false); + expect(asset.visibility).toBe(AssetVisibility.Timeline); + }); +}); diff --git a/web/src/lib/utils/asset-utils.ts b/web/src/lib/utils/asset-utils.ts index 5918137fd3..e39a4f8811 100644 --- a/web/src/lib/utils/asset-utils.ts +++ b/web/src/lib/utils/asset-utils.ts @@ -403,6 +403,7 @@ export const toggleArchive = async (asset: AssetResponseDto) => { }); asset.isArchived = data.isArchived; + asset.visibility = data.visibility; if (asset.isArchived) { const timelineAsset = toTimelineAsset(asset); showUndoArchiveToast($t('added_to_archive'), [timelineAsset]);