import type { PrimaryFieldType } from "core"; import { encodeSearch } from "core/utils"; import { useLocation, useRouter } from "wouter"; import { useBknd } from "../client/BkndProvider"; export const routes = { data: { root: () => "/data", entity: { list: (entity: string) => `/entity/${entity}`, create: (entity: string) => `/entity/${entity}/create`, edit: (entity: string, id: PrimaryFieldType) => `/entity/${entity}/edit/${id}`, }, schema: { root: () => "/schema", entity: (entity: string) => `/schema/entity/${entity}`, }, }, auth: { root: () => "/auth", users: { list: () => "/users", edit: (id: PrimaryFieldType) => `/users/edit/${id}`, }, roles: { list: () => "/roles", edit: (role: string) => `/roles/edit/${role}`, }, settings: () => "/settings", strategies: () => "/strategies", }, flows: { root: () => "/flows", flows: { list: () => "/", edit: (id: PrimaryFieldType) => `/flow/${id}`, }, }, settings: { root: () => "/settings", path: (path: string[]) => `/settings/${path.join("/")}`, }, }; export function withQuery(url: string, query: object) { const search = encodeSearch(query, { encode: false }); return `${url}?${search}`; } export function withAbsolute(url: string) { const { app } = useBknd(); return app.getAbsolutePath(url); } export function useRouteNavigate() { const [navigate] = useNavigate(); return (fn: (r: typeof routes) => string, options?: Parameters[1]) => { navigate(fn(routes), options); }; } export function useNavigate() { const [location, navigate] = useLocation(); const router = useRouter(); const { app } = useBknd(); const basepath = app.options.basepath; return [ ( url: string, options?: | { query?: object; absolute?: boolean; replace?: boolean; state?: any; transition?: boolean; } | { reload: true } | { target: string }, ) => { const wrap = (fn: () => void) => { fn(); // prepared for view transition /*if (options && "transition" in options && options.transition === false) { fn(); } else { document.startViewTransition(fn); }*/ }; wrap(() => { if (options) { if ("reload" in options) { window.location.href = url; return; } else if ("target" in options) { const _url = window.location.origin + basepath + router.base + url; window.open(_url, options.target); return; } } const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url; const state = { ...options?.state, referrer: location, }; navigate(options?.query ? withQuery(_url, options?.query) : _url, { replace: options?.replace, state, }); }); }, location, (opts?: { fallback?: string }) => { const state = window.history.state; if (state?.referrer) { //window.history.replaceState(state, "", state.referrer); navigate(state.referrer, { replace: true }); } else if (opts?.fallback) { navigate(opts.fallback, { replace: true }); } else { window.history.back(); } }, ] as const; } export function useGoBack( fallback: string | (() => void) = "/", options?: { native?: boolean; absolute?: boolean; }, ) { const { app } = useBknd(); const [navigate] = useNavigate(); const referrer = document.referrer; const history_length = window.history.length; const same = referrer.length === 0; const canGoBack = (same && history_length > 1) || !!same; /*console.log("debug", { referrer, history_length, same, canGoBack });*/ function goBack() { if (same && history_length > 2) { //console.log("used history"); window.history.back(); } else { //console.log("used fallback"); if (typeof fallback === "string") { const _fallback = options?.absolute ? app.getAbsolutePath(fallback) : fallback; //console.log("fallback", _fallback); if (options?.native) { window.location.href = _fallback; } else { navigate(_fallback); } } else if (typeof fallback === "function") { fallback(); } } } return { same, canGoBack, goBack, }; }