import { useSyncExternalStore } from 'react'; import { isSheetEntry } from '@/lib/useBackToClose'; export type Route = { name: 'home' } | { name: 'channel'; id: string } | { name: 'share'; id: string }; const CHANNEL_RE = /^\/c\/([A-Za-z0-9_-]{22})\/?$/; const SHARE_RE = /^\/s\/([A-Za-z0-9_-]{22})\/?$/; export function parseRoute(pathname: string): Route { const c = CHANNEL_RE.exec(pathname); if (c?.[1]) return { name: 'channel', id: c[1] }; const s = SHARE_RE.exec(pathname); return s?.[1] ? { name: 'share', id: s[1] } : { name: 'home' }; } let current: Route = parseRoute(location.pathname); const listeners = new Set<() => void>(); const same = (a: Route, b: Route) => a.name === b.name && (a.name === 'home' || ('id' in b && a.id === b.id)); function set(route: Route) { if (same(current, route)) return; current = route; for (const l of listeners) l(); } window.addEventListener('popstate', () => set(parseRoute(location.pathname))); /** * 所有页面跳转的唯一入口。 * 从弹层里跳转时替换掉弹层压入的那条历史,避免返回键多退一步(lib/useBackToClose.ts)。 */ export function navigate(path: string): void { if (isSheetEntry()) history.replaceState(null, '', path); else history.pushState(null, '', path); set(parseRoute(path)); } export function useRoute(): Route { return useSyncExternalStore( (cb) => { listeners.add(cb); return () => listeners.delete(cb); }, () => current, ); } export const channelPath = (id: string) => `/c/${id}`; export const sharePath = (id: string) => `/s/${id}`;