import { useEffect, useState } from 'react'; import type { ConfigResponse, ConfigUpdateRequest } from '../../../../../packages/api-contracts/src/index.js'; import { AccessPanel } from './access-panel.js'; import { RiskDialog } from './risk-dialog.js'; import { useSaveSettingsMutation } from './settings-queries.js'; import { createSettingsDraft, isDraftDirty, validateSettingsDraft, type SettingsDraft } from './settings-model.js'; import { BehaviourSection, MonitoringSection, RateLimitsSection } from './settings-sections.js'; import type { SettingsSection } from './settings-page.js'; interface SettingsEditorProps { config: ConfigResponse; section: SettingsSection; } interface PendingRiskySave { payload: ConfigUpdateRequest; reasons: string[]; } function errorMessage(error: Error | null): string { return error?.message ?? 'Something went wrong.'; } export function SettingsEditor({ config, section }: SettingsEditorProps) { const [persistedDraft, setPersistedDraft] = useState(() => createSettingsDraft(config)); const [draft, setDraft] = useState(persistedDraft); const [errors, setErrors] = useState>({}); const [pendingRiskySave, setPendingRiskySave] = useState(null); const saveMutation = useSaveSettingsMutation(); const isDirty = isDraftDirty(draft, persistedDraft); useEffect(() => { function warnBeforeUnload(event: BeforeUnloadEvent) { if (!isDirty) return; event.preventDefault(); event.returnValue = ''; } window.addEventListener('beforeunload', warnBeforeUnload); return () => window.removeEventListener('beforeunload', warnBeforeUnload); }, [isDirty]); function applySavedResponse(response: Awaited>) { const nextConfig: ConfigResponse = { ...response.config, subreddits: response.subreddits, tones: config.tones, advanced: response.advanced ?? config.advanced, }; const nextDraft = createSettingsDraft(nextConfig); setPersistedDraft(nextDraft); setDraft(nextDraft); setPendingRiskySave(null); } function submit(payload: ConfigUpdateRequest) { saveMutation.mutate(payload, { onSuccess: applySavedResponse }); } function updateDraft(nextDraft: SettingsDraft) { saveMutation.reset(); setErrors({}); setDraft(nextDraft); } function prepareSave() { saveMutation.reset(); const validation = validateSettingsDraft(draft); setErrors(validation.errors); if (!validation.data) return; const reasons: string[] = []; if (persistedDraft.dryRun && !validation.data.dryRun) { reasons.push('Automated messages will be sent to Reddit because dry run mode is being disabled.'); } const persistedLimit = Number(persistedDraft.maxMessagesPerHour); if (validation.data.maxMessagesPerHour > 20 && validation.data.maxMessagesPerHour > persistedLimit) { reasons.push(`The limit will allow ${validation.data.maxMessagesPerHour} messages per hour.`); } if (reasons.length > 0) { setPendingRiskySave({ payload: validation.data.payload, reasons }); return; } submit(validation.data.payload); } return ( <> {section === 'monitoring' ? : null} {section === 'rate-limits' ? : null} {section === 'behaviour' ? : null} {section === 'access' ? : null} {section !== 'access' ? (
{isDirty ? You have unsaved changes. : All changes saved.} {saveMutation.isSuccess ? Settings saved. : null} {saveMutation.isError ? {errorMessage(saveMutation.error)} : null}
) : null} {pendingRiskySave ? ( setPendingRiskySave(null)} onConfirm={() => submit(pendingRiskySave.payload)} reasons={pendingRiskySave.reasons} /> ) : null} ); }