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];
}