diff --git a/web/src/pages/SettingsPage.tsx b/web/src/pages/SettingsPage.tsx index 8665631..c9f9ee6 100644 --- a/web/src/pages/SettingsPage.tsx +++ b/web/src/pages/SettingsPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState, type FormEvent, type ReactNode } from 'react' +import { useCallback, useEffect, useRef, useState, type FormEvent, type ReactNode } from 'react' import { useNavigate } from 'react-router-dom' import { api } from '../api.js' import { useAuth } from '../auth.js' @@ -32,6 +32,8 @@ export default function SettingsPage() { const [newUsername, setNewUsername] = useState('') const [newPassword, setNewPassword] = useState('') + const stopPollRef = useRef<(() => void) | null>(null) + const refreshSettings = useCallback(() => { void api .getSettings() @@ -47,26 +49,32 @@ export default function SettingsPage() { refreshSettings() }, [refreshSettings]) - useEffect(() => { + const pollSync = useCallback(() => { let alive = true - const poll = (): void => { + const tick = (): void => { void api .syncStatus() .then((s) => { - if (alive) setSync(s) + if (!alive) return s + setSync(s) return s }) .then((s) => { - if (alive && s?.status === 'running') setTimeout(poll, 2000) + if (alive && s?.status === 'running') setTimeout(tick, 2000) }) .catch(() => {}) } - poll() + tick() return () => { alive = false } }, []) + useEffect(() => { + stopPollRef.current = pollSync() + return () => stopPollRef.current?.() + }, [pollSync]) + useEffect(() => { if (user?.isAdmin) { void api @@ -95,8 +103,10 @@ export default function SettingsPage() { function saveSubsonic(e: FormEvent) { e.preventDefault() + const payload: Parameters[0] = { subsonicUrl, subsonicUsername } + if (subsonicPassword !== '') payload.subsonicPassword = subsonicPassword void api - .putSettings({ subsonicUrl, subsonicUsername, subsonicPassword }) + .putSettings(payload) .then((v) => { setView(v) setSubsonicPassword('') @@ -161,7 +171,7 @@ export default function SettingsPage() { onChange={(e) => setDiscogsToken(e.target.value)} className={inputCls} autoComplete="off" - placeholder={view?.hasDiscogsToken ? 'Leave empty to keep, clear to remove' : 'Paste token'} + placeholder={view?.hasDiscogsToken ? 'Paste token (saving empty removes it)' : 'Paste token'} />