Files
bedolaga-cabinet/src/hooks/useBranding.ts
c0mrade 71047780c9 fix(favicon): скругляем углы кастомного лого как у плитки в хедере
Кастомное лого квадратное, и в фавиконе показывалось с острыми углами. Теперь
лого прогоняется через canvas со скруглённым клипом (radiusRatio 0.3 — как
rounded-linear-lg 12px на плитке 40px в хедере) и отдаётся скруглённым PNG.
При сбое canvas (taint/нет 2d-контекста) — фолбэк на сырой лого. Применяется и
в кабинете (useBranding), и на лендинге (QuickPurchase); буквенная монограмма
уже скруглена через rx.
2026-06-03 12:33:10 +03:00

88 lines
2.6 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useAuthStore } from '@/store/auth';
import { useTelegramSDK, setCachedFullscreenEnabled } from '@/hooks/useTelegramSDK';
import {
brandingApi,
getCachedBranding,
setCachedBranding,
preloadLogo,
isLogoPreloaded,
} from '@/api/branding';
import { setFavicon, letterFaviconDataUri, roundedFaviconDataUri } from '@/utils/favicon';
const FALLBACK_NAME = import.meta.env.VITE_APP_NAME || 'Cabinet';
const FALLBACK_LOGO = import.meta.env.VITE_APP_LOGO || 'V';
export function useBranding() {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const { isTelegramWebApp, requestFullscreen, isMobile } = useTelegramSDK();
// Branding data
const { data: branding } = useQuery({
queryKey: ['branding'],
queryFn: async () => {
const data = await brandingApi.getBranding();
setCachedBranding(data);
await preloadLogo(data);
return data;
},
initialData: getCachedBranding() ?? undefined,
initialDataUpdatedAt: 0,
staleTime: 60000,
enabled: isAuthenticated,
});
const appName = branding ? branding.name : FALLBACK_NAME;
const logoLetter = branding?.logo_letter || FALLBACK_LOGO;
const hasCustomLogo = branding?.has_custom_logo || false;
const logoUrl = branding ? brandingApi.getLogoUrl(branding) : null;
// Set document title
useEffect(() => {
document.title = appName || 'VPN';
}, [appName]);
// Update favicon — custom logo (rounded like the header tile) when available,
// else a brand-letter monogram so the tab always carries an icon.
useEffect(() => {
if (!logoUrl) {
setFavicon(letterFaviconDataUri(logoLetter));
return;
}
let cancelled = false;
roundedFaviconDataUri(logoUrl).then((rounded) => {
if (!cancelled) setFavicon(rounded || logoUrl);
});
return () => {
cancelled = true;
};
}, [logoUrl, logoLetter]);
// Fullscreen setting from server
const { data: fullscreenSetting } = useQuery({
queryKey: ['fullscreen-enabled'],
queryFn: brandingApi.getFullscreenEnabled,
staleTime: 60000,
});
const fullscreenRequestedRef = useRef(false);
useEffect(() => {
if (!fullscreenSetting || !isTelegramWebApp) return;
setCachedFullscreenEnabled(fullscreenSetting.enabled);
if (fullscreenSetting.enabled && isMobile && !fullscreenRequestedRef.current) {
fullscreenRequestedRef.current = true;
requestFullscreen();
}
}, [fullscreenSetting, isTelegramWebApp, requestFullscreen, isMobile]);
return {
appName,
logoLetter,
hasCustomLogo,
logoUrl,
isLogoPreloaded,
};
}