added rpc polling time selector
This commit is contained in:
@@ -4,7 +4,6 @@ import {
|
||||
Container,
|
||||
FormControl,
|
||||
FormControlLabel,
|
||||
FormGroup,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
InputLabel,
|
||||
@@ -12,6 +11,7 @@ import {
|
||||
Paper,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
Slider,
|
||||
Stack,
|
||||
Switch,
|
||||
TextField,
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
map,
|
||||
takeWhile
|
||||
} from 'rxjs'
|
||||
import { rpcPollingTimeState } from '../atoms/rpc'
|
||||
import {
|
||||
Language,
|
||||
Theme,
|
||||
@@ -55,15 +56,20 @@ import { validateDomain, validateIP } from '../utils'
|
||||
export default function Settings() {
|
||||
const [reverseProxy, setReverseProxy] = useRecoilState(servedFromReverseProxyState)
|
||||
const [baseURL, setBaseURL] = useRecoilState(servedFromReverseProxySubDirState)
|
||||
|
||||
const [formatSelection, setFormatSelection] = useRecoilState(formatSelectionState)
|
||||
const [pathOverriding, setPathOverriding] = useRecoilState(pathOverridingState)
|
||||
const [fileRenaming, setFileRenaming] = useRecoilState(fileRenamingState)
|
||||
const [enableArgs, setEnableArgs] = useRecoilState(enableCustomArgsState)
|
||||
|
||||
const [serverAddr, setServerAddr] = useRecoilState(serverAddressState)
|
||||
const [serverPort, setServerPort] = useRecoilState(serverPortState)
|
||||
const [cliArgs, setCliArgs] = useRecoilState(latestCliArgumentsState)
|
||||
|
||||
const [pollingTime, setPollingTime] = useRecoilState(rpcPollingTimeState)
|
||||
const [language, setLanguage] = useRecoilState(languageState)
|
||||
const [appTitle, setApptitle] = useRecoilState(appTitleState)
|
||||
const [cliArgs, setCliArgs] = useRecoilState(latestCliArgumentsState)
|
||||
|
||||
const [theme, setTheme] = useRecoilState(themeState)
|
||||
|
||||
const [invalidIP, setInvalidIP] = useState(false)
|
||||
@@ -148,216 +154,238 @@ export default function Settings() {
|
||||
|
||||
return (
|
||||
<Container maxWidth="xl" sx={{ mt: 4, mb: 8 }}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<Paper
|
||||
sx={{
|
||||
p: 2.5,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 240,
|
||||
}}
|
||||
>
|
||||
<Typography pb={2} variant="h6" color="primary">
|
||||
{i18n.t('settingsAnchor')}
|
||||
<Paper
|
||||
sx={{
|
||||
p: 2.5,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
minHeight: 240,
|
||||
}}
|
||||
>
|
||||
<Typography pb={2} variant="h6" color="primary">
|
||||
{i18n.t('settingsAnchor')}
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={11}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={i18n.t('serverAddressTitle')}
|
||||
defaultValue={serverAddr}
|
||||
error={invalidIP}
|
||||
onChange={(e) => serverAddr$.next(e.currentTarget.value)}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">ws://</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={1}>
|
||||
<TextField
|
||||
disabled={reverseProxy}
|
||||
fullWidth
|
||||
label={i18n.t('serverPortTitle')}
|
||||
defaultValue={serverPort}
|
||||
onChange={(e) => serverPort$.next(e.currentTarget.value)}
|
||||
error={isNaN(Number(serverPort)) || Number(serverPort) > 65535}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<TextField
|
||||
disabled={reverseProxy}
|
||||
fullWidth
|
||||
label={i18n.t('appTitle')}
|
||||
defaultValue={appTitle}
|
||||
onChange={(e) => setApptitle(e.currentTarget.value)}
|
||||
error={appTitle === ''}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<Typography>
|
||||
{i18n.t('rpcPollingTimeTitle')}
|
||||
</Typography>
|
||||
<FormGroup>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={11}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={i18n.t('serverAddressTitle')}
|
||||
defaultValue={serverAddr}
|
||||
error={invalidIP}
|
||||
onChange={(e) => serverAddr$.next(e.currentTarget.value)}
|
||||
InputProps={{
|
||||
startAdornment: <InputAdornment position="start">ws://</InputAdornment>,
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={1}>
|
||||
<TextField
|
||||
disabled={reverseProxy}
|
||||
fullWidth
|
||||
label={i18n.t('serverPortTitle')}
|
||||
defaultValue={serverPort}
|
||||
onChange={(e) => serverPort$.next(e.currentTarget.value)}
|
||||
error={isNaN(Number(serverPort)) || Number(serverPort) > 65535}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={12}>
|
||||
<TextField
|
||||
disabled={reverseProxy}
|
||||
fullWidth
|
||||
label={i18n.t('appTitle')}
|
||||
defaultValue={appTitle}
|
||||
onChange={(e) => setApptitle(e.currentTarget.value)}
|
||||
error={appTitle === ''}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6" color="primary" sx={{ mb: 0.5 }}>
|
||||
Reverse Proxy
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
defaultChecked={reverseProxy}
|
||||
onChange={() => setReverseProxy(state => !state)}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('servedFromReverseProxyCheckbox')}
|
||||
sx={{ mb: 1 }}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={i18n.t('urlBase')}
|
||||
defaultValue={baseURL}
|
||||
onChange={(e) => {
|
||||
let value = e.currentTarget.value
|
||||
if (value.startsWith('/')) {
|
||||
value = value.substring(1)
|
||||
}
|
||||
if (value.endsWith('/')) {
|
||||
value = value.substring(0, value.length - 1)
|
||||
}
|
||||
baseURL$.next(value)
|
||||
}}
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 0.5, mb: 2 }}>
|
||||
Appaerance
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>{i18n.t('languageSelect')}</InputLabel>
|
||||
<Select
|
||||
defaultValue={language}
|
||||
label={i18n.t('languageSelect')}
|
||||
onChange={handleLanguageChange}
|
||||
>
|
||||
{languages.toSorted((a, b) => a.localeCompare(b)).map(l => (
|
||||
<MenuItem value={l} key={l}>
|
||||
{capitalize(l)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>{i18n.t('themeSelect')}</InputLabel>
|
||||
<Select
|
||||
defaultValue={theme}
|
||||
label={i18n.t('themeSelect')}
|
||||
onChange={handleThemeChange}
|
||||
>
|
||||
<MenuItem value="light">Light</MenuItem>
|
||||
<MenuItem value="dark">Dark</MenuItem>
|
||||
<MenuItem value="system">System</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 2, mb: 0.5 }}>
|
||||
General download settings
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={argsBuilder.noMTime}
|
||||
onChange={() => setCliArgs(argsBuilder.toggleNoMTime().toString())}
|
||||
/>
|
||||
<Typography variant='caption' sx={{ mb: 0.5 }}>
|
||||
{i18n.t('rpcPollingTimeDescription')}
|
||||
</Typography>
|
||||
<Slider
|
||||
aria-label="rpc polling time"
|
||||
defaultValue={pollingTime}
|
||||
max={2000}
|
||||
getAriaValueText={(v: number) => `${v} ms`}
|
||||
step={null}
|
||||
valueLabelDisplay="off"
|
||||
marks={[
|
||||
{ value: 100, label: '100 ms' },
|
||||
{ value: 250, label: '250 ms' },
|
||||
{ value: 500, label: '500 ms' },
|
||||
{ value: 750, label: '750 ms' },
|
||||
{ value: 1000, label: '1000 ms' },
|
||||
{ value: 2000, label: '2000 ms' },
|
||||
]}
|
||||
onChange={(_, value) => typeof value === 'number'
|
||||
? setPollingTime(value)
|
||||
: setPollingTime(1000)
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6" color="primary" sx={{ mb: 0.5 }}>
|
||||
Reverse Proxy
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
defaultChecked={reverseProxy}
|
||||
onChange={() => setReverseProxy(state => !state)}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('servedFromReverseProxyCheckbox')}
|
||||
sx={{ mb: 1 }}
|
||||
/>
|
||||
<TextField
|
||||
fullWidth
|
||||
label={i18n.t('urlBase')}
|
||||
defaultValue={baseURL}
|
||||
onChange={(e) => {
|
||||
let value = e.currentTarget.value
|
||||
if (value.startsWith('/')) {
|
||||
value = value.substring(1)
|
||||
}
|
||||
label={i18n.t('noMTimeCheckbox')}
|
||||
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={argsBuilder.extractAudio}
|
||||
onChange={() => setCliArgs(argsBuilder.toggleExtractAudio().toString())}
|
||||
disabled={formatSelection}
|
||||
/>
|
||||
if (value.endsWith('/')) {
|
||||
value = value.substring(0, value.length - 1)
|
||||
}
|
||||
label={i18n.t('extractAudioCheckbox')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={formatSelection}
|
||||
onChange={() => {
|
||||
setCliArgs(argsBuilder.disableExtractAudio().toString())
|
||||
setFormatSelection(!formatSelection)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('formatSelectionEnabler')}
|
||||
/>
|
||||
<Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 2, mb: 0.5 }}>
|
||||
{i18n.t('overridesAnchor')}
|
||||
</Typography>
|
||||
<Stack direction="column">
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={!!pathOverriding}
|
||||
onChange={() => {
|
||||
setPathOverriding(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('pathOverrideOption')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={fileRenaming}
|
||||
onChange={() => {
|
||||
setFileRenaming(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('filenameOverrideOption')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={enableArgs}
|
||||
onChange={() => {
|
||||
setEnableArgs(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('customArgs')}
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid sx={{ mr: 1, mt: 2 }}>
|
||||
<Typography variant="h6" color="primary" sx={{ mb: 2 }}>
|
||||
Cookies
|
||||
</Typography>
|
||||
<CookiesTextField />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Stack direction="row">
|
||||
<Button
|
||||
sx={{ mr: 1, mt: 3 }}
|
||||
variant="contained"
|
||||
onClick={() => updateBinary()}
|
||||
>
|
||||
{i18n.t('updateBinButton')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</FormGroup>
|
||||
</Paper>
|
||||
baseURL$.next(value)
|
||||
}}
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 0.5, mb: 2 }}>
|
||||
Appaerance
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>{i18n.t('languageSelect')}</InputLabel>
|
||||
<Select
|
||||
defaultValue={language}
|
||||
label={i18n.t('languageSelect')}
|
||||
onChange={handleLanguageChange}
|
||||
>
|
||||
{languages.toSorted((a, b) => a.localeCompare(b)).map(l => (
|
||||
<MenuItem value={l} key={l}>
|
||||
{capitalize(l)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>{i18n.t('themeSelect')}</InputLabel>
|
||||
<Select
|
||||
defaultValue={theme}
|
||||
label={i18n.t('themeSelect')}
|
||||
onChange={handleThemeChange}
|
||||
>
|
||||
<MenuItem value="light">Light</MenuItem>
|
||||
<MenuItem value="dark">Dark</MenuItem>
|
||||
<MenuItem value="system">System</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 2, mb: 0.5 }}>
|
||||
General download settings
|
||||
</Typography>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={argsBuilder.noMTime}
|
||||
onChange={() => setCliArgs(argsBuilder.toggleNoMTime().toString())}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('noMTimeCheckbox')}
|
||||
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={argsBuilder.extractAudio}
|
||||
onChange={() => setCliArgs(argsBuilder.toggleExtractAudio().toString())}
|
||||
disabled={formatSelection}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('extractAudioCheckbox')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={formatSelection}
|
||||
onChange={() => {
|
||||
setCliArgs(argsBuilder.disableExtractAudio().toString())
|
||||
setFormatSelection(!formatSelection)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('formatSelectionEnabler')}
|
||||
/>
|
||||
<Grid>
|
||||
<Typography variant="h6" color="primary" sx={{ mt: 2, mb: 0.5 }}>
|
||||
{i18n.t('overridesAnchor')}
|
||||
</Typography>
|
||||
<Stack direction="column">
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={!!pathOverriding}
|
||||
onChange={() => {
|
||||
setPathOverriding(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('pathOverrideOption')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={fileRenaming}
|
||||
onChange={() => {
|
||||
setFileRenaming(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('filenameOverrideOption')}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
defaultChecked={enableArgs}
|
||||
onChange={() => {
|
||||
setEnableArgs(state => !state)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={i18n.t('customArgs')}
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid sx={{ mr: 1, mt: 2 }}>
|
||||
<Typography variant="h6" color="primary" sx={{ mb: 2 }}>
|
||||
Cookies
|
||||
</Typography>
|
||||
<CookiesTextField />
|
||||
</Grid>
|
||||
<Grid>
|
||||
<Stack direction="row">
|
||||
<Button
|
||||
sx={{ mr: 1, mt: 3 }}
|
||||
variant="contained"
|
||||
onClick={() => updateBinary()}
|
||||
>
|
||||
{i18n.t('updateBinButton')}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user