From 15e6542f0df709611eb27d5f454537e5f0dddabd Mon Sep 17 00:00:00 2001 From: Hanqin Guan <53598282+harveyghq@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:21:08 +0800 Subject: [PATCH] fix: server overrides user's locale/appearance (#2771) --- web/src/App.tsx | 20 ++++++++------------ web/src/store/module/global.ts | 27 ++++++++++++++++++++++----- web/src/store/v1/user.ts | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index baaf11fe..c9515648 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -82,12 +82,11 @@ const App = () => { }, [systemStatus.customizedProfile]); useEffect(() => { - document.documentElement.setAttribute("lang", locale); - i18n.changeLanguage(locale); - storage.set({ - locale: locale, - }); - if (locale === "ar") { + const { locale: storageLocale } = storage.get(["locale"]); + const currentLocale = storageLocale || userStore?.userSetting?.locale || locale; + i18n.changeLanguage(currentLocale); + document.documentElement.setAttribute("lang", currentLocale); + if (currentLocale === "ar") { document.documentElement.setAttribute("dir", "rtl"); } else { document.documentElement.setAttribute("dir", "ltr"); @@ -95,12 +94,9 @@ const App = () => { }, [locale]); useEffect(() => { - storage.set({ - appearance: appearance, - }); - - let currentAppearance = appearance; - if (appearance === "system") { + const { appearance: storageAppearance } = storage.get(["appearance"]); + let currentAppearance = (storageAppearance || userStore?.userSetting?.appearance || appearance) as Appearance; + if (currentAppearance === "system") { currentAppearance = getSystemColorScheme(); } diff --git a/web/src/store/module/global.ts b/web/src/store/module/global.ts index efd5a41c..d646d8e8 100644 --- a/web/src/store/module/global.ts +++ b/web/src/store/module/global.ts @@ -7,10 +7,9 @@ import store, { useAppSelector } from "../"; import { setAppearance, setGlobalState, setLocale } from "../reducer/global"; export const initialGlobalState = async () => { - const { locale: storageLocale, appearance: storageAppearance } = storage.get(["locale", "appearance"]); const defaultGlobalState = { - locale: (storageLocale || "en") as Locale, - appearance: (storageAppearance || "system") as Appearance, + locale: "en" as Locale, + appearance: "system" as Appearance, systemStatus: { allowSignUp: false, disablePasswordLogin: false, @@ -44,9 +43,19 @@ export const initialGlobalState = async () => { externalUrl: "", }, }; + // Use storageLocale > userLocale > customizedProfile.locale (server's default locale) + // Initially, storageLocale is undefined and user is not logged in, so use server's default locale. + // User can change locale in login/sign up page, set storageLocale and override userLocale after logged in. + // Otherwise, storageLocale remains undefined and if userLocale has value after user logged in, set to storageLocale and re-render. + // Otherwise, use server's default locale, set to storageLocale. + const { locale: storageLocale, appearance: storageAppearance } = storage.get(["locale", "appearance"]); defaultGlobalState.locale = - defaultGlobalState.systemStatus.customizedProfile.locale || defaultGlobalState.locale || findNearestLanguageMatch(i18n.language); - defaultGlobalState.appearance = defaultGlobalState.systemStatus.customizedProfile.appearance || defaultGlobalState.appearance; + storageLocale || + defaultGlobalState.systemStatus.customizedProfile.locale || + defaultGlobalState.locale || + findNearestLanguageMatch(i18n.language); + defaultGlobalState.appearance = + storageAppearance || defaultGlobalState.systemStatus.customizedProfile.appearance || defaultGlobalState.appearance; } store.dispatch(setGlobalState(defaultGlobalState)); }; @@ -83,9 +92,17 @@ export const useGlobalStore = () => { ); }, setLocale: (locale: Locale) => { + // Set storageLocale to user selected locale. + storage.set({ + locale: locale, + }); store.dispatch(setLocale(locale)); }, setAppearance: (appearance: Appearance) => { + // Set storageAppearance to user selected appearance. + storage.set({ + appearance: appearance, + }); store.dispatch(setAppearance(appearance)); }, }; diff --git a/web/src/store/v1/user.ts b/web/src/store/v1/user.ts index 03726d92..72aa98aa 100644 --- a/web/src/store/v1/user.ts +++ b/web/src/store/v1/user.ts @@ -1,6 +1,9 @@ import { create } from "zustand"; import { combine } from "zustand/middleware"; import { authServiceClient, userServiceClient } from "@/grpcweb"; +import storage from "@/helpers/storage"; +import store from "@/store"; +import { setAppearance, setLocale } from "@/store/reducer/global"; import { User, UserSetting } from "@/types/proto/api/v2/user_service"; import { UserNamePrefix, extractUsernameFromName } from "./resourceName"; @@ -110,6 +113,22 @@ export const useUserStore = create( ...setting, }), }); + const userLocale = get().userSetting?.locale; + const userAppearance = get().userSetting?.appearance; + const { locale: storedLocale, appearance: storedAppearance } = storage.get(["locale", "appearance"]); + // Use storageLocale > userLocale > default locale + const locale = storedLocale || userLocale || store.getState().global.locale; + const appearance = (storedAppearance || userAppearance || store.getState().global.appearance) as Appearance; + + // If storedLocale is undefined, set storageLocale to userLocale. + if (storedLocale === undefined && storedAppearance === undefined) { + storage.set({ locale: locale }); + storage.set({ appearance: appearance }); + } + + store.dispatch(setLocale(locale)); + store.dispatch(setAppearance(appearance)); + return user; }, updateUserSetting: async (userSetting: Partial, updateMask: string[]) => {