From 6ccab8330c722ea262defce090551b2553b32d9b Mon Sep 17 00:00:00 2001 From: Schalli Date: Tue, 6 Oct 2026 11:53:18 +0200 Subject: [PATCH] fix(web): Favoriten-Platzhalterbuchstabe verschwindet, sobald das Logo geladen ist Bei Logos mit durchsichtigem Hintergrund schien der Buchstabe durch. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + .../widgets/favorites-widget.test.tsx | 19 ++++++++++++ .../dashboard/widgets/favorites-widget.tsx | 31 +++++++++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b4411d..2de40f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T ### Behoben +- Favoriten: Bei Logos mit durchsichtigem Hintergrund schien der graue Platzhalter-Buchstabe durch. Er verschwindet jetzt, sobald das Logo geladen ist. - Linux-App: Links, die in einem neuen Fenster aufgehen (zum Beispiel aus Notizen oder Favoriten), übergibt die App jetzt an das Öffnen-Programm Ihres Systems statt an eine mitgebrachte Kopie. Auf manchen Systemen öffnete sich dadurch vorher kein Browser. ## 1.10.0 – 2026-10-06 diff --git a/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx b/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx index 5393306..71119ad 100644 --- a/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx +++ b/apps/web/src/components/dashboard/widgets/favorites-widget.test.tsx @@ -414,6 +414,25 @@ describe('FavoritesWidget', () => { expect(screen.getByTestId('letter-fallback-fav-id-1')).toHaveTextContent('G'); }); + it('06.10.: geladenes Logo blendet den Buchstaben aus (durchsichtige Logos), Fehler zeigt ihn wieder', async () => { + render(); + await waitFor(() => { + expect(screen.getByText('GitHub')).toBeInTheDocument(); + }); + const proxyImg = screen.getByTestId('icon-proxy-fav-id-1') as HTMLImageElement; + expect(screen.getByTestId('letter-fallback-fav-id-1')).toBeInTheDocument(); + + act(() => { + fireEvent.load(proxyImg); + }); + expect(screen.queryByTestId('letter-fallback-fav-id-1')).not.toBeInTheDocument(); + + act(() => { + fireEvent.error(proxyImg); + }); + expect(screen.getByTestId('letter-fallback-fav-id-1')).toHaveTextContent('G'); + }); + it('ausdrueckliche Symbol-Adresse (260929-lh3): Proxy -> Browser laedt iconUrl direkt (no-referrer) -> Origin-Favicon -> Buchstabe', async () => { mockFetch.mockResolvedValue([ { diff --git a/apps/web/src/components/dashboard/widgets/favorites-widget.tsx b/apps/web/src/components/dashboard/widgets/favorites-widget.tsx index a14d5f5..3d3a1ee 100644 --- a/apps/web/src/components/dashboard/widgets/favorites-widget.tsx +++ b/apps/web/src/components/dashboard/widgets/favorites-widget.tsx @@ -543,7 +543,9 @@ function getDirectCandidates(iconUrl: string | null, url: string): string[] { * Browser aber das Bild), dann `{origin}/favicon.ico` (entfaellt, wenn * identisch). Erreicht auch interne Hosts, die der SSRF-Schutz des * Servers absichtlich ablehnt. - * 3. `none` — der Buchstaben-Platzhalter liegt IMMER darunter. + * 3. `none` — der Buchstaben-Platzhalter steht da, solange kein Bild + * geladen ist. Sobald ein Bild geladen ist, verschwindet er: bei Logos mit + * durchsichtigem Hintergrund schien er sonst durch (06.10.2026). * * Bewusst KEIN Drittanbieter-Favicon-Dienst: der wuerde Hostnamen nach * aussen geben und interne Hosts ohnehin nicht kennen. @@ -590,15 +592,18 @@ function FavoriteIcon({ // 'proxy' | Index in directSrcs | Ende der Kette (Buchstabe). const [stage, setStage] = useState<'proxy' | number>(proxySrc ? 'proxy' : 0); const directSrc = typeof stage === 'number' ? (directSrcs[stage] ?? null) : null; + const [imageLoaded, setImageLoaded] = useState(false); return (
- - {getFallbackLetter(fav.title)} - + {!imageLoaded && ( + + {getFallbackLetter(fav.title)} + + )} {stage === 'proxy' && proxySrc && ( setStage(0)} + onLoad={() => setImageLoaded(true)} + onError={() => { + setImageLoaded(false); + setStage(0); + }} /> )} {directSrc && ( @@ -623,7 +632,11 @@ function FavoriteIcon({ loading="lazy" referrerPolicy="no-referrer" className={`relative rounded-sm ${img}`} - onError={() => setStage((prev) => (typeof prev === 'number' ? prev + 1 : 0))} + onLoad={() => setImageLoaded(true)} + onError={() => { + setImageLoaded(false); + setStage((prev) => (typeof prev === 'number' ? prev + 1 : 0)); + }} /> )}