import { useEffect, useSyncExternalStore } from 'react'; import { tg } from './telegram.ts'; /** * Минимальный hash-роутер: Mini App открывается по одному URL, а hash переживает * перезагрузку WebView и не требует настройки сервера. */ function subscribe(cb: () => void) { window.addEventListener('hashchange', cb); return () => window.removeEventListener('hashchange', cb); } // Telegram открывает Mini App с #tgWebAppData=… — это не маршрут, а параметры запуска const snapshot = () => { const h = window.location.hash.replace(/^#/, ''); return h.startsWith('/') ? h : '/'; }; export function useLocation() { const raw = useSyncExternalStore(subscribe, snapshot, () => '/'); const [path, search = ''] = raw.split('?'); return { path: path || '/', query: new URLSearchParams(search) }; } export function navigate(to: string, opts: { replace?: boolean } = {}) { if (opts.replace) window.location.replace(`#${to}`); else window.location.hash = to; } /** Сопоставление '/orders/:id' → { id } */ export function match(pattern: string, path: string): Record | null { const p = pattern.split('/').filter(Boolean); const a = path.split('/').filter(Boolean); if (p.length !== a.length) return null; const params: Record = {}; for (let i = 0; i < p.length; i++) { if (p[i]!.startsWith(':')) params[p[i]!.slice(1)] = decodeURIComponent(a[i]!); else if (p[i] !== a[i]) return null; } return params; } /** Кнопка «Назад» Telegram на всех экранах, кроме корневых */ export function useTelegramBack(path: string, roots: string[]) { useEffect(() => { const back = tg()?.BackButton; if (!back) return; const onBack = () => window.history.back(); if (roots.includes(path)) back.hide(); else { back.show(); back.onClick(onBack); } return () => back.offClick(onBack); }, [path, roots]); }