Files
WebDrop/web/src/app/navigation.ts
T
2026-10-02 00:59:37 +08:00

51 lines
1.6 KiB
TypeScript

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}`;