perf: move album fetching into detail panel (#26632)

This commit is contained in:
Min Idzelis
2026-03-03 06:25:03 -05:00
committed by GitHub
parent acac0d4f37
commit a868ae3ad0
2 changed files with 66 additions and 53 deletions
@@ -30,7 +30,6 @@
import { toTimelineAsset } from '$lib/utils/timeline-util'; import { toTimelineAsset } from '$lib/utils/timeline-util';
import { import {
AssetTypeEnum, AssetTypeEnum,
getAllAlbums,
getAssetInfo, getAssetInfo,
getStack, getStack,
type AlbumResponseDto, type AlbumResponseDto,
@@ -105,7 +104,6 @@
const asset = $derived(cursor.current); const asset = $derived(cursor.current);
const nextAsset = $derived(cursor.nextAsset); const nextAsset = $derived(cursor.nextAsset);
const previousAsset = $derived(cursor.previousAsset); const previousAsset = $derived(cursor.previousAsset);
let appearsInAlbums: AlbumResponseDto[] = $state([]);
let sharedLink = getSharedLink(); let sharedLink = getSharedLink();
let previewStackedAsset: AssetResponseDto | undefined = $state(); let previewStackedAsset: AssetResponseDto | undefined = $state();
let fullscreenElement = $state<Element>(); let fullscreenElement = $state<Element>();
@@ -147,7 +145,7 @@
} }
}; };
onMount(async () => { onMount(() => {
syncAssetViewerOpenClass(true); syncAssetViewerOpenClass(true);
unsubscribes.push( unsubscribes.push(
slideshowState.subscribe((value) => { slideshowState.subscribe((value) => {
@@ -166,8 +164,6 @@
} }
}), }),
); );
await onAlbumAddAssets();
}); });
onDestroy(() => { onDestroy(() => {
@@ -180,18 +176,6 @@
syncAssetViewerOpenClass(false); syncAssetViewerOpenClass(false);
}); });
const onAlbumAddAssets = async () => {
if (authManager.isSharedLink) {
return;
}
try {
appearsInAlbums = await getAllAlbums({ assetId: asset.id });
} catch (error) {
console.error('Error getting album that asset belong to', error);
}
};
const closeViewer = () => { const closeViewer = () => {
onClose?.(asset); onClose?.(asset);
}; };
@@ -363,7 +347,6 @@
const refresh = async () => { const refresh = async () => {
await refreshStack(); await refreshStack();
await onAlbumAddAssets();
ocrManager.clear(); ocrManager.clear();
if (!sharedLink) { if (!sharedLink) {
if (previewStackedAsset) { if (previewStackedAsset) {
@@ -441,7 +424,7 @@
</script> </script>
<CommandPaletteDefaultProvider name={$t('assets')} actions={[Tag]} /> <CommandPaletteDefaultProvider name={$t('assets')} actions={[Tag]} />
<OnEvents {onAssetReplace} {onAssetUpdate} {onAlbumAddAssets} /> <OnEvents {onAssetReplace} {onAssetUpdate} />
<svelte:document bind:fullscreenElement /> <svelte:document bind:fullscreenElement />
@@ -586,7 +569,7 @@
> >
{#if showDetailPanel} {#if showDetailPanel}
<div class="w-90 h-full"> <div class="w-90 h-full">
<DetailPanel {asset} currentAlbum={album} albums={appearsInAlbums} /> <DetailPanel {asset} currentAlbum={album} />
</div> </div>
{:else if assetViewerManager.isShowEditor} {:else if assetViewerManager.isShowEditor}
<div class="w-100 h-full"> <div class="w-100 h-full">
@@ -17,9 +17,16 @@
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils'; import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
import { delay, getDimensions } from '$lib/utils/asset-utils'; import { delay, getDimensions } from '$lib/utils/asset-utils';
import { getByteUnitString } from '$lib/utils/byte-units'; import { getByteUnitString } from '$lib/utils/byte-units';
import { handleError } from '$lib/utils/handle-error';
import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util'; import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
import { getParentPath } from '$lib/utils/tree-utils'; import { getParentPath } from '$lib/utils/tree-utils';
import { AssetMediaSize, getAssetInfo, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk'; import {
AssetMediaSize,
getAllAlbums,
getAssetInfo,
type AlbumResponseDto,
type AssetResponseDto,
} from '@immich/sdk';
import { Icon, IconButton, LoadingSpinner, modalManager, Text } from '@immich/ui'; import { Icon, IconButton, LoadingSpinner, modalManager, Text } from '@immich/ui';
import { import {
mdiCalendar, mdiCalendar,
@@ -38,16 +45,16 @@
import { slide } from 'svelte/transition'; import { slide } from 'svelte/transition';
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte'; import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
import PersonSidePanel from '../faces-page/person-side-panel.svelte'; import PersonSidePanel from '../faces-page/person-side-panel.svelte';
import OnEvents from '../OnEvents.svelte';
import UserAvatar from '../shared-components/user-avatar.svelte'; import UserAvatar from '../shared-components/user-avatar.svelte';
import AlbumListItemDetails from './album-list-item-details.svelte'; import AlbumListItemDetails from './album-list-item-details.svelte';
interface Props { interface Props {
asset: AssetResponseDto; asset: AssetResponseDto;
albums?: AlbumResponseDto[];
currentAlbum?: AlbumResponseDto | null; currentAlbum?: AlbumResponseDto | null;
} }
let { asset, albums = [], currentAlbum = null }: Props = $props(); let { asset, currentAlbum = null }: Props = $props();
let showAssetPath = $state(false); let showAssetPath = $state(false);
let showEditFaces = $state(false); let showEditFaces = $state(false);
@@ -74,14 +81,33 @@
let previousId: string | undefined = $state(); let previousId: string | undefined = $state();
let previousRoute = $derived(currentAlbum?.id ? Route.viewAlbum(currentAlbum) : Route.photos()); let previousRoute = $derived(currentAlbum?.id ? Route.viewAlbum(currentAlbum) : Route.photos());
const refreshAlbums = async () => {
if (authManager.isSharedLink) {
return [];
}
try {
return await getAllAlbums({ assetId: asset.id });
} catch (error) {
handleError(error, 'Error getting asset album membership');
return [];
}
};
let albums = $derived(refreshAlbums());
$effect(() => { $effect(() => {
if (!previousId) { if (!previousId) {
previousId = asset.id; previousId = asset.id;
return;
} }
if (asset.id !== previousId) {
showEditFaces = false; if (asset.id === previousId) {
previousId = asset.id; return;
} }
showEditFaces = false;
previousId = asset.id;
}); });
const getMegapixel = (width: number, height: number): number | undefined => { const getMegapixel = (width: number, height: number): number | undefined => {
@@ -119,6 +145,8 @@
}; };
</script> </script>
<OnEvents onAlbumAddAssets={() => (albums = refreshAlbums())} />
<section class="relative p-2"> <section class="relative p-2">
<div class="flex place-items-center gap-2"> <div class="flex place-items-center gap-2">
<IconButton <IconButton
@@ -502,37 +530,39 @@
</section> </section>
{/if} {/if}
{#if albums.length > 0} {#await albums then albums}
<section class="px-6 py-6 dark:text-immich-dark-fg"> {#if albums.length > 0}
<div class="pb-4"> <section class="px-6 py-6 dark:text-immich-dark-fg">
<Text size="small" color="muted">{$t('appears_in')}</Text> <div class="pb-4">
</div> <Text size="small" color="muted">{$t('appears_in')}</Text>
{#each albums as album (album.id)} </div>
<a href={Route.viewAlbum(album)}> {#each albums as album (album.id)}
<div class="flex gap-4 pt-2 hover:cursor-pointer items-center"> <a href={Route.viewAlbum(album)}>
<div> <div class="flex gap-4 pt-2 hover:cursor-pointer items-center">
<img <div>
alt={album.albumName} <img
class="h-12.5 w-12.5 rounded object-cover" alt={album.albumName}
src={album.albumThumbnailAssetId && class="h-12.5 w-12.5 rounded object-cover"
getAssetMediaUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })} src={album.albumThumbnailAssetId &&
draggable="false" getAssetMediaUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })}
/> draggable="false"
</div> />
</div>
<div class="mb-auto mt-auto"> <div class="mb-auto mt-auto">
<p class="dark:text-immich-dark-primary">{album.albumName}</p> <p class="dark:text-immich-dark-primary">{album.albumName}</p>
<div class="flex flex-col gap-0 text-sm"> <div class="flex flex-col gap-0 text-sm">
<div> <div>
<AlbumListItemDetails {album} /> <AlbumListItemDetails {album} />
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </a>
</a> {/each}
{/each} </section>
</section> {/if}
{/if} {/await}
{#if $preferences?.tags?.enabled} {#if $preferences?.tags?.enabled}
<section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg"> <section class="relative px-2 pb-12 dark:bg-immich-dark-bg dark:text-immich-dark-fg">