Implement reactive programing for language switching

This commit is contained in:
Joseph Garrone 2021-03-06 15:16:21 +01:00
parent b8751d67db
commit 6eccd313b6
3 changed files with 36 additions and 26 deletions

View File

@ -1,12 +1,13 @@
import { useState, useReducer ,useEffect, memo } from "react"; import { useReducer, useEffect, memo } from "react";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { useKcTranslation } from "../i18n/useKcTranslation"; import { useKcTranslation } from "../i18n/useKcTranslation";
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
import { kcContext } from "../kcContext"; import { kcContext } from "../kcContext";
import { assert } from "../tools/assert"; import { assert } from "../tools/assert";
import { cx } from "tss-react"; import { cx } from "tss-react";
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
import type { KcLanguageTag } from "../i18n/KcLanguageTag"; import type { KcLanguageTag } from "../i18n/KcLanguageTag";
import { getBestMatchAmongKcLanguageTag } from "../i18n/KcLanguageTag";
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag"; import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
import { useCallbackFactory } from "powerhooks"; import { useCallbackFactory } from "powerhooks";
import { appendHead } from "../tools/appendHead"; import { appendHead } from "../tools/appendHead";
@ -41,29 +42,45 @@ export const Template = memo((props: TemplateProps) => {
displayInfoNode = null displayInfoNode = null
} = props; } = props;
useEffect(()=> { console.log("Rendering this page with react using keycloakify") },[]); useEffect(() => { console.log("Rendering this page with react using keycloakify") }, []);
const { t } = useKcTranslation(); const { t } = useKcTranslation();
const { kcLanguageTag, setKcLanguageTag } = useKcLanguageTag(); const { kcLanguageTag, setKcLanguageTag } = useKcLanguageTag();
const onChangeLanguageClickFactory = useCallbackFactory( const onChangeLanguageClickFactory = useCallbackFactory(
([languageTag]: [KcLanguageTag]) => ([languageTag]: [KcLanguageTag]) =>
setKcLanguageTag(languageTag) setKcLanguageTag(languageTag)
); );
const onTryAnotherWayClick = useConstCallback(() => { const onTryAnotherWayClick = useConstCallback(() =>
(document.forms["kc-select-try-another-way-form" as never].submit(), false)
);
document.forms["kc-select-try-another-way-form" as never].submit(); assert(kcContext !== undefined);
return false; const {
realm, locale, auth,
url, message, isAppInitiatedAction
}= kcContext;
}); useEffect(()=>{
const [{ realm, locale, auth, url, message, isAppInitiatedAction }] = useState(() => ( if( !realm.internationalizationEnabled ){
assert(kcContext !== undefined, "App is not currently being served by KeyCloak"), return;
kcContext }
));
assert( locale !== undefined );
if( kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current) ){
return;
}
window.location.href =
locale.supported.find(({ languageTag }) => languageTag === kcLanguageTag)!.url;
},[kcLanguageTag]);
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false); const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
@ -71,7 +88,8 @@ export const Template = memo((props: TemplateProps) => {
let isUnmounted = false; let isUnmounted = false;
const toArr= (x: string | readonly string[] | undefined )=> typeof x === "string" ? x.split(" ") : x ?? []; const toArr = (x: string | readonly string[] | undefined) =>
typeof x === "string" ? x.split(" ") : x ?? [];
Promise.all( Promise.all(
[ [
@ -135,15 +153,14 @@ export const Template = memo((props: TemplateProps) => {
<ul> <ul>
{ {
locale.supported.map( locale.supported.map(
({ languageTag, url }) => ({ languageTag }) =>
<li className="kc-dropdown-item"> <li className="kc-dropdown-item">
<a href={url} onClick={onChangeLanguageClickFactory(languageTag)}> <a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
{getKcLanguageTagLabel(languageTag)} {getKcLanguageTagLabel(languageTag)}
</a> </a>
</li> </li>
) )
} }
</ul> </ul>
</div> </div>

View File

@ -6,7 +6,7 @@ import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
export const { useKcLanguageTag } = createUseGlobalState( export const { useKcLanguageTag } = createUseGlobalState(
"kcLanguageTag", "kcLanguageTag",
() => getBestMatchAmongKcLanguageTag( () => getBestMatchAmongKcLanguageTag(
kcContext?.locale?.["current" as never] ?? kcContext?.locale?.current ??
navigator.language navigator.language
), ),
{ "persistance": "cookie" } { "persistance": "cookie" }

View File

@ -5,16 +5,11 @@ import { id } from "evt/tools/typeSafety/id";
import type { KcLanguageTag } from "./i18n/KcLanguageTag"; import type { KcLanguageTag } from "./i18n/KcLanguageTag";
import { doExtends } from "evt/tools/typeSafety/doExtends"; import { doExtends } from "evt/tools/typeSafety/doExtends";
import type { MessageKey } from "./i18n/useKcTranslation"; import type { MessageKey } from "./i18n/useKcTranslation";
import type { LanguageLabel } from "./i18n/KcLanguageTag";
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
StrEnum extends `${Prefix}${infer U}` ? U : never; StrEnum extends `${Prefix}${infer U}` ? U : never;
const x: "33" | "44" = null as any;;
const y: `foo.${typeof x}` = `foo.${x}` as const;
y;
export type KcContext = KcContext.Login | KcContext.Register | KcContext.Info; export type KcContext = KcContext.Login | KcContext.Register | KcContext.Info;
export declare namespace KcContext { export declare namespace KcContext {
@ -43,9 +38,7 @@ export declare namespace KcContext {
*/ */
//label: LanguageLabel; //label: LanguageLabel;
}[]; }[];
//NOTE: We do not expose this because the language is managed current: LanguageLabel;
//client side. We use this value however to set the default.
//current: LanguageLabel;
}, },
auth?: { auth?: {
showUsername: boolean; showUsername: boolean;