mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
Merge 412633d0aa into 505e16c37c
This commit is contained in:
commit
f6e4543796
4 changed files with 52 additions and 24 deletions
|
|
@ -45,15 +45,4 @@ describe('Thumbnail component', () => {
|
||||||
const tabbables = getTabbable(container!);
|
const tabbables = getTabbable(container!);
|
||||||
expect(tabbables.length).toBe(0);
|
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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||||
|
import { isCached } from '$lib/utils/cache';
|
||||||
import { moveFocus } from '$lib/utils/focus-util';
|
import { moveFocus } from '$lib/utils/focus-util';
|
||||||
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
|
import { currentUrlReplaceAssetId } from '$lib/utils/navigation';
|
||||||
import { TUNABLES } from '$lib/utils/tunables';
|
import { TUNABLES } from '$lib/utils/tunables';
|
||||||
|
|
@ -75,6 +76,12 @@
|
||||||
IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
|
IMAGE_THUMBNAIL: { THUMBHASH_FADE_DURATION },
|
||||||
} = TUNABLES;
|
} = TUNABLES;
|
||||||
|
|
||||||
|
const thumbnailURL = getAssetThumbnailUrl({
|
||||||
|
id: asset.id,
|
||||||
|
size: AssetMediaSize.Thumbnail,
|
||||||
|
cacheKey: asset.thumbhash,
|
||||||
|
});
|
||||||
|
|
||||||
let usingMobileDevice = $derived(mobileDevice.pointerCoarse);
|
let usingMobileDevice = $derived(mobileDevice.pointerCoarse);
|
||||||
let element: HTMLElement | undefined = $state();
|
let element: HTMLElement | undefined = $state();
|
||||||
let mouseOver = $state(false);
|
let mouseOver = $state(false);
|
||||||
|
|
@ -313,7 +320,7 @@
|
||||||
<ImageThumbnail
|
<ImageThumbnail
|
||||||
class={imageClass}
|
class={imageClass}
|
||||||
{brokenAssetClass}
|
{brokenAssetClass}
|
||||||
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
url={thumbnailURL}
|
||||||
altText={$getAltText(asset)}
|
altText={$getAltText(asset)}
|
||||||
widthStyle="{width}px"
|
widthStyle="{width}px"
|
||||||
heightStyle="{height}px"
|
heightStyle="{height}px"
|
||||||
|
|
@ -344,17 +351,31 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if (!loaded || thumbError) && asset.thumbhash}
|
{#if asset.thumbhash}
|
||||||
<canvas
|
{#await isCached(new Request(thumbnailURL))}
|
||||||
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
|
<canvas
|
||||||
data-testid="thumbhash"
|
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
|
||||||
class="absolute top-0 object-cover"
|
data-testid="thumbhash"
|
||||||
style:width="{width}px"
|
class="absolute top-0 object-cover"
|
||||||
style:height="{height}px"
|
style:width="{width}px"
|
||||||
class:rounded-xl={selected}
|
style:height="{height}px"
|
||||||
draggable="false"
|
class:rounded-xl={selected}
|
||||||
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
|
draggable="false"
|
||||||
></canvas>
|
></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}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
18
web/src/lib/utils/cache.ts
Normal file
18
web/src/lib/utils/cache.ts
Normal 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));
|
||||||
|
};
|
||||||
|
|
@ -29,6 +29,6 @@ export const TUNABLES = {
|
||||||
NAVIGATE_ON_ASSET_IN_VIEW: getBoolean(storage.getItem('ASSET_GRID.NAVIGATE_ON_ASSET_IN_VIEW'), false),
|
NAVIGATE_ON_ASSET_IN_VIEW: getBoolean(storage.getItem('ASSET_GRID.NAVIGATE_ON_ASSET_IN_VIEW'), false),
|
||||||
},
|
},
|
||||||
IMAGE_THUMBNAIL: {
|
IMAGE_THUMBNAIL: {
|
||||||
THUMBHASH_FADE_DURATION: getNumber(storage.getItem('THUMBHASH_FADE_DURATION'), 100),
|
THUMBHASH_FADE_DURATION: getNumber(storage.getItem('THUMBHASH_FADE_DURATION'), 1000),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue