Files
userbase/app/src/ui/client/ClientProvider.tsx
T

109 lines
2.7 KiB
TypeScript

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<AuthState>;
};
const ClientContext = createContext<BkndClientContext>(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<Partial<AuthState> | undefined>(api.getAuthState());
return (
<ClientContext.Provider value={{ baseUrl: api.baseUrl, api, authState }}>
{children}
</ClientContext.Provider>
);
};
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;
}