51 lines
1.6 KiB
TypeScript
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}`;
|