From fd6c50015f5b8ab24894a6da000b331c049c4e73 Mon Sep 17 00:00:00 2001 From: Matthieu MALVACHE Date: Fri, 17 Apr 2026 18:35:07 +0200 Subject: [PATCH] fix(favicon): repaint base icon on zero unread so the badge clears Removing the dynamic favicon link left Chromium painting the last cached badged icon in the tab, so the "1" stuck around after reading the mail. Keep the link and overwrite its href with the plain base icon when the count drops to 0 to force a repaint. --- hooks/use-favicon-badge.ts | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/hooks/use-favicon-badge.ts b/hooks/use-favicon-badge.ts index 1b49bbe..ecf86e6 100644 --- a/hooks/use-favicon-badge.ts +++ b/hooks/use-favicon-badge.ts @@ -34,9 +34,7 @@ export function useFaviconBadge(count: number) { const img = new Image(); img.onload = () => { imageRef.current = img; - if (countRef.current > 0) { - applyBadge(canvas, img, countRef.current); - } + renderFavicon(canvas, img, countRef.current); }; img.src = reader.result as string; }; @@ -52,17 +50,36 @@ export function useFaviconBadge(count: number) { }, []); useEffect(() => { - if (count <= 0) { - clearDynamicFavicon(); - return; - } - - if (canvasRef.current) { - applyBadge(canvasRef.current, imageRef.current, count); + if (canvasRef.current && imageRef.current) { + renderFavicon(canvasRef.current, imageRef.current, count); } }, [count]); } +function renderFavicon( + canvas: HTMLCanvasElement, + image: HTMLImageElement, + count: number, +) { + if (count > 0) { + applyBadge(canvas, image, count); + } else { + // Paint the plain base icon into the dynamic link instead of removing + // it. Chromium caches the previously-painted badged favicon until a + // new href replaces it; simply removing the link leaves the stale + // badge visible until the tab is reloaded. + drawBase(canvas, image); + } +} + +function drawBase(canvas: HTMLCanvasElement, image: HTMLImageElement) { + const ctx = canvas.getContext("2d"); + if (!ctx) return; + ctx.clearRect(0, 0, SIZE, SIZE); + ctx.drawImage(image, 0, 0, SIZE, SIZE); + setDynamicFavicon(canvas.toDataURL("image/png")); +} + function applyBadge( canvas: HTMLCanvasElement, image: HTMLImageElement | null,