From 2fa20363bec50b64bca858b7efe6e9201de702bb Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 21 Apr 2022 14:01:56 +0800 Subject: [PATCH] feat(ui): load lanuage settings from sign-in-experience settings (#595) * feat(ui): load lanuage settings from sign-in-experience settings load lanuage settings from sign-in-experience settings * fix(ui): cr fix cr fix * fix(ui): throw error on getSignInExperienceSettings throw error on getSignInExperienceSettings * fix(ui): cr fix cr fix * fix(ui): cr fix cr fix --- packages/ui/src/App.tsx | 10 ++++--- .../ui/src/components/Input/index.module.scss | 1 + packages/ui/src/i18n/init.ts | 30 +++++++++++-------- .../ui/src/utils/sign-in-experience.test.ts | 16 +++++----- packages/ui/src/utils/sign-in-experience.ts | 27 +++++++---------- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 1d753db77..2cc8a8b57 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -15,8 +15,6 @@ import getSignInExperienceSettings from './utils/sign-in-experience'; import './scss/normalized.scss'; -void initI18n(); - const App = () => { const [loading, setLoading] = useState(false); const [toast, setToast] = useState(''); @@ -30,9 +28,13 @@ const App = () => { useEffect(() => { (async () => { setLoading(true); - // TODO: error handling - const { settings } = await getSignInExperienceSettings(); + const settings = await getSignInExperienceSettings(); + + // Note: i18n must be initialized ahead of global experience settings + await initI18n(settings.languageInfo); + setExperienceSettings(settings); + setLoading(false); })(); }, []); diff --git a/packages/ui/src/components/Input/index.module.scss b/packages/ui/src/components/Input/index.module.scss index cfbb3e98c..8972dc9a4 100644 --- a/packages/ui/src/components/Input/index.module.scss +++ b/packages/ui/src/components/Input/index.module.scss @@ -29,6 +29,7 @@ padding: _.unit(3) 0; caret-color: var(--color-primary); font: var(--font-body-bold); + color: var(--color-text); &::placeholder { color: var(--color-caption); diff --git a/packages/ui/src/i18n/init.ts b/packages/ui/src/i18n/init.ts index 142bc4129..54a87aa34 100644 --- a/packages/ui/src/i18n/init.ts +++ b/packages/ui/src/i18n/init.ts @@ -1,18 +1,24 @@ import resources from '@logto/phrases'; -import i18next from 'i18next'; +import { LanguageInfo } from '@logto/schemas'; +import i18next, { InitOptions } from 'i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; -const initI18n = async () => - i18next - .use(initReactI18next) - .use(LanguageDetector) - .init({ - resources, - fallbackLng: 'en', - interpolation: { - escapeValue: false, - }, - }); +const initI18n = async (languageSettings?: LanguageInfo) => { + const baseOptions: InitOptions = { + resources, + fallbackLng: languageSettings?.fallbackLanguage ?? 'en', + interpolation: { + escapeValue: false, + }, + }; + + const options: InitOptions = + languageSettings?.autoDetect === false + ? { ...baseOptions, lng: languageSettings.fixedLanguage } + : baseOptions; + + return i18next.use(initReactI18next).use(LanguageDetector).init(options); +}; export default initI18n; diff --git a/packages/ui/src/utils/sign-in-experience.test.ts b/packages/ui/src/utils/sign-in-experience.test.ts index 94ebecce7..0d0b6d389 100644 --- a/packages/ui/src/utils/sign-in-experience.test.ts +++ b/packages/ui/src/utils/sign-in-experience.test.ts @@ -12,14 +12,14 @@ describe('getSignInExperienceSettings', () => { it('should return the sign in experience settings', async () => { getSignInExperienceMock.mockResolvedValueOnce(mockSignInExperience); - const { settings } = await getSignInExperienceSettings(); + const settings = await getSignInExperienceSettings(); - expect(settings?.branding).toEqual(mockSignInExperience.branding); - expect(settings?.languageInfo).toEqual(mockSignInExperience.languageInfo); - expect(settings?.termsOfUse).toEqual(mockSignInExperience.termsOfUse); - expect(settings?.primarySignInMethod).toEqual('username'); - expect(settings?.secondarySignInMethods).toContain('email'); - expect(settings?.secondarySignInMethods).toContain('sms'); - expect(settings?.secondarySignInMethods).toContain('social'); + expect(settings.branding).toEqual(mockSignInExperience.branding); + expect(settings.languageInfo).toEqual(mockSignInExperience.languageInfo); + expect(settings.termsOfUse).toEqual(mockSignInExperience.termsOfUse); + expect(settings.primarySignInMethod).toEqual('username'); + expect(settings.secondarySignInMethods).toContain('email'); + expect(settings.secondarySignInMethods).toContain('sms'); + expect(settings.secondarySignInMethods).toContain('social'); }); }); diff --git a/packages/ui/src/utils/sign-in-experience.ts b/packages/ui/src/utils/sign-in-experience.ts index 9a5321eaf..2e7f3f76a 100644 --- a/packages/ui/src/utils/sign-in-experience.ts +++ b/packages/ui/src/utils/sign-in-experience.ts @@ -6,7 +6,7 @@ import { SignInMethods } from '@logto/schemas'; import { getSignInExperience } from '@/apis/settings'; -import { SignInMethod } from '@/types'; +import { SignInMethod, SignInExperienceSettings } from '@/types'; const getPrimarySignInMethod = (signInMethods: SignInMethods) => { for (const [key, value] of Object.entries(signInMethods)) { @@ -18,7 +18,7 @@ const getPrimarySignInMethod = (signInMethods: SignInMethods) => { return 'username'; }; -const getSecondarySignInMethod = (signInMethods: SignInMethods) => +const getSecondarySignInMethods = (signInMethods: SignInMethods) => Object.entries(signInMethods).reduce((methods, [key, value]) => { if (value === 'secondary') { return [...methods, key as SignInMethod]; @@ -27,21 +27,16 @@ const getSecondarySignInMethod = (signInMethods: SignInMethods) => return methods; }, []); -const getSignInExperienceSettings = async () => { - try { - const result = await getSignInExperience(); - const settings = { - branding: result.branding, - languageInfo: result.languageInfo, - termsOfUse: result.termsOfUse, - primarySignInMethod: getPrimarySignInMethod(result.signInMethods), - secondarySignInMethods: getSecondarySignInMethod(result.signInMethods), - }; +const getSignInExperienceSettings = async (): Promise => { + const { branding, languageInfo, termsOfUse, signInMethods } = await getSignInExperience(); - return { settings }; - } catch (error: unknown) { - return { error }; - } + return { + branding, + languageInfo, + termsOfUse, + primarySignInMethod: getPrimarySignInMethod(signInMethods), + secondarySignInMethods: getSecondarySignInMethods(signInMethods), + }; }; export default getSignInExperienceSettings;