This commit is contained in:
Thomas 2025-10-16 17:59:12 -04:00 committed by GitHub
commit f6e4543796
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 24 deletions

View file

@ -45,15 +45,4 @@ describe('Thumbnail component', () => {
const tabbables = getTabbable(container!);
expect(tabbables.length).toBe(0);
});
it('shows thumbhash while image is loading', () => {
const asset = assetFactory.build({ originalPath: 'image.jpg', originalMimeType: 'image/jpeg' });
const sut = render(Thumbnail, {
asset,
selected: true,
});
const thumbhash = sut.getByTestId('thumbhash');
expect(thumbhash).not.toBeFalsy();
});
});

View file

@ -19,6 +19,7 @@
import { authManager } from '$lib/managers/auth-manager.svelte';
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
import { isCached } from '$lib/utils/cache';
import { moveFocus } from '$lib/utils/focus-util';
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
import { TUNABLES } from '$lib/utils/tunables';
@ -75,6 +76,12 @@
IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
} = TUNABLES;
const thumbnailURL = getAssetThumbnailUrl({
id: asset.id,
size: AssetMediaSize.Thumbnail,
cacheKey: asset.thumbhash,
});
let usingMobileDevice = $derived(mobileDevice.pointerCoarse);
let element: HTMLElement | undefined = $state();
let mouseOver = $state(false);
@ -313,7 +320,7 @@
<ImageThumbnail
class={imageClass}
{brokenAssetClass}
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
url={thumbnailURL}
altText={$getAltText(asset)}
widthStyle="{width}px"
heightStyle="{height}px"
@ -344,17 +351,31 @@
</div>
{/if}
{#if (!loaded || thumbError) && asset.thumbhash}
<canvas
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
data-testid="thumbhash"
class="absolute top-0 object-cover"
style:width="{width}px"
style:height="{height}px"
class:rounded-xl={selected}
draggable="false"
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
></canvas>
{#if asset.thumbhash}
{#await isCached(new Request(thumbnailURL))}
<canvas
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
data-testid="thumbhash"
class="absolute top-0 object-cover"
style:width="{width}px"
style:height="{height}px"
class:rounded-xl={selected}
draggable="false"
></canvas>
{:then cached}
{#if !cached && !loaded && !thumbError}
<canvas
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
data-testid="thumbhash"
class="absolute top-0 object-cover"
style:width="{width}px"
style:height="{height}px"
class:rounded-xl={selected}
draggable="false"
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
></canvas>
{/if}
{/await}
{/if}
</div>

View file

@ -0,0 +1,18 @@
let cache: Cache | undefined;
const getCache = async () => {
cache ||= await openCache();
return cache;
};
const openCache = async () => {
const [key] = await caches.keys();
if (key) {
return caches.open(key);
}
};
export const isCached = async (req: Request) => {
const cache = await getCache();
return !!(await cache?.match(req));
};

View file

@ -29,6 +29,6 @@ export const TUNABLES = {
NAVIGATE_ON_ASSET_IN_VIEW: getBoolean(storage.getItem('ASSET_GRID.NAVIGATE_ON_ASSET_IN_VIEW'), false),
},
IMAGE_THUMBNAIL: {
THUMBHASH_FADE_DURATION: getNumber(storage.getItem('THUMBHASH_FADE_DURATION'), 100),
THUMBHASH_FADE_DURATION: getNumber(storage.getItem('THUMBHASH_FADE_DURATION'), 1000),
},
};