From 0498f6cb9df6addd846908b69d67c9bc982f5b9b Mon Sep 17 00:00:00 2001 From: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Date: Mon, 24 Nov 2025 17:14:24 +0100 Subject: [PATCH] fix: albums page reactivity loops (#24046) --- .../components/album-page/albums-list.svelte | 85 +++++++------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/web/src/lib/components/album-page/albums-list.svelte b/web/src/lib/components/album-page/albums-list.svelte index 0671422c28..e4b588af8f 100644 --- a/web/src/lib/components/album-page/albums-list.svelte +++ b/web/src/lib/components/album-page/albums-list.svelte @@ -33,7 +33,6 @@ import { groupBy } from 'lodash-es'; import { onMount, type Snippet } from 'svelte'; import { t } from 'svelte-i18n'; - import { run } from 'svelte/legacy'; interface Props { ownedAlbums?: AlbumResponseDto[]; @@ -128,63 +127,45 @@ }, }; - let albums: AlbumResponseDto[] = $state([]); - let filteredAlbums: AlbumResponseDto[] = $state([]); - let groupedAlbums: AlbumGroup[] = $state([]); + let albums = $derived.by(() => { + switch (userSettings.filter) { + case AlbumFilter.Owned: { + return ownedAlbums; + } + case AlbumFilter.Shared: { + return sharedAlbums; + } + default: { + const nonOwnedAlbums = sharedAlbums.filter((album) => album.ownerId !== $user.id); + return nonOwnedAlbums.length > 0 ? ownedAlbums.concat(nonOwnedAlbums) : ownedAlbums; + } + } + }); + const normalizedSearchQuery = $derived(normalizeSearchString(searchQuery)); + let filteredAlbums = $derived( + normalizedSearchQuery + ? albums.filter(({ albumName }) => normalizeSearchString(albumName).includes(normalizedSearchQuery)) + : albums, + ); - let albumGroupOption: string = $state(AlbumGroupBy.None); + let albumGroupOption = $derived(getSelectedAlbumGroupOption(userSettings)); + let groupedAlbums = $derived.by(() => { + const groupFunc = groupOptions[albumGroupOption] ?? groupOptions[AlbumGroupBy.None]; + const groupedAlbums = groupFunc(stringToSortOrder(userSettings.groupOrder), filteredAlbums); + + return groupedAlbums.map((group) => ({ + id: group.id, + name: group.name, + albums: sortAlbums(group.albums, { sortBy: userSettings.sortBy, orderBy: userSettings.sortOrder }), + })); + }); let contextMenuPosition: ContextMenuPosition = $state({ x: 0, y: 0 }); let selectedAlbum: AlbumResponseDto | undefined = $state(); let isOpen = $state(false); - // Step 1: Filter between Owned and Shared albums, or both. - run(() => { - switch (userSettings.filter) { - case AlbumFilter.Owned: { - albums = ownedAlbums; - break; - } - case AlbumFilter.Shared: { - albums = sharedAlbums; - break; - } - default: { - const userId = $user.id; - const nonOwnedAlbums = sharedAlbums.filter((album) => album.ownerId !== userId); - albums = nonOwnedAlbums.length > 0 ? ownedAlbums.concat(nonOwnedAlbums) : ownedAlbums; - } - } - }); - - // Step 2: Filter using the given search query. - run(() => { - if (searchQuery) { - const searchAlbumNormalized = normalizeSearchString(searchQuery); - - filteredAlbums = albums.filter((album) => { - return normalizeSearchString(album.albumName).includes(searchAlbumNormalized); - }); - } else { - filteredAlbums = albums; - } - }); - - // Step 3: Group albums. - run(() => { - albumGroupOption = getSelectedAlbumGroupOption(userSettings); - const groupFunc = groupOptions[albumGroupOption] ?? groupOptions[AlbumGroupBy.None]; - groupedAlbums = groupFunc(stringToSortOrder(userSettings.groupOrder), filteredAlbums); - }); - - // Step 4: Sort albums amongst each group. - run(() => { - groupedAlbums = groupedAlbums.map((group) => ({ - id: group.id, - name: group.name, - albums: sortAlbums(group.albums, { sortBy: userSettings.sortBy, orderBy: userSettings.sortOrder }), - })); - + // TODO get rid of this + $effect(() => { albumGroupIds = groupedAlbums.map(({ id }) => id); });