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.
This commit is contained in:
Matthieu MALVACHE
2026-04-17 18:35:07 +02:00
parent 885e9504fe
commit fd6c50015f
+27 -10
View File
@@ -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,