jvcgpt / src /contexts /route.ts
Greums's picture
major improvements to the app
a417977
raw
history blame
3.71 kB
import {createContext} from "@/utils/context"
import {Topic} from "@/contexts/topics";
type Route = {
name: keyof typeof routes,
args: any[],
location: string,
}
const matchers: {[key: string]: (location: string) => any[] | null} = {};
function constructRoute<A>(
name: keyof typeof routes,
generateLocation: (...args: A) => string,
matcher: (location: string) => A | null,
): (...args: A) => Route {
matchers[name] = matcher;
return (...args) => ({
name,
args,
location: generateLocation(...args),
} satisfies Route);
}
export const routes = {
home: constructRoute(
"home",
(page: number) => {
// console.log(page)
if(page < 0) {
throw new Error("Page not found");
}
return page ? `/${page + 1}` : "/"
},
(location) => {
// console.log(location)
if(location === "/") {
return [0];
}
const match = location.match(/^\/(\d+)/);
// console.log([Math.max(parseInt(match[1]) - 1, 0)]);
return match ? [Math.max(parseInt(match[1]) - 1, 0)] : null;
},
// (location) => location === "/" ? [] : null,
),
topic: constructRoute(
"topic",
(id: string, page: number) => `/topic/${id}/${page}`,
(location) => {
const match = location.match(/^\/topic\/(.+)\/(\d+)/);
return match ? [match[1], Math.max(parseInt(match[2]) - 1, 1)] : null;
},
),
settings: constructRoute(
"settings",
() => "/settings",
(location) => location === "/settings" ? [] : null,
),
} as const satisfies {
[key: string]: (...args: any[]) => Route
};
function matchBrowserLocation(): Route {
for (const [name, matcher] of Object.entries(matchers)) {
const location = window.location.pathname + window.location.search;
const args = matcher(location);
if (args !== null) {
return routes[name as keyof typeof routes](...args);
}
}
return routes.home(0);
}
const history: Route[] = [];
let historyIndex = 0;
export const routeCtx = createContext({
initialValue: matchBrowserLocation,
controllers: (route: Route, setRoute) => ({
onMount: () => {
// console.log("add listener")
// Add initial route to history
history.push(route);
historyIndex = history.length - 1;
window.addEventListener('popstate', () => {
setRoute(matchBrowserLocation());
});
},
effect: () => {
const location = route.location;
if (location !== `${window.location.pathname}${window.location.search}`) {
history.push(route);
historyIndex = history.length - 1;
window.history.pushState({}, "", location);
}
},
actions: {
goBack: () => {
// console.log(history);
// console.log(historyIndex);
if (historyIndex > 0) {
historyIndex--;
setRoute(history[historyIndex]);
} else {
setRoute(routes.home(0));
}
},
getHistoryIndex: () => historyIndex,
// goBack: (route: Route, setRoute) => {
// if (historyIndex > 0) {
// historyIndex--;
//
// }
// }
// goForward: () => {
// if (historyIndex < history.length - 1) {}
// }
}
})
})