mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
feat: add favicon status hook
This commit is contained in:
98
packages/app/src/hooks/use-favicon-status.ts
Normal file
98
packages/app/src/hooks/use-favicon-status.ts
Normal 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]);
|
||||
}
|
||||
Reference in New Issue
Block a user