import React, { useReducer, useEffect, memo } from "react"; import type { ReactNode } from "react"; import { getMsg, getCurrentKcLanguageTag, changeLocale, getTagLabel } from "../i18n"; import type { KcLanguageTag } from "../i18n"; import type { KcContextBase } from "../getKcContext/KcContextBase"; import { assert } from "../tools/assert"; import { useCallbackFactory } from "powerhooks/useCallbackFactory"; import { headInsert } from "../tools/headInsert"; import { pathJoin } from "../../bin/tools/pathJoin"; import { useConstCallback } from "powerhooks/useConstCallback"; import type { KcTemplateProps } from "./KcProps"; import { useCssAndCx } from "tss-react"; export type TemplateProps = { displayInfo?: boolean; displayMessage?: boolean; displayRequiredFields?: boolean; displayWide?: boolean; showAnotherWayIfPresent?: boolean; headerNode: ReactNode; showUsernameNode?: ReactNode; formNode: ReactNode; infoNode?: ReactNode; /** If you write your own page you probably want * to avoid pulling the default theme assets. */ doFetchDefaultThemeResources: boolean; } & { kcContext: KcContextBase } & KcTemplateProps; const Template = memo((props: TemplateProps) => { const { displayInfo = false, displayMessage = true, displayRequiredFields = false, displayWide = false, showAnotherWayIfPresent = true, headerNode, showUsernameNode = null, formNode, infoNode = null, kcContext, doFetchDefaultThemeResources, } = props; const { cx } = useCssAndCx(); useEffect(() => { console.log("Rendering this page with react using keycloakify"); }, []); const { msg } = getMsg(kcContext); const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [KcLanguageTag]) => changeLocale({ kcContext, kcLanguageTag, }), ); const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false)); const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext; const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false); useEffect(() => { if (!doFetchDefaultThemeResources) { setExtraCssLoaded(); return; } let isUnmounted = false; const cleanups: (() => void)[] = []; const toArr = (x: string | readonly string[] | undefined) => (typeof x === "string" ? x.split(" ") : x ?? []); Promise.all( [ ...toArr(props.stylesCommon).map(relativePath => pathJoin(url.resourcesCommonPath, relativePath)), ...toArr(props.styles).map(relativePath => pathJoin(url.resourcesPath, relativePath)), ] .reverse() .map(href => headInsert({ "type": "css", href, "position": "prepend", }), ), ).then(() => { if (isUnmounted) { return; } setExtraCssLoaded(); }); toArr(props.scripts).forEach(relativePath => headInsert({ "type": "javascript", "src": pathJoin(url.resourcesPath, relativePath), }), ); if (props.kcHtmlClass !== undefined) { const htmlClassList = document.getElementsByTagName("html")[0].classList; const tokens = cx(props.kcHtmlClass).split(" "); htmlClassList.add(...tokens); cleanups.push(() => htmlClassList.remove(...tokens)); } return () => { isUnmounted = true; cleanups.forEach(f => f()); }; }, [props.kcHtmlClass]); if (!isExtraCssLoaded) { return null; } return (