From b364c53c61911124c25fce32ded3b17250169e3c Mon Sep 17 00:00:00 2001 From: byGalax Date: Tue, 2 Jun 2026 23:02:07 +0200 Subject: [PATCH] fix(desktop): degrade missing avatar image to letter circle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avatar rendered a bare with no error handling, so an avatar_url whose storage object is unreachable (e.g. a 404 after the server move) showed a broken image instead of the coloured letter-circle fallback. Track an onError flag and fall back to the circle; reset it when the URL changes so a fresh valid avatar is retried. This is client-side resilience only — it does not restore a genuinely missing storage object. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/desktop/src/components/Avatar.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/components/Avatar.tsx b/apps/desktop/src/components/Avatar.tsx index 8a18963..edb14e0 100644 --- a/apps/desktop/src/components/Avatar.tsx +++ b/apps/desktop/src/components/Avatar.tsx @@ -1,6 +1,8 @@ // Reusable avatar that prefers an uploaded image and falls back to a coloured // letter circle. Use this everywhere the app needs to render a profile. +import { useEffect, useState } from 'react'; + import { useCachedAvatarUrl } from '../lib/avatarCache'; interface Props { @@ -27,7 +29,13 @@ export function Avatar({ loading = 'lazy', }: Props) { const effectiveUrl = useCachedAvatarUrl(url); - if (effectiveUrl) { + // If the image URL is non-empty but unreachable (e.g. the storage object is + // missing / 404s), the bare would render broken with no fallback. + // Track a load error and degrade to the letter circle instead. Reset on URL + // change so a fresh, valid avatar is retried. + const [failed, setFailed] = useState(false); + useEffect(() => setFailed(false), [effectiveUrl]); + if (effectiveUrl && !failed) { return ( setFailed(true)} /> ); }