chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { useCallback, useEffect, useRef, useState } from 'react';
/**
* Path routing over the History API. The server serves this document for
* every account path, so a reload or a shared link lands on the same screen
* the nav does. Both the pathname and the query string are tracked, so a
* detail view like a collection is a real, addressable page.
*/
export function useRouter() {
const [href, setHref] = useState(
() => window.location.pathname + window.location.search,
);
// Depth in this app's own history stack. 0 is the first page loaded, so Back
// can tell a real step back (still inside the app) from one that would leave.
const depth = useRef(window.history.state?.idx ?? 0);
useEffect(() => {
if (window.history.state?.idx == null) {
window.history.replaceState({ idx: 0 }, '');
}
/** @param {PopStateEvent} event */
const onPop = (event) => {
depth.current = event.state?.idx ?? 0;
setHref(window.location.pathname + window.location.search);
};
window.addEventListener('popstate', onPop);
return () => window.removeEventListener('popstate', onPop);
}, []);
const navigate = useCallback(
/** @param {string} next */ (next) => {
depth.current += 1;
window.history.pushState({ idx: depth.current }, '', next);
setHref(next);
window.scrollTo(0, 0);
},
[],
);
// Step back to wherever the visitor came from. When they landed here directly
// (a shared link or a reload, nothing to step back to), take the fallback so
// Back still lands somewhere sensible instead of leaving the app.
const back = useCallback(
/** @param {string} [fallback] */
(fallback) => {
if (depth.current > 0) window.history.back();
else if (fallback) navigate(fallback);
},
[navigate],
);
const url = new URL(href, window.location.origin);
return { path: url.pathname, params: url.searchParams, navigate, back };
}