mirror of
https://github.com/immich-app/immich.git
synced 2025-05-31 04:05:39 -04:00
feat: show thumbhash behind load error, if possible (#17554)
* feat: show thumbhash behind load error, if possible * forgot this
This commit is contained in:
parent
40e3322b25
commit
c62fc155c8
@ -595,7 +595,7 @@
|
|||||||
id="stack-slideshow"
|
id="stack-slideshow"
|
||||||
class="z-[1002] flex place-item-center place-content-center absolute bottom-0 w-full col-span-4 col-start-1 overflow-x-auto horizontal-scrollbar"
|
class="z-[1002] flex place-item-center place-content-center absolute bottom-0 w-full col-span-4 col-start-1 overflow-x-auto horizontal-scrollbar"
|
||||||
>
|
>
|
||||||
<div class="relative w-full whitespace-nowrap">
|
<div class="relative w-full">
|
||||||
{#each stackedAssets as stackedAsset (stackedAsset.id)}
|
{#each stackedAssets as stackedAsset (stackedAsset.id)}
|
||||||
<div
|
<div
|
||||||
class={['inline-block px-1 relative transition-all pb-2']}
|
class={['inline-block px-1 relative transition-all pb-2']}
|
||||||
@ -603,6 +603,7 @@
|
|||||||
>
|
>
|
||||||
<Thumbnail
|
<Thumbnail
|
||||||
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
|
imageClass={{ 'border-2 border-white': stackedAsset.id === asset.id }}
|
||||||
|
brokenAssetClass="text-xs"
|
||||||
dimmed={stackedAsset.id !== asset.id}
|
dimmed={stackedAsset.id !== asset.id}
|
||||||
asset={stackedAsset}
|
asset={stackedAsset}
|
||||||
onClick={(stackedAsset) => {
|
onClick={(stackedAsset) => {
|
||||||
|
@ -184,7 +184,9 @@
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{#if imageError}
|
{#if imageError}
|
||||||
<BrokenAsset class="text-xl" />
|
<div class="h-full w-full">
|
||||||
|
<BrokenAsset class="text-xl h-full w-full" />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<!-- svelte-ignore a11y_missing_attribute -->
|
<!-- svelte-ignore a11y_missing_attribute -->
|
||||||
<img bind:this={loader} style="display:none" src={imageLoaderUrl} aria-hidden="true" />
|
<img bind:this={loader} style="display:none" src={imageLoaderUrl} aria-hidden="true" />
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex flex-col overflow-hidden max-h-full max-w-full justify-center items-center bg-gray-100 dark:bg-gray-700 dark:text-gray-100 p-4 {className}"
|
class="flex flex-col overflow-hidden max-h-full max-w-full justify-center items-center bg-gray-100/40 dark:bg-gray-700/40 dark:text-gray-100 p-4 {className}"
|
||||||
style:width
|
style:width
|
||||||
style:height
|
style:height
|
||||||
>
|
>
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
border?: boolean;
|
border?: boolean;
|
||||||
hiddenIconClass?: string;
|
hiddenIconClass?: string;
|
||||||
class?: ClassValue;
|
class?: ClassValue;
|
||||||
onComplete?: (() => void) | undefined;
|
brokenAssetClass?: ClassValue;
|
||||||
|
onComplete?: ((errored: boolean) => void) | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@ -39,6 +40,7 @@
|
|||||||
hiddenIconClass = 'text-white',
|
hiddenIconClass = 'text-white',
|
||||||
onComplete = undefined,
|
onComplete = undefined,
|
||||||
class: imageClass = '',
|
class: imageClass = '',
|
||||||
|
brokenAssetClass = '',
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@ -50,17 +52,17 @@
|
|||||||
|
|
||||||
const setLoaded = () => {
|
const setLoaded = () => {
|
||||||
loaded = true;
|
loaded = true;
|
||||||
onComplete?.();
|
onComplete?.(false);
|
||||||
};
|
};
|
||||||
const setErrored = () => {
|
const setErrored = () => {
|
||||||
errored = true;
|
errored = true;
|
||||||
onComplete?.();
|
onComplete?.(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
function mount(elem: HTMLImageElement) {
|
function mount(elem: HTMLImageElement) {
|
||||||
if (elem.complete) {
|
if (elem.complete) {
|
||||||
loaded = true;
|
loaded = true;
|
||||||
onComplete?.();
|
onComplete?.(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,6 +73,7 @@
|
|||||||
shadow && 'shadow-lg',
|
shadow && 'shadow-lg',
|
||||||
(circle || !heightStyle) && 'aspect-square',
|
(circle || !heightStyle) && 'aspect-square',
|
||||||
border && 'border-[3px] border-immich-dark-primary/80 hover:border-immich-primary',
|
border && 'border-[3px] border-immich-dark-primary/80 hover:border-immich-primary',
|
||||||
|
brokenAssetClass,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(' '),
|
.join(' '),
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
showArchiveIcon?: boolean;
|
showArchiveIcon?: boolean;
|
||||||
showStackedIcon?: boolean;
|
showStackedIcon?: boolean;
|
||||||
imageClass?: ClassValue;
|
imageClass?: ClassValue;
|
||||||
|
brokenAssetClass?: ClassValue;
|
||||||
dimmed?: boolean;
|
dimmed?: boolean;
|
||||||
onClick?: ((asset: AssetResponseDto) => void) | undefined;
|
onClick?: ((asset: AssetResponseDto) => void) | undefined;
|
||||||
onSelect?: ((asset: AssetResponseDto) => void) | undefined;
|
onSelect?: ((asset: AssetResponseDto) => void) | undefined;
|
||||||
@ -66,6 +67,7 @@
|
|||||||
onMouseEvent = undefined,
|
onMouseEvent = undefined,
|
||||||
handleFocus = undefined,
|
handleFocus = undefined,
|
||||||
imageClass = '',
|
imageClass = '',
|
||||||
|
brokenAssetClass = '',
|
||||||
dimmed = false,
|
dimmed = false,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@ -77,6 +79,7 @@
|
|||||||
let focussableElement: HTMLElement | undefined = $state();
|
let focussableElement: HTMLElement | undefined = $state();
|
||||||
let mouseOver = $state(false);
|
let mouseOver = $state(false);
|
||||||
let loaded = $state(false);
|
let loaded = $state(false);
|
||||||
|
let thumbError = $state(false);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (focussed && document.activeElement !== focussableElement) {
|
if (focussed && document.activeElement !== focussableElement) {
|
||||||
@ -153,10 +156,10 @@
|
|||||||
style:width="{width}px"
|
style:width="{width}px"
|
||||||
style:height="{height}px"
|
style:height="{height}px"
|
||||||
>
|
>
|
||||||
{#if !loaded && asset.thumbhash}
|
{#if (!loaded || thumbError) && asset.thumbhash}
|
||||||
<canvas
|
<canvas
|
||||||
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
|
use:thumbhash={{ base64ThumbHash: asset.thumbhash }}
|
||||||
class="absolute object-cover z-10"
|
class="absolute object-cover"
|
||||||
style:width="{width}px"
|
style:width="{width}px"
|
||||||
style:height="{height}px"
|
style:height="{height}px"
|
||||||
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
|
out:fade={{ duration: THUMBHASH_FADE_DURATION }}
|
||||||
@ -296,12 +299,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<ImageThumbnail
|
<ImageThumbnail
|
||||||
class={imageClass}
|
class={imageClass}
|
||||||
|
{brokenAssetClass}
|
||||||
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
||||||
altText={$getAltText(asset)}
|
altText={$getAltText(asset)}
|
||||||
widthStyle="{width}px"
|
widthStyle="{width}px"
|
||||||
heightStyle="{height}px"
|
heightStyle="{height}px"
|
||||||
curve={selected}
|
curve={selected}
|
||||||
onComplete={() => (loaded = true)}
|
onComplete={(errored) => ((loaded = true), (thumbError = errored))}
|
||||||
/>
|
/>
|
||||||
{#if asset.type === AssetTypeEnum.Video}
|
{#if asset.type === AssetTypeEnum.Video}
|
||||||
<div class="absolute top-0 h-full w-full">
|
<div class="absolute top-0 h-full w-full">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user