Files
hanko/frontend/elements/src/contexts/AppProvider.tsx

152 lines
3.5 KiB
TypeScript

import * as preact from "preact";
import { ComponentChildren, createContext, h } from "preact";
import { TranslateProvider } from "@denysvuika/preact-translate";
import {
StateUpdater,
useState,
useCallback,
useMemo,
useRef,
} from "preact/compat";
import {
Hanko,
User,
UserInfo,
Passcode,
Emails,
Config,
WebauthnCredentials,
} from "@teamhanko/hanko-frontend-sdk";
import { translations } from "../Translations";
import Container from "../components/wrapper/Container";
import InitPage from "../pages/InitPage";
import { JSXInternal } from "preact/src/jsx";
import SignalLike = JSXInternal.SignalLike;
type ExperimentalFeature = "conditionalMediation";
type ExperimentalFeatures = ExperimentalFeature[];
type ComponentName = "auth" | "profile";
interface Props {
api?: string;
lang?: string | SignalLike<string>;
fallbackLang?: string;
experimental?: string;
componentName: ComponentName;
children?: ComponentChildren;
}
interface States {
config: Config;
setConfig: StateUpdater<Config>;
userInfo: UserInfo;
setUserInfo: StateUpdater<UserInfo>;
passcode: Passcode;
setPasscode: StateUpdater<Passcode>;
user: User;
setUser: StateUpdater<User>;
emails: Emails;
setEmails: StateUpdater<Emails>;
webauthnCredentials: WebauthnCredentials;
setWebauthnCredentials: StateUpdater<WebauthnCredentials>;
page: h.JSX.Element;
setPage: StateUpdater<h.JSX.Element>;
}
interface Context extends States {
hanko: Hanko;
componentName: ComponentName;
experimentalFeatures?: ExperimentalFeatures;
emitSuccessEvent: () => void;
}
export const AppContext = createContext<Context>(null);
const AppProvider = ({
api,
lang,
fallbackLang = "en",
componentName,
experimental = "",
}: Props) => {
const ref = useRef<HTMLElement>(null);
const hanko = useMemo(() => {
if (api) {
return new Hanko(api, 13000);
}
return null;
}, [api]);
const experimentalFeatures = useMemo(
() =>
experimental
.split(" ")
.filter((feature) => feature.length)
.map((feature) => feature as ExperimentalFeature),
[experimental]
);
const emitSuccessEvent = useCallback(() => {
const event = new Event("hankoAuthSuccess", {
bubbles: true,
composed: true,
});
const fn = setTimeout(() => {
ref.current.dispatchEvent(event);
}, 500);
return () => clearTimeout(fn);
}, []);
const [config, setConfig] = useState<Config>();
const [userInfo, setUserInfo] = useState<UserInfo>(null);
const [passcode, setPasscode] = useState<Passcode>();
const [user, setUser] = useState<User>();
const [emails, setEmails] = useState<Emails>();
const [webauthnCredentials, setWebauthnCredentials] =
useState<WebauthnCredentials>();
const [page, setPage] = useState<h.JSX.Element>(<InitPage />);
return (
<AppContext.Provider
value={{
hanko,
componentName,
experimentalFeatures,
emitSuccessEvent,
config,
setConfig,
userInfo,
setUserInfo,
passcode,
setPasscode,
user,
setUser,
emails,
setEmails,
webauthnCredentials,
setWebauthnCredentials,
page,
setPage,
}}
>
<TranslateProvider
translations={translations}
lang={lang?.toString()}
fallbackLang={fallbackLang}
>
<Container ref={ref}>{page}</Container>
</TranslateProvider>
</AppContext.Provider>
);
};
export default AppProvider;