feat: add favicon status hook

This commit is contained in:
Mohamed Boudra
2026-01-14 19:19:04 +07:00
parent 83b989124e
commit 6ac2ef7366

View File

@@ -0,0 +1,98 @@
import { useEffect, useState } from "react";
import { Platform } from "react-native";
import { useAggregatedAgents } from "./use-aggregated-agents";
type FaviconStatus = "none" | "running" | "attention";
type ColorScheme = "dark" | "light";
/* eslint-disable @typescript-eslint/no-require-imports */
const FAVICON_IMAGES: Record<ColorScheme, Record<FaviconStatus, { uri: string } | number>> = {
dark: {
none: require("../../assets/images/favicon-dark.png"),
running: require("../../assets/images/favicon-dark-running.png"),
attention: require("../../assets/images/favicon-dark-attention.png"),
},
light: {
none: require("../../assets/images/favicon-light.png"),
running: require("../../assets/images/favicon-light-running.png"),
attention: require("../../assets/images/favicon-light-attention.png"),
},
};
/* eslint-enable @typescript-eslint/no-require-imports */
function deriveFaviconStatus(
agents: ReturnType<typeof useAggregatedAgents>["agents"]
): FaviconStatus {
const hasRunning = agents.some((agent) => agent.status === "running");
if (hasRunning) {
return "running";
}
const hasAttention = agents.some((agent) => agent.requiresAttention);
if (hasAttention) {
return "attention";
}
return "none";
}
function getFaviconUri(status: FaviconStatus, colorScheme: ColorScheme): string {
const image = FAVICON_IMAGES[colorScheme][status];
if (typeof image === "object" && "uri" in image) {
return image.uri;
}
const suffix = status === "none" ? "" : `-${status}`;
return `/assets/images/favicon-${colorScheme}${suffix}.png`;
}
function getOrCreateFaviconLink(): HTMLLinkElement | null {
if (typeof document === "undefined") return null;
let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
if (!link) {
link = document.createElement("link");
link.rel = "icon";
link.type = "image/png";
document.head.appendChild(link);
}
return link;
}
function updateFavicon(status: FaviconStatus, colorScheme: ColorScheme) {
const link = getOrCreateFaviconLink();
if (!link) return;
const newHref = getFaviconUri(status, colorScheme);
if (link.href !== newHref) {
link.href = newHref;
}
}
function getSystemColorScheme(): ColorScheme {
if (typeof window === "undefined") return "dark";
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
export function useFaviconStatus() {
const { agents } = useAggregatedAgents();
const [colorScheme, setColorScheme] = useState<ColorScheme>(getSystemColorScheme);
// Listen for system color scheme changes
useEffect(() => {
if (Platform.OS !== "web" || typeof window === "undefined") return;
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handler = (e: MediaQueryListEvent) => {
setColorScheme(e.matches ? "dark" : "light");
};
mediaQuery.addEventListener("change", handler);
return () => mediaQuery.removeEventListener("change", handler);
}, []);
// Update favicon when agents or color scheme changes
useEffect(() => {
if (Platform.OS !== "web") return;
const status = deriveFaviconStatus(agents);
updateFavicon(status, colorScheme);
}, [agents, colorScheme]);
}