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:
parent
4905ec09ae
commit
aef4a42f8e
4 changed files with 95 additions and 60 deletions
|
@ -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>
|
||||||
|
|
78
src/hooks/useUserSettings.tsx
Normal file
78
src/hooks/useUserSettings.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue