mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
feat: (preferences) hide display settings when unsupported
This commit is contained in:
parent
3dd26c7785
commit
5edcadd423
5 changed files with 163 additions and 99 deletions
|
@ -2,13 +2,16 @@ import Checkbox from '@mui/material/Checkbox';
|
||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import FormHelperText from '@mui/material/FormHelperText';
|
import FormHelperText from '@mui/material/FormHelperText';
|
||||||
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
|
|
||||||
|
import { appHost } from 'components/apphost';
|
||||||
|
import { useApi } from 'hooks/useApi';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import { DisplaySettingsValues } from './types';
|
import { DisplaySettingsValues } from './types';
|
||||||
import { useScreensavers } from './hooks/useScreensavers';
|
import { useScreensavers } from './hooks/useScreensavers';
|
||||||
|
@ -20,20 +23,23 @@ interface DisplayPreferencesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DisplayPreferences({ onChange, values }: Readonly<DisplayPreferencesProps>) {
|
export function DisplayPreferences({ onChange, values }: Readonly<DisplayPreferencesProps>) {
|
||||||
|
const { user } = useApi();
|
||||||
const { screensavers } = useScreensavers();
|
const { screensavers } = useScreensavers();
|
||||||
const { themes } = useServerThemes();
|
const { themes } = useServerThemes();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={3}>
|
||||||
<Typography variant='h2'>{globalize.translate('Display')}</Typography>
|
<Typography variant='h2'>{globalize.translate('Display')}</Typography>
|
||||||
|
|
||||||
|
{ appHost.supports('displaymode') && (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-layout-label'>{globalize.translate('LabelDisplayMode')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
aria-describedby='display-settings-layout-description'
|
aria-describedby='display-settings-layout-description'
|
||||||
inputProps={{
|
inputProps={{
|
||||||
name: 'layout'
|
name: 'layout'
|
||||||
}}
|
}}
|
||||||
label={globalize.translate('LabelDisplayMode')}
|
labelId='display-settings-layout-label'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={values.layout}
|
value={values.layout}
|
||||||
>
|
>
|
||||||
|
@ -48,13 +54,16 @@ export function DisplayPreferences({ onChange, values }: Readonly<DisplayPrefere
|
||||||
<span>{globalize.translate('LabelPleaseRestart')}</span>
|
<span>{globalize.translate('LabelPleaseRestart')}</span>
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
) }
|
||||||
|
|
||||||
|
{ themes.length > 0 && (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-theme-label'>{globalize.translate('LabelTheme')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
inputProps={{
|
inputProps={{
|
||||||
name: 'theme'
|
name: 'theme'
|
||||||
}}
|
}}
|
||||||
label={globalize.translate('LabelTheme')}
|
labelId='display-settings-theme-label'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={values.theme}
|
value={values.theme}
|
||||||
>
|
>
|
||||||
|
@ -63,6 +72,7 @@ export function DisplayPreferences({ onChange, values }: Readonly<DisplayPrefere
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
) }
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
@ -84,7 +94,7 @@ export function DisplayPreferences({ onChange, values }: Readonly<DisplayPrefere
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<TextField
|
<TextField
|
||||||
aria-describedby='display-settings-custom-css-description'
|
aria-describedby='display-settings-custom-css-description'
|
||||||
defaultValue={values.customCss}
|
value={values.customCss}
|
||||||
label={globalize.translate('LabelCustomCss')}
|
label={globalize.translate('LabelCustomCss')}
|
||||||
multiline
|
multiline
|
||||||
name='customCss'
|
name='customCss'
|
||||||
|
@ -95,15 +105,33 @@ export function DisplayPreferences({ onChange, values }: Readonly<DisplayPrefere
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
{/* TODO: There are some admin-only options here */}
|
{ themes.length > 0 && user?.Policy?.IsAdministrator && (
|
||||||
{/* Server Dashboard Theme */}
|
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-dashboard-theme-label'>{globalize.translate('LabelDashboardTheme')}</InputLabel>
|
||||||
|
<Select
|
||||||
|
inputProps={{
|
||||||
|
name: 'dashboardTheme'
|
||||||
|
}}
|
||||||
|
labelId='display-settings-dashboard-theme-label'
|
||||||
|
onChange={ onChange }
|
||||||
|
value={ values.dashboardTheme }
|
||||||
|
>
|
||||||
|
{ ...themes.map(({ id, name }) => (
|
||||||
|
<MenuItem key={ id } value={ id }>{ name }</MenuItem>
|
||||||
|
)) }
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
) }
|
||||||
|
|
||||||
|
{ screensavers.length > 0 && appHost.supports('screensaver') && (
|
||||||
|
<Fragment>
|
||||||
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-screensaver-label'>{globalize.translate('LabelScreensaver')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
inputProps={{
|
inputProps={{
|
||||||
name: 'screensaver'
|
name: 'screensaver'
|
||||||
}}
|
}}
|
||||||
label={globalize.translate('LabelScreensaver')}
|
labelId='display-settings-screensaver-label'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={values.screensaver}
|
value={values.screensaver}
|
||||||
>
|
>
|
||||||
|
@ -113,7 +141,29 @@ export function DisplayPreferences({ onChange, values }: Readonly<DisplayPrefere
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
{/* TODO: There are some extra options here related to screensavers */}
|
<FormControl fullWidth>
|
||||||
|
<TextField
|
||||||
|
aria-describedby='display-settings-screensaver-interval-description'
|
||||||
|
value={values.screensaverInterval}
|
||||||
|
inputProps={{
|
||||||
|
inputMode: 'numeric',
|
||||||
|
max: '3600',
|
||||||
|
min: '1',
|
||||||
|
pattern: '[0-9]',
|
||||||
|
required: true,
|
||||||
|
step: '1',
|
||||||
|
type: 'number'
|
||||||
|
}}
|
||||||
|
label={globalize.translate('LabelBackdropScreensaverInterval')}
|
||||||
|
name='screensaverInterval'
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
<FormHelperText id='display-settings-screensaver-interval-description'>
|
||||||
|
{globalize.translate('LabelBackdropScreensaverIntervalHelp')}
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
</Fragment>
|
||||||
|
) }
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
|
|
|
@ -17,7 +17,7 @@ interface LibraryPreferencesProps {
|
||||||
|
|
||||||
export function LibraryPreferences({ onChange, values }: Readonly<LibraryPreferencesProps>) {
|
export function LibraryPreferences({ onChange, values }: Readonly<LibraryPreferencesProps>) {
|
||||||
return (
|
return (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={3}>
|
||||||
<Typography variant='h2'>{globalize.translate('HeaderLibraries')}</Typography>
|
<Typography variant='h2'>{globalize.translate('HeaderLibraries')}</Typography>
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
@ -32,7 +32,7 @@ export function LibraryPreferences({ onChange, values }: Readonly<LibraryPrefere
|
||||||
required: true,
|
required: true,
|
||||||
step: '1'
|
step: '1'
|
||||||
}}
|
}}
|
||||||
defaultValue={values.libraryPageSize}
|
value={values.libraryPageSize}
|
||||||
label={globalize.translate('LabelLibraryPageSize')}
|
label={globalize.translate('LabelLibraryPageSize')}
|
||||||
name='libraryPageSize'
|
name='libraryPageSize'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import FormControl from '@mui/material/FormControl';
|
import FormControl from '@mui/material/FormControl';
|
||||||
import FormHelperText from '@mui/material/FormHelperText';
|
import FormHelperText from '@mui/material/FormHelperText';
|
||||||
|
import InputLabel from '@mui/material/InputLabel';
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
import Select, { SelectChangeEvent } from '@mui/material/Select';
|
||||||
|
@ -7,6 +8,8 @@ import Stack from '@mui/material/Stack';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { appHost } from 'components/apphost';
|
||||||
|
import datetime from 'scripts/datetime';
|
||||||
import globalize from 'scripts/globalize';
|
import globalize from 'scripts/globalize';
|
||||||
import { DATE_LOCALE_OPTIONS, LANGUAGE_OPTIONS } from './constants';
|
import { DATE_LOCALE_OPTIONS, LANGUAGE_OPTIONS } from './constants';
|
||||||
import { DisplaySettingsValues } from './types';
|
import { DisplaySettingsValues } from './types';
|
||||||
|
@ -17,17 +20,22 @@ interface LocalizationPreferencesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LocalizationPreferences({ onChange, values }: Readonly<LocalizationPreferencesProps>) {
|
export function LocalizationPreferences({ onChange, values }: Readonly<LocalizationPreferencesProps>) {
|
||||||
|
if (!appHost.supports('displaylanguage') && !datetime.supportsLocalization()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={3}>
|
||||||
<Typography variant='h2'>{globalize.translate('Localization')}</Typography>
|
<Typography variant='h2'>{globalize.translate('Localization')}</Typography>
|
||||||
|
|
||||||
|
{ appHost.supports('displaylanguage') && (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-language-label'>{globalize.translate('LabelDisplayLanguage')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
aria-describedby='display-settings-language-description'
|
aria-describedby='display-settings-language-description'
|
||||||
inputProps={{
|
inputProps={{
|
||||||
name: 'language'
|
name: 'language'
|
||||||
}}
|
}}
|
||||||
label={globalize.translate('LabelDisplayLanguage')}
|
labelId='display-settings-language-label'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={values.language}
|
value={values.language}
|
||||||
>
|
>
|
||||||
|
@ -37,6 +45,7 @@ export function LocalizationPreferences({ onChange, values }: Readonly<Localizat
|
||||||
</Select>
|
</Select>
|
||||||
<FormHelperText component={Stack} id='display-settings-language-description'>
|
<FormHelperText component={Stack} id='display-settings-language-description'>
|
||||||
<span>{globalize.translate('LabelDisplayLanguageHelp')}</span>
|
<span>{globalize.translate('LabelDisplayLanguageHelp')}</span>
|
||||||
|
{ appHost.supports('externallinks') && (
|
||||||
<Link
|
<Link
|
||||||
href='https://github.com/jellyfin/jellyfin'
|
href='https://github.com/jellyfin/jellyfin'
|
||||||
rel='noopener noreferrer'
|
rel='noopener noreferrer'
|
||||||
|
@ -44,15 +53,19 @@ export function LocalizationPreferences({ onChange, values }: Readonly<Localizat
|
||||||
>
|
>
|
||||||
{globalize.translate('LearnHowYouCanContribute')}
|
{globalize.translate('LearnHowYouCanContribute')}
|
||||||
</Link>
|
</Link>
|
||||||
|
) }
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
) }
|
||||||
|
|
||||||
|
{ datetime.supportsLocalization() && (
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
|
<InputLabel id='display-settings-locale-label'>{globalize.translate('LabelDateTimeLocale')}</InputLabel>
|
||||||
<Select
|
<Select
|
||||||
inputProps={{
|
inputProps={{
|
||||||
name: 'dateTimeLocale'
|
name: 'dateTimeLocale'
|
||||||
}}
|
}}
|
||||||
label={globalize.translate('LabelDateTimeLocale')}
|
labelId='display-settings-locale-label'
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={values.dateTimeLocale}
|
value={values.dateTimeLocale}
|
||||||
>
|
>
|
||||||
|
@ -61,6 +74,7 @@ export function LocalizationPreferences({ onChange, values }: Readonly<Localizat
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
) }
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ interface NextUpPreferencesProps {
|
||||||
|
|
||||||
export function NextUpPreferences({ onChange, values }: Readonly<NextUpPreferencesProps>) {
|
export function NextUpPreferences({ onChange, values }: Readonly<NextUpPreferencesProps>) {
|
||||||
return (
|
return (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={3}>
|
||||||
<Typography variant='h2'>{globalize.translate('NextUp')}</Typography>
|
<Typography variant='h2'>{globalize.translate('NextUp')}</Typography>
|
||||||
|
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<TextField
|
<TextField
|
||||||
aria-describedby='display-settings-max-days-next-up-description'
|
aria-describedby='display-settings-max-days-next-up-description'
|
||||||
defaultValue={values.maxDaysForNextUp}
|
value={values.maxDaysForNextUp}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
type: 'number',
|
type: 'number',
|
||||||
inputMode: 'numeric',
|
inputMode: 'numeric',
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default function UserDisplayPreferences() {
|
||||||
const handleFieldChange = useCallback((e: SelectChangeEvent | React.SyntheticEvent) => {
|
const handleFieldChange = useCallback((e: SelectChangeEvent | React.SyntheticEvent) => {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
const fieldName = target.name as keyof DisplaySettingsValues;
|
const fieldName = target.name as keyof DisplaySettingsValues;
|
||||||
const fieldValue = target.checked ?? target.value;
|
const fieldValue = target.type === 'checkbox' ? target.checked : target.value;
|
||||||
|
|
||||||
if (values?.[fieldName] !== fieldValue) {
|
if (values?.[fieldName] !== fieldValue) {
|
||||||
updateField({
|
updateField({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue