From c2965c44084b495c23a4ed7222061eda42a3cbc3 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:10:43 +0200 Subject: [PATCH] fix(web): detail panel out of sync when reopening (#11713) * fix(web): detail panel out of sync when reopening * extract event handler --- .../asset-viewer/detail-panel.e2e-spec.ts | 26 +++++++++++++++++++ .../asset-viewer/asset-viewer.svelte | 23 +++++++++++----- .../asset-viewer/detail-panel.svelte | 11 +------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/e2e/src/web/specs/asset-viewer/detail-panel.e2e-spec.ts b/e2e/src/web/specs/asset-viewer/detail-panel.e2e-spec.ts index 072b48908e..2f90e4e3d8 100644 --- a/e2e/src/web/specs/asset-viewer/detail-panel.e2e-spec.ts +++ b/e2e/src/web/specs/asset-viewer/detail-panel.e2e-spec.ts @@ -1,16 +1,23 @@ import { AssetMediaResponseDto, LoginResponseDto, SharedLinkType } from '@immich/sdk'; import { expect, test } from '@playwright/test'; +import type { Socket } from 'socket.io-client'; import { utils } from 'src/utils'; test.describe('Detail Panel', () => { let admin: LoginResponseDto; let asset: AssetMediaResponseDto; + let websocket: Socket; test.beforeAll(async () => { utils.initSdk(); await utils.resetDatabase(); admin = await utils.adminSetup(); asset = await utils.createAsset(admin.accessToken); + websocket = await utils.connectWebsocket(admin.accessToken); + }); + + test.afterAll(() => { + utils.disconnectWebsocket(websocket); }); test('can be opened for shared links', async ({ page }) => { @@ -57,4 +64,23 @@ test.describe('Detail Panel', () => { await expect(textarea).toBeVisible(); await expect(textarea).not.toBeDisabled(); }); + + test('description changes are visible after reopening', async ({ context, page }) => { + await utils.setAuthCookies(context, admin.accessToken); + await page.goto(`/photos/${asset.id}`); + await page.waitForSelector('#immich-asset-viewer'); + + await page.getByRole('button', { name: 'Info' }).click(); + const textarea = page.getByRole('textbox', { name: 'Add a description' }); + await textarea.fill('new description'); + await expect(textarea).toHaveValue('new description'); + + await page.getByRole('button', { name: 'Info' }).click(); + await expect(textarea).not.toBeVisible(); + await page.getByRole('button', { name: 'Info' }).click(); + await expect(textarea).toBeVisible(); + + await utils.waitForWebsocketEvent({ event: 'assetUpdate', id: asset.id }); + await expect(textarea).toHaveValue('new description'); + }); }); diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index 91238bb9e7..2148ff7dda 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -83,7 +83,7 @@ let isLiked: ActivityResponseDto | null = null; let numberOfComments: number; let fullscreenElement: Element; - let unsubscribe: () => void; + let unsubscribes: (() => void)[] = []; let zoomToggle = () => void 0; let copyImage: () => Promise; @@ -172,6 +172,12 @@ } }; + const onAssetUpdate = (assetUpdate: AssetResponseDto) => { + if (assetUpdate.id === asset.id) { + asset = assetUpdate; + } + }; + $: { if (isShared && asset.id) { handlePromiseError(getFavorite()); @@ -180,11 +186,11 @@ } onMount(async () => { - unsubscribe = websocketEvents.on('on_upload_success', (assetUpdate) => { - if (assetUpdate.id === asset.id) { - asset = assetUpdate; - } - }); + unsubscribes.push( + websocketEvents.on('on_upload_success', onAssetUpdate), + websocketEvents.on('on_asset_update', onAssetUpdate), + ); + await navigate({ targetRoute: 'current', assetId: asset.id }); slideshowStateUnsubscribe = slideshowState.subscribe((value) => { if (value === SlideshowState.PlaySlideshow) { @@ -225,7 +231,10 @@ if (shuffleSlideshowUnsubscribe) { shuffleSlideshowUnsubscribe(); } - unsubscribe?.(); + + for (const unsubscribe of unsubscribes) { + unsubscribe(); + } }); $: { diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte index 268de61f04..2dd5ff1a4d 100644 --- a/web/src/lib/components/asset-viewer/detail-panel.svelte +++ b/web/src/lib/components/asset-viewer/detail-panel.svelte @@ -7,7 +7,6 @@ import { locale } from '$lib/stores/preferences.store'; import { featureFlags } from '$lib/stores/server-config.store'; import { user } from '$lib/stores/user.store'; - import { websocketEvents } from '$lib/stores/websocket'; import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils'; import { delay, isFlipped } from '$lib/utils/asset-utils'; import { @@ -30,7 +29,7 @@ mdiAccountOff, } from '@mdi/js'; import { DateTime } from 'luxon'; - import { createEventDispatcher, onMount } from 'svelte'; + import { createEventDispatcher } from 'svelte'; import { slide } from 'svelte/transition'; import { getByteUnitString } from '$lib/utils/byte-units'; import { handleError } from '$lib/utils/handle-error'; @@ -99,14 +98,6 @@ $: unassignedFaces = asset.unassignedFaces || []; - onMount(() => { - return websocketEvents.on('on_asset_update', (assetUpdate) => { - if (assetUpdate.id === asset.id) { - asset = assetUpdate; - } - }); - }); - const dispatch = createEventDispatcher<{ close: void; }>();