From 79d7c858325e3ab189ddc525937bbf5bd30f7cdf Mon Sep 17 00:00:00 2001 From: Hampus Date: Sat, 3 Oct 2026 02:14:28 +0200 Subject: [PATCH] fix(app): retry emoji picker images that fail to load (#3151) --- .../components/emoji_picker/EmojiRenderer.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/fluxer_app/src/features/channel/components/emoji_picker/EmojiRenderer.tsx b/fluxer_app/src/features/channel/components/emoji_picker/EmojiRenderer.tsx index bf933a9ce..3637e8a5c 100644 --- a/fluxer_app/src/features/channel/components/emoji_picker/EmojiRenderer.tsx +++ b/fluxer_app/src/features/channel/components/emoji_picker/EmojiRenderer.tsx @@ -11,6 +11,7 @@ import type {FlatEmoji} from '@app/features/emoji/types/EmojiTypes'; import {checkEmojiAvailability} from '@app/features/expressions/utils/ExpressionPermissionUtils'; import {getEmojiDisplayDataWithSkinTone} from '@app/features/expressions/utils/SkinToneUtils'; import UnicodeEmojis, {EMOJI_SPRITES} from '@app/features/expressions/utils/UnicodeEmojis'; +import {loadImage} from '@app/features/messaging/utils/ImageCacheUtils'; import {getEmojiRenderUrl} from '@app/features/messaging/utils/markdown/EmojiDetector'; import {EmojiContextMenuItems} from '@app/features/ui/action_menu/items/EmojiContextMenuItems'; import * as ContextMenuCommands from '@app/features/ui/commands/ContextMenuCommands'; @@ -25,9 +26,34 @@ type PickerEmojiImageProps = React.ImgHTMLAttributes & { alt: string; }; +const PICKER_IMAGE_RETRY_LIMIT = 3; + const PickerEmojiImage = ({src, alt, ...props}: PickerEmojiImageProps) => { const imageRef = useRef(null); const hasLoadedRef = useRef(false); + const retriesRef = useRef(0); + const cancelRetryRef = useRef<(() => void) | null>(null); + useEffect(() => { + retriesRef.current = 0; + return () => { + cancelRetryRef.current?.(); + cancelRetryRef.current = null; + }; + }, [src]); + const handleError = () => { + if (retriesRef.current >= PICKER_IMAGE_RETRY_LIMIT) { + return; + } + retriesRef.current += 1; + cancelRetryRef.current?.(); + cancelRetryRef.current = loadImage(src, () => { + cancelRetryRef.current = null; + const image = imageRef.current; + if (image != null && image.getAttribute('src') === src) { + image.src = src; + } + }); + }; const handleLoad = (event: React.SyntheticEvent) => { const view = event.currentTarget?.ownerDocument?.defaultView ?? window; view.requestAnimationFrame(() => { @@ -47,6 +73,7 @@ const PickerEmojiImage = ({src, alt, ...props}: PickerEmojiImageProps) => { alt={alt} className={hasLoadedRef.current ? styles.emojiImage : clsx(styles.emojiImage, styles.emojiImageLoading)} onLoad={hasLoadedRef.current ? undefined : handleLoad} + onError={hasLoadedRef.current ? undefined : handleError} /> ); };