import { Api, type ApiOptions, type AuthState } from "Api"; import { isDebug } from "core/env"; import { createContext, type ReactNode, useContext, useMemo, useState } from "react"; import type { AdminBkndWindowContext } from "modules/server/AdminController"; export type BkndClientContext = { baseUrl: string; api: Api; authState?: Partial; }; const ClientContext = createContext(undefined!); export type ClientProviderProps = { children?: ReactNode; baseUrl?: string; api?: Api; } & ApiOptions; export const ClientProvider = ({ children, host, baseUrl: _baseUrl = host, api: _api, ...props }: ClientProviderProps) => { const winCtx = useBkndWindowContext(); const _ctx = useClientContext(); let actualBaseUrl = _baseUrl ?? _ctx?.baseUrl ?? ""; let user: any; if (winCtx) { user = winCtx.user; } if (!actualBaseUrl) { try { actualBaseUrl = window.location.origin; } catch (e) {} } const apiProps = { user, ...props, host: actualBaseUrl }; const api = useMemo( () => _api ?? new Api({ ...apiProps, verbose: isDebug(), onAuthStateChange: (state) => { props.onAuthStateChange?.(state); if (!authState?.token || state.token !== authState?.token) { setAuthState(state); } }, }), [_api, JSON.stringify(apiProps)], ); const [authState, setAuthState] = useState | undefined>(api.getAuthState()); return ( {children} ); }; export const useApi = (host?: ApiOptions["host"]): Api => { const context = useContext(ClientContext); if (!context?.api || (host && host.length > 0 && host !== context.baseUrl)) { console.info("creating new api", { host }); return new Api({ host: host ?? "" }); } if (!context) { throw new Error("useApi must be used within a ClientProvider"); } return context.api; }; export const useClientContext = () => { return useContext(ClientContext); }; /** * @deprecated use useApi().baseUrl instead */ export const useBaseUrl = () => { const context = useClientContext(); return context?.baseUrl; }; export function useBkndWindowContext(): AdminBkndWindowContext { const defaults = { logout_route: "/api/auth/logout", admin_basepath: "", }; if (typeof window !== "undefined" && window.__BKND__) { return { ...defaults, ...window.__BKND__, }; } return defaults; }