diff --git a/src/RootApp.tsx b/src/RootApp.tsx index 9949cf603e..ebfebc4252 100644 --- a/src/RootApp.tsx +++ b/src/RootApp.tsx @@ -3,6 +3,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import React from 'react'; import { ApiProvider } from 'hooks/useApi'; +import { UserSettingsProvider } from 'hooks/useUserSettings'; import { WebConfigProvider } from 'hooks/useWebConfig'; import { queryClient } from 'utils/query/queryClient'; @@ -11,9 +12,11 @@ import RootAppRouter from 'RootAppRouter'; const RootApp = () => ( - - - + + + + + diff --git a/src/hooks/useUserSettings.tsx b/src/hooks/useUserSettings.tsx new file mode 100644 index 0000000000..84eb49117e --- /dev/null +++ b/src/hooks/useUserSettings.tsx @@ -0,0 +1,78 @@ +import React, { type FC, type PropsWithChildren, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; + +import { FALLBACK_CULTURE } from 'lib/globalize'; +import { currentSettings as userSettings } from 'scripts/settings/userSettings'; +import Events, { type Event } from 'utils/events'; + +import { useApi } from './useApi'; + +interface UserSettings { + theme?: string + dashboardTheme?: string + dateTimeLocale?: string + language?: string +} + +// NOTE: This is an incomplete list of only the settings that are currently being used +const UserSettingField = { + // Theme settings + Theme: 'appTheme', + DashboardTheme: 'dashboardTheme', + // Locale settings + DateTimeLocale: 'datetimelocale', + Language: 'language' +}; + +const UserSettingsContext = createContext({}); + +export const useUserSettings = () => useContext(UserSettingsContext); + +export const UserSettingsProvider: FC> = ({ children }) => { + const [ theme, setTheme ] = useState(); + const [ dashboardTheme, setDashboardTheme ] = useState(); + const [ dateTimeLocale, setDateTimeLocale ] = useState(); + const [ language, setLanguage ] = useState(FALLBACK_CULTURE); + + const { user } = useApi(); + + const context = useMemo(() => ({ + theme, + dashboardTheme, + dateTimeLocale, + locale: language + }), [ theme, dashboardTheme, dateTimeLocale, language ]); + + // Update the values of the user settings + const updateUserSettings = useCallback(() => { + setTheme(userSettings.theme()); + setDashboardTheme(userSettings.dashboardTheme()); + setDateTimeLocale(userSettings.dateTimeLocale()); + setLanguage(userSettings.language()); + }, []); + + const onUserSettingsChange = useCallback((_e: Event, name?: string) => { + if (name && Object.values(UserSettingField).includes(name)) { + updateUserSettings(); + } + }, [ updateUserSettings ]); + + // Handle user settings changes + useEffect(() => { + Events.on(userSettings, 'change', onUserSettingsChange); + + return () => { + Events.off(userSettings, 'change', onUserSettingsChange); + }; + }, [ onUserSettingsChange ]); + + // Update the settings if the user changes + useEffect(() => { + updateUserSettings(); + }, [ updateUserSettings, user ]); + + return ( + + {children} + + ); +}; diff --git a/src/hooks/useUserTheme.ts b/src/hooks/useUserTheme.ts index 5e759b6dff..29593ddca3 100644 --- a/src/hooks/useUserTheme.ts +++ b/src/hooks/useUserTheme.ts @@ -1,57 +1,12 @@ -import { useCallback, useEffect, useState } from 'react'; - -import { currentSettings as userSettings } from 'scripts/settings/userSettings'; -import Events, { type Event } from 'utils/events'; - -import { useApi } from './useApi'; import { useThemes } from './useThemes'; - -const THEME_FIELD_NAMES = [ 'appTheme', 'dashboardTheme' ]; +import { useUserSettings } from './useUserSettings'; export function useUserTheme() { - const [ theme, setTheme ] = useState(); - const [ dashboardTheme, setDashboardTheme ] = useState(); - - const { user } = useApi(); + const { theme, dashboardTheme } = useUserSettings(); const { defaultTheme } = useThemes(); - useEffect(() => { - if (defaultTheme) { - if (!theme) setTheme(defaultTheme.id); - if (!dashboardTheme) setDashboardTheme(defaultTheme.id); - } - }, [ dashboardTheme, defaultTheme, theme ]); - - // Update the current themes with values from user settings - const updateThemesFromSettings = useCallback(() => { - const userTheme = userSettings.theme(); - if (userTheme) setTheme(userTheme); - const userDashboardTheme = userSettings.dashboardTheme(); - if (userDashboardTheme) setDashboardTheme(userDashboardTheme); - }, []); - - const onUserSettingsChange = useCallback((_e: Event, name?: string) => { - if (name && THEME_FIELD_NAMES.includes(name)) { - updateThemesFromSettings(); - } - }, [ updateThemesFromSettings ]); - - // Handle user settings changes - useEffect(() => { - Events.on(userSettings, 'change', onUserSettingsChange); - - return () => { - Events.off(userSettings, 'change', onUserSettingsChange); - }; - }, [ onUserSettingsChange ]); - - // Update the theme if the user changes - useEffect(() => { - updateThemesFromSettings(); - }, [ updateThemesFromSettings, user ]); - return { - theme, - dashboardTheme + theme: theme || defaultTheme?.id, + dashboardTheme: dashboardTheme || defaultTheme?.id }; } diff --git a/src/lib/globalize/index.js b/src/lib/globalize/index.js index f88ce0d05e..aad779a28d 100644 --- a/src/lib/globalize/index.js +++ b/src/lib/globalize/index.js @@ -9,7 +9,7 @@ const Direction = { ltr: 'ltr' }; -const fallbackCulture = 'en-us'; +export const FALLBACK_CULTURE = 'en-us'; const RTL_LANGS = ['ar', 'fa', 'ur', 'he']; const allTranslations = {}; @@ -41,7 +41,7 @@ function getDefaultLanguage() { return navigator.languages[0]; } - return fallbackCulture; + return FALLBACK_CULTURE; } export function getIsRTL() { @@ -50,7 +50,6 @@ export function getIsRTL() { function checkAndProcessDir(culture) { isRTL = false; - console.log(culture); for (const lang of RTL_LANGS) { if (culture.includes(lang)) { isRTL = true; @@ -111,9 +110,9 @@ function ensureTranslations(culture) { for (const i in allTranslations) { ensureTranslation(allTranslations[i], culture); } - if (culture !== fallbackCulture) { + if (culture !== FALLBACK_CULTURE) { for (const i in allTranslations) { - ensureTranslation(allTranslations[i], fallbackCulture); + ensureTranslation(allTranslations[i], FALLBACK_CULTURE); } } } @@ -163,7 +162,7 @@ export function loadStrings(options) { register(options); } promises.push(ensureTranslation(allTranslations[optionsName], locale)); - promises.push(ensureTranslation(allTranslations[optionsName], fallbackCulture)); + promises.push(ensureTranslation(allTranslations[optionsName], FALLBACK_CULTURE)); return Promise.all(promises); } @@ -183,7 +182,7 @@ function loadTranslation(translations, lang) { if (!filtered.length) { filtered = translations.filter(function (t) { - return normalizeLocaleName(t.lang) === fallbackCulture; + return normalizeLocaleName(t.lang) === FALLBACK_CULTURE; }); } } @@ -222,7 +221,7 @@ function translateKeyFromModule(key, module) { return dictionary[key]; } - dictionary = getDictionary(module, fallbackCulture); + dictionary = getDictionary(module, FALLBACK_CULTURE); if (dictionary?.[key]) { return dictionary[key]; }