import { useQuery } from '@tanstack/react-query'; import { getTenantFonts, type TenantFont, type TenantFontVariant } from '../api'; const fontLoaders = new Map>(); export function useTenantFonts() { const { data, isLoading, isFetching, refetch } = useQuery({ queryKey: ['tenant', 'fonts'], queryFn: getTenantFonts, staleTime: 6 * 60 * 60 * 1000, }); return { fonts: data ?? [], isLoading: isLoading || isFetching, refetch, }; } export function ensureFontLoaded(font: TenantFont, preferred?: { weight?: number; style?: string }): Promise { if (typeof document === 'undefined' || typeof window === 'undefined') { return Promise.resolve(); } const variant = pickVariant(font, preferred); if (!variant) { return Promise.resolve(); } const key = `${font.family}-${variant.weight}-${variant.style}`; if (document.fonts?.check(`1rem "${font.family}"`)) { return Promise.resolve(); } if (! fontLoaders.has(key)) { const loader = new FontFace(font.family, `url(${variant.url})`, { weight: String(variant.weight ?? ''), style: variant.style ?? 'normal', display: 'swap', }) .load() .then((fontFace) => { document.fonts.add(fontFace); }) .catch((error) => { console.warn('[fonts] failed to load font', font.family, variant, error); fontLoaders.delete(key); }); fontLoaders.set(key, loader); } return fontLoaders.get(key) ?? Promise.resolve(); } function pickVariant(font: TenantFont, preferred?: { weight?: number; style?: string }): TenantFontVariant | null { const variants = font.variants ?? []; if (! variants.length) { return null; } if (preferred?.weight || preferred?.style) { const found = variants.find((variant) => { const matchesWeight = preferred.weight ? Number(variant.weight) === Number(preferred.weight) : true; const matchesStyle = preferred.style ? variant.style === preferred.style : true; return matchesWeight && matchesStyle; }); if (found) { return found; } } return variants[0]; }