import type { BrandingOptions } from '@jellyfin/sdk/lib/generated-client/models/branding-options'; import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api'; import Alert from '@mui/material/Alert'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import FormControlLabel from '@mui/material/FormControlLabel'; import Stack from '@mui/material/Stack'; import Switch from '@mui/material/Switch'; import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import React, { useCallback, useState } from 'react'; import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom'; import { getBrandingOptionsQuery, QUERY_KEY, useBrandingOptions } from 'apps/dashboard/features/branding/api/useBrandingOptions'; import Loading from 'components/loading/LoadingComponent'; import Page from 'components/Page'; import ServerConnections from 'components/ServerConnections'; import globalize from 'lib/globalize'; import { queryClient } from 'utils/query/queryClient'; import { ActionData } from 'types/actionData'; const BRANDING_CONFIG_KEY = 'branding'; const BrandingOption = { CustomCss: 'CustomCss', LoginDisclaimer: 'LoginDisclaimer', SplashscreenEnabled: 'SplashscreenEnabled' }; export const action = async ({ request }: ActionFunctionArgs) => { const api = ServerConnections.getCurrentApi(); if (!api) throw new Error('No Api instance available'); const formData = await request.formData(); const data = Object.fromEntries(formData); const brandingOptions: BrandingOptions = { CustomCss: data.CustomCss?.toString(), LoginDisclaimer: data.LoginDisclaimer?.toString(), SplashscreenEnabled: data.SplashscreenEnabled?.toString() === 'on' }; await getConfigurationApi(api) .updateNamedConfiguration({ key: BRANDING_CONFIG_KEY, body: JSON.stringify(brandingOptions) }); void queryClient.invalidateQueries({ queryKey: [ QUERY_KEY ] }); return { isSaved: true }; }; export const loader = () => { return queryClient.ensureQueryData( getBrandingOptionsQuery(ServerConnections.getCurrentApi())); }; export const Component = () => { const navigation = useNavigation(); const actionData = useActionData() as ActionData | undefined; const isSubmitting = navigation.state === 'submitting'; const { data: defaultBrandingOptions, isPending } = useBrandingOptions(); const [ brandingOptions, setBrandingOptions ] = useState(defaultBrandingOptions || {}); const setSplashscreenEnabled = useCallback((_: React.ChangeEvent, isEnabled: boolean) => { setBrandingOptions({ ...brandingOptions, [BrandingOption.SplashscreenEnabled]: isEnabled }); }, [ brandingOptions ]); const setBrandingOption = useCallback((event: React.ChangeEvent) => { if (Object.keys(BrandingOption).includes(event.target.name)) { setBrandingOptions({ ...brandingOptions, [event.target.name]: event.target.value }); } }, [ brandingOptions ]); if (isPending) return ; return (
{globalize.translate('HeaderBranding')} {!isSubmitting && actionData?.isSaved && ( {globalize.translate('SettingsSaved')} )} } label={globalize.translate('EnableSplashScreen')} />
); }; Component.displayName = 'BrandingPage';