Our Personal Data Server from scratch!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 93 lines 2.2 kB view raw
1import { 2 buildUrl, 3 type Route, 4 type RouteParams, 5 routes, 6 type RoutesWithParams, 7} from "./types/routes.ts"; 8 9const APP_BASE = "/app"; 10 11type Brand<T, B extends string> = T & { readonly __brand: B }; 12type AppPath = Brand<string, "AppPath">; 13 14function asAppPath(path: string): AppPath { 15 const normalized = path.startsWith("/") ? path : "/" + path; 16 return normalized as AppPath; 17} 18 19function getAppPath(): AppPath { 20 const pathname = globalThis.location.pathname; 21 if (pathname.startsWith(APP_BASE)) { 22 const path = pathname.slice(APP_BASE.length) || "/"; 23 return asAppPath(path); 24 } 25 return asAppPath("/"); 26} 27 28function getSearchParams(): URLSearchParams { 29 return new URLSearchParams(globalThis.location.search); 30} 31 32interface RouterState { 33 readonly path: AppPath; 34 readonly searchParams: URLSearchParams; 35} 36 37const state = $state<{ current: RouterState }>({ 38 current: { 39 path: getAppPath(), 40 searchParams: getSearchParams(), 41 }, 42}); 43 44function updateState(): void { 45 state.current = { 46 path: getAppPath(), 47 searchParams: getSearchParams(), 48 }; 49} 50 51globalThis.addEventListener("popstate", updateState); 52 53export function navigate<R extends Route>( 54 route: R, 55 options?: { 56 params?: R extends RoutesWithParams ? RouteParams[R] : never; 57 replace?: boolean; 58 }, 59): void { 60 const url = options?.params ? buildUrl(route, options.params) : route; 61 const fullPath = APP_BASE + (url.startsWith("/") ? url : "/" + url); 62 63 if (options?.replace) { 64 globalThis.history.replaceState(null, "", fullPath); 65 } else { 66 globalThis.history.pushState(null, "", fullPath); 67 } 68 69 updateState(); 70} 71 72export function getCurrentPath(): AppPath { 73 return state.current.path; 74} 75 76export function getCurrentSearchParams(): URLSearchParams { 77 return state.current.searchParams; 78} 79 80export function getSearchParam(key: string): string | null { 81 return state.current.searchParams.get(key); 82} 83 84export function getFullUrl(path: string): string { 85 return APP_BASE + (path.startsWith("/") ? path : "/" + path); 86} 87 88export function isCurrentRoute(route: Route): boolean { 89 const pathWithoutQuery = state.current.path.split("?")[0]; 90 return pathWithoutQuery === route; 91} 92 93export { type Route, type RouteParams, routes };