From 53ce2e40a4308123c2af72482f89d6db246d665b Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Thu, 9 Jul 2026 23:47:19 -0400 Subject: [PATCH] fix(url-preview): thumbnail fallback + quiet timeline console spam MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two console-noise / glitch fixes surfaced while a room with link previews was open: - URL preview og:image thumbnails 400 when Synapse can't thumbnail a cached preview image (SVG/animated), leaving a broken image that the browser keeps re-requesting. GenericCard now falls back to the full image on error, then hides the image (and shows the link icon) if that also fails, so no broken image and no repeated failing requests. - Extend the existing console.warn filter to drop matrix-js-sdk's high-volume, benign timeline bookkeeping warnings ("EventTimelineSet…" and "Decrypted event … is not in room …"), which fire constantly in E2EE rooms with threads. Real warnings still log. Co-Authored-By: Claude Opus 4.8 --- .../components/url-preview/UrlPreviewCard.tsx | 19 +++++++++++++++--- src/index.tsx | 20 +++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/app/components/url-preview/UrlPreviewCard.tsx b/src/app/components/url-preview/UrlPreviewCard.tsx index c408bf3e9..d5e1ddf6b 100644 --- a/src/app/components/url-preview/UrlPreviewCard.tsx +++ b/src/app/components/url-preview/UrlPreviewCard.tsx @@ -1965,16 +1965,29 @@ function GenericCard({ const description = prev['og:description'] ?? ''; const siteName = typeof prev['og:site_name'] === 'string' ? prev['og:site_name'] : undefined; + // Synapse returns 400 from the thumbnail endpoint when it can't thumbnail a + // cached preview image (e.g. SVG/animated). Fall back to the full image, then + // hide entirely if that also fails — otherwise the card shows a broken image + // and the browser keeps re-requesting the failing thumbnail (console spam). + const [useFullImg, setUseFullImg] = useState(false); + const [imgFailed, setImgFailed] = useState(false); + const displayThumb = useFullImg ? imgUrl : thumbUrl; + const handleImgError = () => { + if (!useFullImg && imgUrl && imgUrl !== thumbUrl) setUseFullImg(true); + else setImgFailed(true); + }; + return ( <> - {thumbUrl && ( + {!imgFailed && displayThumb && ( onEnterOrSpace(() => onOpenViewer())(evt)} onClick={onOpenViewer} + onError={handleImgError} /> )} {imgUrl && ( @@ -1997,7 +2010,7 @@ function GenericCard({ size="T200" priority="300" > - {!thumbUrl && ( + {(!displayThumb || imgFailed) && ( { // Clear the reload flag after a successful load so future deploys can still trigger a reload window.addEventListener('load', () => sessionStorage.removeItem('chunk-reload-attempted')); -// Synapse does not yet ship MSC3786/MSC3914 as server-default push rules. -// matrix-js-sdk patches them client-side on every login and logs a warn for each. -// Suppress the noise until Synapse implements these MSCs upstream. +// Filter out known-benign, high-volume matrix-js-sdk console warnings that we +// can't fix client-side and that would otherwise flood the console: +// - "Adding default global …": the SDK patches MSC3786/MSC3914 push rules on +// every login (one warn each) until Synapse ships them as server defaults. +// - "EventTimelineSet…": the SDK loudly warns whenever a decrypted event +// references a thread/room the current timeline set doesn't hold, then +// discards it harmlessly. This fires constantly in E2EE rooms with threads. +// - "Decrypted event … is not in room …": same family — a late decryption for +// an event the room's timeline no longer tracks; ignored by the SDK. +// These are informational SDK bookkeeping, not errors; real warnings still log. { + const suppressedPrefixes = ['Adding default global ', 'EventTimelineSet']; const _warn = console.warn.bind(console); console.warn = (...args: unknown[]) => { - if (typeof args[0] === 'string' && args[0].startsWith('Adding default global ')) return; + const first = args[0]; + if (typeof first === 'string') { + if (suppressedPrefixes.some((p) => first.startsWith(p))) return; + if (first.startsWith('Decrypted event ') && first.includes('is not in room')) return; + } _warn(...args); }; }