diff --git a/src/shared/components/image/image.tsx b/src/shared/components/image/image.tsx index 36a79106c..7d1b73574 100644 --- a/src/shared/components/image/image.tsx +++ b/src/shared/components/image/image.tsx @@ -26,7 +26,7 @@ export interface ImageProps extends Omit, 's imageContainerProps?: Omit; includeLoader?: boolean; includeUnloader?: boolean; - src: string | string[] | undefined; + src: string | undefined; thumbHash?: string; unloaderIcon?: keyof typeof AppIcon; } @@ -147,20 +147,31 @@ function ImageWithDebounce({ const hasBeenInViewportRef = useRef(false); const prevDebouncedSrcRef = useRef(debouncedSrc); + const srcInDisplayedCache = isInDisplayedCache(src); + + if (srcInDisplayedCache) { + hasBeenInViewportRef.current = true; + } + if (prevDebouncedSrcRef.current !== debouncedSrc) { prevDebouncedSrcRef.current = debouncedSrc; - hasBeenInViewportRef.current = false; + if (!srcInDisplayedCache) hasBeenInViewportRef.current = false; } if (inViewport && debouncedSrc) { hasBeenInViewportRef.current = true; } + const effectiveSrc = debouncedSrc ?? (srcInDisplayedCache ? src : undefined); const shouldShowImage = enableViewport - ? (inViewport || hasBeenInViewportRef.current) && debouncedSrc - : debouncedSrc; + ? (inViewport || hasBeenInViewportRef.current) && effectiveSrc + : effectiveSrc; if (enableViewport) { + if (shouldShowImage && effectiveSrc) { + addToDisplayedCache(effectiveSrc); + } + return ( - {shouldShowImage && debouncedSrc ? ( + {shouldShowImage && effectiveSrc ? ( : null} - src={debouncedSrc} + src={effectiveSrc} unloader={ includeUnloader ? ( @@ -193,13 +204,14 @@ function ImageWithDebounce({ ); } + if (effectiveSrc) addToDisplayedCache(effectiveSrc); return ( - {debouncedSrc ? ( + {effectiveSrc ? ( : null} - src={debouncedSrc} + src={effectiveSrc} unloader={ includeUnloader ? ( @@ -242,9 +254,14 @@ function ImageWithViewport({ const hasBeenInViewportRef = useRef(false); const prevSrcRef = useRef(src); + const srcInDisplayedCache = isInDisplayedCache(src); + if (srcInDisplayedCache) { + hasBeenInViewportRef.current = true; + } + if (prevSrcRef.current !== src) { prevSrcRef.current = src; - hasBeenInViewportRef.current = false; + if (!srcInDisplayedCache) hasBeenInViewportRef.current = false; } if (inViewport && src) { @@ -253,6 +270,7 @@ function ImageWithViewport({ const shouldShowImage = (inViewport || hasBeenInViewportRef.current) && src; + if (shouldShowImage && src) addToDisplayedCache(src); return ( = MAX_DISPLAYED_SRC_CACHE) { + cache.shift(); + } + + cache.push(src); + sessionStorage.setItem(DISPLAYED_SRC_CACHE_KEY, JSON.stringify(cache)); + } catch { + // ignore error if sessionStorage is unavailable + } +} + +function getDisplayedSrcCache(): string[] { + try { + const raw = sessionStorage.getItem(DISPLAYED_SRC_CACHE_KEY); + return raw ? (JSON.parse(raw) as string[]) : []; + } catch { + return []; + } +} + +function isInDisplayedCache(src: string | undefined): boolean { + if (!src) return false; + try { + return getDisplayedSrcCache().includes(src); + } catch { + return false; + } +} + export const Image = memo(BaseImage); const ImageContainer = forwardRef(