fix(url-preview): thumbnail fallback + quiet timeline console spam

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 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 23:47:19 -04:00
co-authored by Claude Opus 4.8
parent 398e743cdd
commit 53ce2e40a4
2 changed files with 32 additions and 7 deletions
@@ -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 && (
<UrlPreviewImg
src={thumbUrl}
src={displayThumb}
alt={prev['og:title']}
title={prev['og:title']}
tabIndex={0}
onKeyDown={(evt) => onEnterOrSpace(() => onOpenViewer())(evt)}
onClick={onOpenViewer}
onError={handleImgError}
/>
)}
{imgUrl && (
@@ -1997,7 +2010,7 @@ function GenericCard({
size="T200"
priority="300"
>
{!thumbUrl && (
{(!displayThumb || imgFailed) && (
<Icon
src={Icons.Link}
size="50"
+16 -4
View File
@@ -68,13 +68,25 @@ window.addEventListener('vite:preloadError', () => {
// 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);
};
}