Add save button to DM decrypt attempts and keep settings state consistent with serverside

This commit is contained in:
Jack Kingsman
2026-01-19 12:29:25 -08:00
parent a7d0a8f20b
commit 3cc139e3a5
8 changed files with 56 additions and 39 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -13,8 +13,8 @@
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/site.webmanifest" />
<script type="module" crossorigin src="/assets/index-BeFZihz7.js"></script> <script type="module" crossorigin src="/assets/index-CgdvRjQo.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CnRBRJ10.css"> <link rel="stylesheet" crossorigin href="/assets/index-DTfLIjxa.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+1
View File
@@ -967,6 +967,7 @@ export function App() {
const data = await api.getHealth(); const data = await api.getHealth();
setHealth(data); setHealth(data);
}} }}
onRefreshAppSettings={fetchAppSettings}
/> />
<Toaster position="top-right" /> <Toaster position="top-right" />
+30 -14
View File
@@ -55,6 +55,7 @@ interface SettingsModalProps {
onReboot: () => Promise<void>; onReboot: () => Promise<void>;
onAdvertise: () => Promise<void>; onAdvertise: () => Promise<void>;
onHealthRefresh: () => Promise<void>; onHealthRefresh: () => Promise<void>;
onRefreshAppSettings: () => Promise<void>;
} }
export function SettingsModal({ export function SettingsModal({
@@ -69,6 +70,7 @@ export function SettingsModal({
onReboot, onReboot,
onAdvertise, onAdvertise,
onHealthRefresh, onHealthRefresh,
onRefreshAppSettings,
}: SettingsModalProps) { }: SettingsModalProps) {
// Tab state // Tab state
type SettingsTab = 'radio' | 'identity' | 'serial' | 'database' | 'advertise'; type SettingsTab = 'radio' | 'identity' | 'serial' | 'database' | 'advertise';
@@ -118,6 +120,14 @@ export function SettingsModal({
} }
}, [appSettings]); }, [appSettings]);
// Refresh settings from server when modal opens
// This ensures UI reflects actual server state (prevents stale UI after checkbox toggle without save)
useEffect(() => {
if (open) {
onRefreshAppSettings();
}
}, [open, onRefreshAppSettings]);
// Detect current preset from form values // Detect current preset from form values
const currentPreset = useMemo(() => { const currentPreset = useMemo(() => {
const freqNum = parseFloat(freq); const freqNum = parseFloat(freq);
@@ -316,16 +326,19 @@ export function SettingsModal({
} }
}; };
const handleToggleAutoDecrypt = async () => { const handleSaveDatabaseSettings = async () => {
const newValue = !autoDecryptOnAdvert; setLoading(true);
setAutoDecryptOnAdvert(newValue); // Optimistic update setError('');
try { try {
await onSaveAppSettings({ auto_decrypt_dm_on_advert: newValue }); await onSaveAppSettings({ auto_decrypt_dm_on_advert: autoDecryptOnAdvert });
toast.success('Database settings saved');
} catch (err) { } catch (err) {
console.error('Failed to save auto-decrypt setting:', err); console.error('Failed to save database settings:', err);
setAutoDecryptOnAdvert(!newValue); // Revert on error setError(err instanceof Error ? err.message : 'Failed to save');
toast.error('Failed to save setting'); toast.error('Failed to save settings');
} finally {
setLoading(false);
} }
}; };
@@ -658,26 +671,29 @@ export function SettingsModal({
<div className="space-y-3"> <div className="space-y-3">
<Label>DM Decryption</Label> <Label>DM Decryption</Label>
<div <label className="flex items-center gap-3 cursor-pointer">
className="flex items-center gap-3 cursor-pointer"
onClick={handleToggleAutoDecrypt}
>
<input <input
type="checkbox" type="checkbox"
checked={autoDecryptOnAdvert} checked={autoDecryptOnAdvert}
readOnly onChange={(e) => setAutoDecryptOnAdvert(e.target.checked)}
className="w-4 h-4 rounded border-input accent-primary pointer-events-none" className="w-4 h-4 rounded border-input accent-primary"
/> />
<span className="text-sm"> <span className="text-sm">
Auto-decrypt historical DMs when new contact advertises Auto-decrypt historical DMs when new contact advertises
</span> </span>
</div> </label>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
When enabled, the server will automatically try to decrypt stored DM packets when When enabled, the server will automatically try to decrypt stored DM packets when
a new contact sends an advertisement. This may cause brief delays on large packet a new contact sends an advertisement. This may cause brief delays on large packet
backlogs. backlogs.
</p> </p>
</div> </div>
{error && <div className="text-sm text-destructive">{error}</div>}
<Button onClick={handleSaveDatabaseSettings} disabled={loading} className="w-full">
{loading ? 'Saving...' : 'Save Settings'}
</Button>
</TabsContent> </TabsContent>
{/* Advertise Tab */} {/* Advertise Tab */}