From 9fc6fbc3735bbca045c9dfbd61807b55f15f894c Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Wed, 11 Mar 2026 13:46:29 +0100 Subject: [PATCH] fix(web): restore asset update events in asset viewer (#26845) --- .../asset-viewer/asset-viewer.spec.ts | 76 +++++++++++++++++++ .../asset-viewer/asset-viewer.svelte | 8 ++ 2 files changed, 84 insertions(+) create mode 100644 web/src/lib/components/asset-viewer/asset-viewer.spec.ts diff --git a/web/src/lib/components/asset-viewer/asset-viewer.spec.ts b/web/src/lib/components/asset-viewer/asset-viewer.spec.ts new file mode 100644 index 0000000000..a1f50da86a --- /dev/null +++ b/web/src/lib/components/asset-viewer/asset-viewer.spec.ts @@ -0,0 +1,76 @@ +import { getAnimateMock } from '$lib/__mocks__/animate.mock'; +import { getResizeObserverMock } from '$lib/__mocks__/resize-observer.mock'; +import { preferences as preferencesStore, resetSavedUser, user as userStore } from '$lib/stores/user.store'; +import { renderWithTooltips } from '$tests/helpers'; +import { updateAsset } from '@immich/sdk'; +import { assetFactory } from '@test-data/factories/asset-factory'; +import { preferencesFactory } from '@test-data/factories/preferences-factory'; +import { userAdminFactory } from '@test-data/factories/user-factory'; +import { fireEvent, waitFor } from '@testing-library/svelte'; +import AssetViewer from './asset-viewer.svelte'; + +vi.mock('$lib/managers/feature-flags-manager.svelte', () => ({ + featureFlagsManager: { + init: vi.fn(), + loadFeatureFlags: vi.fn(), + value: { smartSearch: true, trash: true }, + } as never, +})); + +vi.mock('$lib/stores/ocr.svelte', () => ({ + ocrManager: { + clear: vi.fn(), + getAssetOcr: vi.fn(), + hasOcrData: false, + showOverlay: false, + }, +})); + +vi.mock('@immich/sdk', async () => { + const sdk = await vi.importActual('@immich/sdk'); + return { + ...sdk, + updateAsset: vi.fn(), + }; +}); + +describe('AssetViewer', () => { + beforeAll(() => { + Element.prototype.animate = getAnimateMock(); + vi.stubGlobal('ResizeObserver', getResizeObserverMock()); + }); + + afterEach(() => { + resetSavedUser(); + vi.clearAllMocks(); + }); + + afterAll(() => { + vi.restoreAllMocks(); + }); + + it('updates the top bar favorite action after pressing favorite', async () => { + const ownerId = 'owner-id'; + const user = userAdminFactory.build({ id: ownerId }); + const asset = assetFactory.build({ ownerId, isFavorite: false, isTrashed: false }); + + userStore.set(user); + preferencesStore.set(preferencesFactory.build({ cast: { gCastEnabled: false } })); + vi.mocked(updateAsset).mockResolvedValue({ ...asset, isFavorite: true }); + + const { getByLabelText, queryByLabelText } = renderWithTooltips(AssetViewer, { + cursor: { current: asset }, + showNavigation: false, + }); + + expect(getByLabelText('to_favorite')).toBeInTheDocument(); + expect(queryByLabelText('unfavorite')).toBeNull(); + + await fireEvent.click(getByLabelText('to_favorite')); + + await waitFor(() => + expect(updateAsset).toHaveBeenCalledWith({ id: asset.id, updateAssetDto: { isFavorite: true } }), + ); + await waitFor(() => expect(getByLabelText('unfavorite')).toBeInTheDocument()); + }); +}); diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index cf1ad4be5a..8520e69a3d 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -5,6 +5,7 @@ import NextAssetAction from '$lib/components/asset-viewer/actions/next-asset-action.svelte'; import PreviousAssetAction from '$lib/components/asset-viewer/actions/previous-asset-action.svelte'; import AssetViewerNavBar from '$lib/components/asset-viewer/asset-viewer-nav-bar.svelte'; + import OnEvents from '$lib/components/OnEvents.svelte'; import { AssetAction, ProjectionType } from '$lib/constants'; import { activityManager } from '$lib/managers/activity-manager.svelte'; import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte'; @@ -142,6 +143,12 @@ } }; + const onAssetUpdate = (updatedAsset: AssetResponseDto) => { + if (asset.id === updatedAsset.id) { + cursor = { ...cursor, current: updatedAsset }; + } + }; + onMount(() => { syncAssetViewerOpenClass(true); unsubscribes.push( @@ -406,6 +413,7 @@ +