From d2d199e13622883892b6453d20db87a05bca5fd7 Mon Sep 17 00:00:00 2001 From: Hampus Date: Mon, 24 Aug 2026 12:34:04 +0200 Subject: [PATCH] perf(app): fetch text attachment previews only when they scroll near (#1865) --- .../components/embeds/attachments/AttachmentFile.tsx | 4 +++- .../embeds/attachments/TextualAttachmentPreview.tsx | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/fluxer_app/src/features/channel/components/embeds/attachments/AttachmentFile.tsx b/fluxer_app/src/features/channel/components/embeds/attachments/AttachmentFile.tsx index 1e47e39eb..2c6eb4541 100644 --- a/fluxer_app/src/features/channel/components/embeds/attachments/AttachmentFile.tsx +++ b/fluxer_app/src/features/channel/components/embeds/attachments/AttachmentFile.tsx @@ -48,9 +48,10 @@ interface AttachmentFileProps { attachment: MessageAttachment; isPreview?: boolean; message?: Message; + spoilerHidden?: boolean; } -export const AttachmentFile = observer(({attachment, message, isPreview}: AttachmentFileProps) => { +export const AttachmentFile = observer(({attachment, message, isPreview, spoilerHidden}: AttachmentFileProps) => { const {i18n} = useLingui(); const {enabled: isMobile} = MobileLayout; const isExpired = Boolean(attachment.expired); @@ -247,6 +248,7 @@ export const AttachmentFile = observer(({attachment, message, isPreview}: Attach {showTextPreview ? ( ) : ( diff --git a/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreview.tsx b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreview.tsx index 3ee2d698b..c380ef248 100644 --- a/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreview.tsx +++ b/fluxer_app/src/features/channel/components/embeds/attachments/TextualAttachmentPreview.tsx @@ -26,6 +26,7 @@ import { } from '@app/features/channel/components/embeds/attachments/TextualAttachmentPreviewUtils'; import {TextualPreviewContextMenu} from '@app/features/channel/components/embeds/attachments/TextualPreviewContextMenu'; import {useArboriumHighlightedHtml} from '@app/features/code_highlighting/utils/ArboriumHighlighting'; +import {useNearViewport} from '@app/features/messaging/hooks/useNearViewport'; import TextualPreview from '@app/features/messaging/state/TextualPreview'; import { shouldPreviewAttachment, @@ -93,9 +94,12 @@ async function readPreviewText(response: Response): Promise { export const TextualAttachmentPreview = observer(function TextualAttachmentPreview({ attachment, -}: TextualAttachmentPreviewProps) { + spoilerHidden = false, +}: TextualAttachmentPreviewProps & {spoilerHidden?: boolean}) { const {i18n} = useLingui(); const shouldShowPreview = shouldPreviewAttachment(attachment); + const {ref: visibilityRef, isNearViewport} = useNearViewport({rememberKey: attachment.url}); + const shouldFetchPreview = shouldShowPreview && isNearViewport && !spoilerHidden; const isCsvPreview = useMemo( () => isCsvAttachment(attachment), [attachment.content_type, attachment.filename, attachment.title], @@ -123,6 +127,9 @@ export const TextualAttachmentPreview = observer(function TextualAttachmentPrevi setIsFullscreenOpen(false); }, [attachment.id]); useEffect(() => { + if (!shouldFetchPreview) { + return; + } if (!attachment.url) { setStatus('error'); setPreviewError({type: 'network'}); @@ -164,7 +171,7 @@ export const TextualAttachmentPreview = observer(function TextualAttachmentPrevi setPreviewError({type: 'network', message: error?.message ?? 'Failed to load preview'}); }); return () => controller.abort(); - }, [attachment.id, attachment.size, attachment.url]); + }, [attachment.id, attachment.size, attachment.url, shouldFetchPreview]); const lineCount = useMemo(() => getLineCount(textContent), [textContent]); const csvRows = useMemo(() => (isCsvPreview ? parseCsvRows(textContent) : null), [isCsvPreview, textContent]); const csvRowCount = csvRows?.length ?? 0; @@ -282,6 +289,7 @@ export const TextualAttachmentPreview = observer(function TextualAttachmentPrevi return ( <>