diff --git a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte index 67b5e34736..d4b2c09517 100644 --- a/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/navigation-bar.svelte @@ -2,6 +2,7 @@ import { goto } from '$app/navigation'; import { page } from '$app/stores'; import { clickOutside } from '$lib/utils/click-outside'; + import { imageLoad } from '$lib/utils/image-load'; import { createEventDispatcher } from 'svelte'; import { fade, fly } from 'svelte/transition'; import TrayArrowUp from 'svelte-material-icons/TrayArrowUp.svelte'; @@ -124,13 +125,13 @@ > {#if user.profileImagePath} profile-img (showProfilePictureFallback = false)} + use:imageLoad + on:image-load={() => (showProfilePictureFallback = false)} /> {/if} {#if showProfilePictureFallback} diff --git a/web/src/lib/utils/image-load.ts b/web/src/lib/utils/image-load.ts new file mode 100644 index 0000000000..a3a7858713 --- /dev/null +++ b/web/src/lib/utils/image-load.ts @@ -0,0 +1,38 @@ +import { tick } from 'svelte'; +import type { ActionReturn } from 'svelte/action'; + +interface Attributes { + 'on:image-error'?: (e: CustomEvent) => void; + 'on:image-load'?: (e: CustomEvent) => void; +} + +export function imageLoad(img: HTMLImageElement): ActionReturn { + const onImageError = () => img.dispatchEvent(new CustomEvent('image-error')); + const onImageLoaded = () => img.dispatchEvent(new CustomEvent('image-load')); + + if (img.complete) { + // Browser has fetched the image, naturalHeight is used to check + // if any loading errors have occurred. + const loadingError = img.naturalHeight === 0; + + // Report status after a tick, to make sure event listeners are registered. + if (loadingError) { + tick().then(onImageError); + } else { + tick().then(onImageLoaded); + } + + return {}; + } + + // Image has not been loaded yet, report status with event listeners. + img.addEventListener('load', onImageLoaded, { once: true }); + img.addEventListener('error', onImageError, { once: true }); + + return { + destroy() { + img.removeEventListener('load', onImageLoaded); + img.removeEventListener('error', onImageError); + } + }; +}