diff --git a/frontend/src/atoms/rpc.ts b/frontend/src/atoms/rpc.ts
index e22dd87..82df50b 100644
--- a/frontend/src/atoms/rpc.ts
+++ b/frontend/src/atoms/rpc.ts
@@ -1,4 +1,4 @@
-import { selector } from 'recoil'
+import { atom, selector } from 'recoil'
import { RPCClient } from '../lib/rpcClient'
import { rpcHTTPEndpoint, rpcWebSocketEndpoint } from './settings'
@@ -12,3 +12,12 @@ export const rpcClientState = selector({
),
dangerouslyAllowMutability: true,
})
+
+export const rpcPollingTimeState = atom({
+ key: 'rpcPollingTimeState',
+ default: Number(localStorage.getItem('rpc-polling-time')) || 1000,
+ effects: [
+ ({ onSet }) =>
+ onSet(a => localStorage.setItem('rpc-polling-time', a.toString()))
+ ]
+})
\ No newline at end of file
diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx
index f530daf..32422ec 100644
--- a/frontend/src/components/Footer.tsx
+++ b/frontend/src/components/Footer.tsx
@@ -1,15 +1,15 @@
+import DownloadIcon from '@mui/icons-material/Download'
import SettingsEthernet from '@mui/icons-material/SettingsEthernet'
import { AppBar, Chip, Divider, Toolbar } from '@mui/material'
import { Suspense } from 'react'
import { useRecoilValue } from 'recoil'
import { settingsState } from '../atoms/settings'
import { connectedState } from '../atoms/status'
+import { totalDownloadSpeedState } from '../atoms/ui'
import { useI18n } from '../hooks/useI18n'
+import { formatSpeedMiB } from '../utils'
import FreeSpaceIndicator from './FreeSpaceIndicator'
import VersionIndicator from './VersionIndicator'
-import DownloadIcon from '@mui/icons-material/Download'
-import { totalDownloadSpeedState } from '../atoms/ui'
-import { formatSpeedMiB } from '../utils'
const Footer: React.FC = () => {
const settings = useRecoilValue(settingsState)
@@ -35,7 +35,7 @@ const Footer: React.FC = () => {
display: 'flex', gap: 1, justifyContent: 'space-between'
}}>
-
+
diff --git a/frontend/src/components/SocketSubscriber.tsx b/frontend/src/components/SocketSubscriber.tsx
index 2ad6e1a..91df41a 100644
--- a/frontend/src/components/SocketSubscriber.tsx
+++ b/frontend/src/components/SocketSubscriber.tsx
@@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { take, timer } from 'rxjs'
import { downloadsState } from '../atoms/downloads'
+import { rpcPollingTimeState } from '../atoms/rpc'
import { serverAddressAndPortState } from '../atoms/settings'
import { connectedState } from '../atoms/status'
import { useSubscription } from '../hooks/observable'
@@ -19,6 +20,7 @@ const SocketSubscriber: React.FC
= () => {
const [, setDownloads] = useRecoilState(downloadsState)
const serverAddressAndPort = useRecoilValue(serverAddressAndPortState)
+ const rpcPollingTime = useRecoilValue(rpcPollingTimeState)
const { i18n } = useI18n()
const { client } = useRPC()
@@ -70,11 +72,10 @@ const SocketSubscriber: React.FC = () => {
useEffect(() => {
if (connected) {
- const sub = timer(0, 1000).subscribe(() => client.running())
-
+ const sub = timer(0, rpcPollingTime).subscribe(() => client.running())
return () => sub.unsubscribe()
}
- }, [connected, client])
+ }, [connected, client, rpcPollingTime])
return null
}
diff --git a/frontend/src/views/Settings.tsx b/frontend/src/views/Settings.tsx
index 89a28c2..4eeacd7 100644
--- a/frontend/src/views/Settings.tsx
+++ b/frontend/src/views/Settings.tsx
@@ -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 (
-
-
-
-
- {i18n.t('settingsAnchor')}
+
+
+ {i18n.t('settingsAnchor')}
+
+
+
+ serverAddr$.next(e.currentTarget.value)}
+ InputProps={{
+ startAdornment: ws://,
+ }}
+ />
+
+
+ serverPort$.next(e.currentTarget.value)}
+ error={isNaN(Number(serverPort)) || Number(serverPort) > 65535}
+ />
+
+
+ setApptitle(e.currentTarget.value)}
+ error={appTitle === ''}
+ />
+
+
+
+ {i18n.t('rpcPollingTimeTitle')}
-
-
-
- serverAddr$.next(e.currentTarget.value)}
- InputProps={{
- startAdornment: ws://,
- }}
- />
-
-
- serverPort$.next(e.currentTarget.value)}
- error={isNaN(Number(serverPort)) || Number(serverPort) > 65535}
- />
-
-
- setApptitle(e.currentTarget.value)}
- error={appTitle === ''}
- />
-
-
-
- Reverse Proxy
-
- setReverseProxy(state => !state)}
- />
- }
- label={i18n.t('servedFromReverseProxyCheckbox')}
- sx={{ mb: 1 }}
- />
- {
- 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 }}
- />
-
-
-
- Appaerance
-
-
-
-
- {i18n.t('languageSelect')}
-
-
-
-
-
- {i18n.t('themeSelect')}
-
-
-
-
-
- General download settings
-
- setCliArgs(argsBuilder.toggleNoMTime().toString())}
- />
+
+ {i18n.t('rpcPollingTimeDescription')}
+
+ `${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)
+ }
+ />
+
+
+
+ Reverse Proxy
+
+ setReverseProxy(state => !state)}
+ />
+ }
+ label={i18n.t('servedFromReverseProxyCheckbox')}
+ sx={{ mb: 1 }}
+ />
+ {
+ let value = e.currentTarget.value
+ if (value.startsWith('/')) {
+ value = value.substring(1)
}
- label={i18n.t('noMTimeCheckbox')}
-
- />
- setCliArgs(argsBuilder.toggleExtractAudio().toString())}
- disabled={formatSelection}
- />
+ if (value.endsWith('/')) {
+ value = value.substring(0, value.length - 1)
}
- label={i18n.t('extractAudioCheckbox')}
- />
- {
- setCliArgs(argsBuilder.disableExtractAudio().toString())
- setFormatSelection(!formatSelection)
- }}
- />
- }
- label={i18n.t('formatSelectionEnabler')}
- />
-
-
- {i18n.t('overridesAnchor')}
-
-
- {
- setPathOverriding(state => !state)
- }}
- />
- }
- label={i18n.t('pathOverrideOption')}
- />
- {
- setFileRenaming(state => !state)
- }}
- />
- }
- label={i18n.t('filenameOverrideOption')}
- />
- {
- setEnableArgs(state => !state)
- }}
- />
- }
- label={i18n.t('customArgs')}
- />
-
-
-
-
- Cookies
-
-
-
-
-
-
-
-
-
-
+ baseURL$.next(value)
+ }}
+ sx={{ mb: 2 }}
+ />
+
-
+
+ Appaerance
+
+
+
+
+ {i18n.t('languageSelect')}
+
+
+
+
+
+ {i18n.t('themeSelect')}
+
+
+
+
+
+ General download settings
+
+ setCliArgs(argsBuilder.toggleNoMTime().toString())}
+ />
+ }
+ label={i18n.t('noMTimeCheckbox')}
+
+ />
+ setCliArgs(argsBuilder.toggleExtractAudio().toString())}
+ disabled={formatSelection}
+ />
+ }
+ label={i18n.t('extractAudioCheckbox')}
+ />
+ {
+ setCliArgs(argsBuilder.disableExtractAudio().toString())
+ setFormatSelection(!formatSelection)
+ }}
+ />
+ }
+ label={i18n.t('formatSelectionEnabler')}
+ />
+
+
+ {i18n.t('overridesAnchor')}
+
+
+ {
+ setPathOverriding(state => !state)
+ }}
+ />
+ }
+ label={i18n.t('pathOverrideOption')}
+ />
+ {
+ setFileRenaming(state => !state)
+ }}
+ />
+ }
+ label={i18n.t('filenameOverrideOption')}
+ />
+ {
+ setEnableArgs(state => !state)
+ }}
+ />
+ }
+ label={i18n.t('customArgs')}
+ />
+
+
+
+
+ Cookies
+
+
+
+
+
+
+
+
+
)
}