ai-chatbot/components/favicon-updater.tsx
2026-06-22 12:00:52 +08:00

87 lines
2.5 KiB
TypeScript

"use client";
import { useEffect } from "react";
export function FaviconUpdater() {
useEffect(() => {
let isDisposed = false;
const mediaQuery = window.matchMedia?.("(prefers-color-scheme: dark)");
const getFaviconUrl = () => {
const html = document.documentElement;
const hasDarkClass = html.classList.contains("dark");
const hasLightClass = html.classList.contains("light");
const prefersDark = Boolean(mediaQuery?.matches);
const isDark = hasDarkClass || (!hasLightClass && prefersDark);
return isDark ? "/api/favicon?theme=dark" : "/api/favicon";
};
const updateFavicon = () => {
if (isDisposed) return;
const urlWithTheme = getFaviconUrl();
const iconType = urlWithTheme.includes("theme=dark")
? "image/png"
: "image/x-icon";
const href =
`${urlWithTheme}${urlWithTheme.includes("?") ? "&" : "?"}v=${Date.now()}`;
const managedSelector =
'link[rel="icon"],link[rel="shortcut icon"],link[rel="apple-touch-icon"]';
const existingLinks = document.querySelectorAll<HTMLLinkElement>(
managedSelector
);
existingLinks.forEach((link) => {
link.href = href;
link.setAttribute("data-managed-favicon", "true");
if (link.rel === "icon" || link.rel === "shortcut icon") {
link.type = iconType;
link.setAttribute("sizes", "any");
}
});
const ensureLink = (rel: string, type?: string) => {
let link = document.querySelector(
`link[rel="${rel}"]`
) as HTMLLinkElement | null;
if (!link) {
link = document.createElement("link");
link.rel = rel;
document.head.appendChild(link);
}
link.href = href;
link.setAttribute("data-managed-favicon", "true");
if (type) {
link.type = type;
}
if (rel === "icon") {
link.setAttribute("sizes", "any");
}
};
ensureLink("icon", iconType);
ensureLink("apple-touch-icon");
ensureLink("shortcut icon", iconType);
};
const observer = new MutationObserver(updateFavicon);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"],
});
mediaQuery?.addEventListener?.("change", updateFavicon);
Promise.resolve().then(() => {
updateFavicon();
});
return () => {
isDisposed = true;
observer.disconnect();
mediaQuery?.removeEventListener?.("change", updateFavicon);
};
}, []);
return null;
}