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); }; }