This commit is contained in:
Andras Schmelczer 2026-09-11 16:58:56 +01:00
commit 9689bfcc18
23 changed files with 1815 additions and 955 deletions

View file

@ -2,14 +2,31 @@ import { useCallback, useEffect, useState } from 'react';
import type { UrlState } from './types';
import { decodeState, encodeState } from './url-state';
/** The fragment is the durable source of truth. No local storage or server state. */
const readLocation = () => decodeState(window.location.search || window.location.hash);
const queryUrl = (query: string) => `${window.location.pathname}${query}`;
/** Readable query parameters are the durable source of truth. */
export function useProject() {
const [loaded, setLoaded] = useState(() => decodeState(window.location.hash));
const [loaded, setLoaded] = useState(readLocation);
useEffect(() => {
const restore = () => setLoaded(decodeState(window.location.hash));
const restore = () => {
const next = readLocation();
if (!next.error) {
try {
// Canonicalize editable queries and migrate existing fragment links
// without creating an extra entry in browser history.
const query = encodeState(next.state);
if (query !== window.location.search || window.location.hash)
window.history.replaceState(null, '', queryUrl(query));
} catch (error) {
next.error = error instanceof Error ? error.message : 'This plan could not be saved.';
}
}
setLoaded(next);
};
window.addEventListener('hashchange', restore);
window.addEventListener('popstate', restore);
if (!window.location.hash) window.history.replaceState(null, '', encodeState(loaded.state));
restore();
return () => {
window.removeEventListener('hashchange', restore);
window.removeEventListener('popstate', restore);
@ -19,10 +36,10 @@ export function useProject() {
setLoaded((current) => {
const next = transform(current.state);
try {
const hash = encodeState(next);
if (hash !== window.location.hash)
window.history[milestone ? 'pushState' : 'replaceState'](null, '', hash);
const canonical = decodeState(hash).state;
const query = encodeState(next);
if (query !== window.location.search || window.location.hash)
window.history[milestone ? 'pushState' : 'replaceState'](null, '', queryUrl(query));
const canonical = decodeState(query).state;
if (next.project === current.state.project) canonical.project = current.state.project;
return { state: canonical, error: null };
} catch (error) {