import { InputAdornment, ListItem, ListItemText, Stack, TextField, } from '@mui/material' import { useLockFn } from 'ahooks' import { Fragment, useMemo, useState, type ChangeEvent } from 'react' import { useTranslation } from 'react-i18next' import { Switch } from '@/components/base' import { useVerge } from '@/hooks/use-verge' import { showNotice } from '@/services/notice-service' const MIN_INTERVAL_HOURS = 1 const MAX_INTERVAL_HOURS = 168 interface AutoBackupState { scheduleEnabled: boolean intervalHours: number changeEnabled: boolean } export function AutoBackupSettings() { const { t } = useTranslation() const { verge, patchVerge } = useVerge() const derivedValues = useMemo(() => { return { scheduleEnabled: verge?.enable_auto_backup_schedule ?? false, intervalHours: verge?.auto_backup_interval_hours ?? 24, changeEnabled: verge?.auto_backup_on_change ?? true, } }, [ verge?.enable_auto_backup_schedule, verge?.auto_backup_interval_hours, verge?.auto_backup_on_change, ]) const [pendingValues, setPendingValues] = useState( null, ) const values = useMemo(() => { if (!pendingValues) { return derivedValues } if ( pendingValues.scheduleEnabled === derivedValues.scheduleEnabled && pendingValues.intervalHours === derivedValues.intervalHours && pendingValues.changeEnabled === derivedValues.changeEnabled ) { return derivedValues } return pendingValues }, [pendingValues, derivedValues]) const [intervalInputDraft, setIntervalInputDraft] = useState( null, ) const applyPatch = useLockFn( async ( partial: Partial, payload: Partial, ) => { const nextValues = { ...values, ...partial } setPendingValues(nextValues) try { await patchVerge(payload) } catch (error) { showNotice.error(error) setPendingValues(null) } }, ) const disabled = !verge const handleScheduleToggle = ( _: ChangeEvent, checked: boolean, ) => { applyPatch( { scheduleEnabled: checked }, { enable_auto_backup_schedule: checked, auto_backup_interval_hours: values.intervalHours, }, ) } const handleChangeToggle = ( _: ChangeEvent, checked: boolean, ) => { applyPatch({ changeEnabled: checked }, { auto_backup_on_change: checked }) } const handleIntervalInputChange = (event: ChangeEvent) => { setIntervalInputDraft(event.target.value) } const commitIntervalInput = () => { const rawValue = intervalInputDraft ?? values.intervalHours.toString() const trimmed = rawValue.trim() if (trimmed === '') { setIntervalInputDraft(null) return } const parsed = Number(trimmed) if (!Number.isFinite(parsed)) { setIntervalInputDraft(null) return } const clamped = Math.min( MAX_INTERVAL_HOURS, Math.max(MIN_INTERVAL_HOURS, Math.round(parsed)), ) if (clamped === values.intervalHours) { setIntervalInputDraft(null) return } applyPatch( { intervalHours: clamped }, { auto_backup_interval_hours: clamped }, ) setIntervalInputDraft(null) } const scheduleDisabled = disabled || !values.scheduleEnabled return ( { if (event.key === 'Enter') { event.preventDefault() commitIntervalInput() } }} sx={{ minWidth: 160 }} slotProps={{ input: { endAdornment: ( {t('shared.units.hours')} ), }, htmlInput: { min: MIN_INTERVAL_HOURS, max: MAX_INTERVAL_HOURS, inputMode: 'numeric', }, }} /> ) }