1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Add user settings hook

This commit is contained in:
Bill Thornton 2024-08-28 16:24:12 -04:00
parent 4905ec09ae
commit aef4a42f8e
4 changed files with 95 additions and 60 deletions

View file

@ -3,6 +3,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react'; import React from 'react';
import { ApiProvider } from 'hooks/useApi'; import { ApiProvider } from 'hooks/useApi';
import { UserSettingsProvider } from 'hooks/useUserSettings';
import { WebConfigProvider } from 'hooks/useWebConfig'; import { WebConfigProvider } from 'hooks/useWebConfig';
import { queryClient } from 'utils/query/queryClient'; import { queryClient } from 'utils/query/queryClient';
@ -11,9 +12,11 @@ import RootAppRouter from 'RootAppRouter';
const RootApp = () => ( const RootApp = () => (
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ApiProvider> <ApiProvider>
<WebConfigProvider> <UserSettingsProvider>
<RootAppRouter /> <WebConfigProvider>
</WebConfigProvider> <RootAppRouter />
</WebConfigProvider>
</UserSettingsProvider>
</ApiProvider> </ApiProvider>
<ReactQueryDevtools initialIsOpen={false} /> <ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider> </QueryClientProvider>

View file

@ -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<UserSettings>({});
export const useUserSettings = () => useContext(UserSettingsContext);
export const UserSettingsProvider: FC<PropsWithChildren<unknown>> = ({ children }) => {
const [ theme, setTheme ] = useState<string>();
const [ dashboardTheme, setDashboardTheme ] = useState<string>();
const [ dateTimeLocale, setDateTimeLocale ] = useState<string>();
const [ language, setLanguage ] = useState<string | undefined>(FALLBACK_CULTURE);
const { user } = useApi();
const context = useMemo<UserSettings>(() => ({
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 (
<UserSettingsContext.Provider value={context}>
{children}
</UserSettingsContext.Provider>
);
};

View file

@ -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'; import { useThemes } from './useThemes';
import { useUserSettings } from './useUserSettings';
const THEME_FIELD_NAMES = [ 'appTheme', 'dashboardTheme' ];
export function useUserTheme() { export function useUserTheme() {
const [ theme, setTheme ] = useState<string>(); const { theme, dashboardTheme } = useUserSettings();
const [ dashboardTheme, setDashboardTheme ] = useState<string>();
const { user } = useApi();
const { defaultTheme } = useThemes(); 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 { return {
theme, theme: theme || defaultTheme?.id,
dashboardTheme dashboardTheme: dashboardTheme || defaultTheme?.id
}; };
} }

View file

@ -9,7 +9,7 @@ const Direction = {
ltr: 'ltr' ltr: 'ltr'
}; };
const fallbackCulture = 'en-us'; export const FALLBACK_CULTURE = 'en-us';
const RTL_LANGS = ['ar', 'fa', 'ur', 'he']; const RTL_LANGS = ['ar', 'fa', 'ur', 'he'];
const allTranslations = {}; const allTranslations = {};
@ -41,7 +41,7 @@ function getDefaultLanguage() {
return navigator.languages[0]; return navigator.languages[0];
} }
return fallbackCulture; return FALLBACK_CULTURE;
} }
export function getIsRTL() { export function getIsRTL() {
@ -50,7 +50,6 @@ export function getIsRTL() {
function checkAndProcessDir(culture) { function checkAndProcessDir(culture) {
isRTL = false; isRTL = false;
console.log(culture);
for (const lang of RTL_LANGS) { for (const lang of RTL_LANGS) {
if (culture.includes(lang)) { if (culture.includes(lang)) {
isRTL = true; isRTL = true;
@ -111,9 +110,9 @@ function ensureTranslations(culture) {
for (const i in allTranslations) { for (const i in allTranslations) {
ensureTranslation(allTranslations[i], culture); ensureTranslation(allTranslations[i], culture);
} }
if (culture !== fallbackCulture) { if (culture !== FALLBACK_CULTURE) {
for (const i in allTranslations) { for (const i in allTranslations) {
ensureTranslation(allTranslations[i], fallbackCulture); ensureTranslation(allTranslations[i], FALLBACK_CULTURE);
} }
} }
} }
@ -163,7 +162,7 @@ export function loadStrings(options) {
register(options); register(options);
} }
promises.push(ensureTranslation(allTranslations[optionsName], locale)); promises.push(ensureTranslation(allTranslations[optionsName], locale));
promises.push(ensureTranslation(allTranslations[optionsName], fallbackCulture)); promises.push(ensureTranslation(allTranslations[optionsName], FALLBACK_CULTURE));
return Promise.all(promises); return Promise.all(promises);
} }
@ -183,7 +182,7 @@ function loadTranslation(translations, lang) {
if (!filtered.length) { if (!filtered.length) {
filtered = translations.filter(function (t) { 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]; return dictionary[key];
} }
dictionary = getDictionary(module, fallbackCulture); dictionary = getDictionary(module, FALLBACK_CULTURE);
if (dictionary?.[key]) { if (dictionary?.[key]) {
return dictionary[key]; return dictionary[key];
} }