Move to multi-connection modality

This commit is contained in:
Jack Kingsman
2026-02-04 14:22:49 -08:00
committed by jkingsman
parent 64d261e6f9
commit a86d2d7cda
23 changed files with 427 additions and 63 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
+1 -1
View File
@@ -13,7 +13,7 @@
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<script type="module" crossorigin src="/assets/index-CWnjp-zX.js"></script>
<script type="module" crossorigin src="/assets/index-BteQsTFF.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DJA5wYVF.css">
</head>
<body>
+3 -1
View File
@@ -135,7 +135,9 @@ export function App() {
if (prev !== null && prev.radio_connected !== data.radio_connected) {
if (data.radio_connected) {
toast.success('Radio connected', {
description: data.serial_port ? `Connected to ${data.serial_port}` : undefined,
description: data.connection_info
? `Connected via ${data.connection_info}`
: undefined,
});
// Refresh config after reconnection (may have changed after reboot)
api.getRadioConfig().then(setConfig).catch(console.error);
+11 -11
View File
@@ -78,7 +78,7 @@ export function SettingsModal({
onRefreshAppSettings,
}: SettingsModalProps) {
// Tab state
type SettingsTab = 'radio' | 'identity' | 'serial' | 'database' | 'bot';
type SettingsTab = 'radio' | 'identity' | 'connectivity' | 'database' | 'bot';
const [activeTab, setActiveTab] = useState<SettingsTab>('radio');
// Radio config state
@@ -285,7 +285,7 @@ export function SettingsModal({
}
};
const handleSaveSerial = async () => {
const handleSaveConnectivity = async () => {
setError('');
setLoading(true);
@@ -294,7 +294,7 @@ export function SettingsModal({
if (!isNaN(newMaxRadioContacts) && newMaxRadioContacts !== appSettings?.max_radio_contacts) {
await onSaveAppSettings({ max_radio_contacts: newMaxRadioContacts });
}
toast.success('Serial settings saved');
toast.success('Connectivity settings saved');
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to save');
} finally {
@@ -474,7 +474,7 @@ export function SettingsModal({
{activeTab === 'radio' && 'Configure radio frequency, power, and location settings'}
{activeTab === 'identity' &&
'Manage radio name, public key, private key, and advertising settings'}
{activeTab === 'serial' && 'View serial port connection and configure contact sync'}
{activeTab === 'connectivity' && 'View connection status and configure contact sync'}
{activeTab === 'database' && 'View database statistics and clean up old packets'}
{activeTab === 'bot' && 'Configure automatic message bot with Python code'}
</DialogDescription>
@@ -491,7 +491,7 @@ export function SettingsModal({
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="radio">Radio</TabsTrigger>
<TabsTrigger value="identity">Identity</TabsTrigger>
<TabsTrigger value="serial">Serial</TabsTrigger>
<TabsTrigger value="connectivity">Connectivity</TabsTrigger>
<TabsTrigger value="database">Database</TabsTrigger>
<TabsTrigger value="bot">Bot</TabsTrigger>
</TabsList>
@@ -716,15 +716,15 @@ export function SettingsModal({
{error && <div className="text-sm text-destructive">{error}</div>}
</TabsContent>
{/* Serial Tab */}
<TabsContent value="serial" className="space-y-4 mt-4">
{/* Connectivity Tab */}
<TabsContent value="connectivity" className="space-y-4 mt-4">
<div className="space-y-2">
<Label>Serial Port</Label>
{health?.serial_port ? (
<Label>Connection</Label>
{health?.connection_info ? (
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-green-500" />
<code className="px-2 py-1 bg-muted rounded text-foreground text-sm">
{health.serial_port}
{health.connection_info}
</code>
</div>
) : (
@@ -752,7 +752,7 @@ export function SettingsModal({
</p>
</div>
<Button onClick={handleSaveSerial} disabled={loading} className="w-full">
<Button onClick={handleSaveConnectivity} disabled={loading} className="w-full">
{loading ? 'Saving...' : 'Save Settings'}
</Button>
+2 -2
View File
@@ -70,7 +70,7 @@ describe('parseWebSocketMessage', () => {
const onHealth = vi.fn();
const data = JSON.stringify({
type: 'health',
data: { radio_connected: true, serial_port: '/dev/ttyUSB0' },
data: { radio_connected: true, connection_info: 'Serial: /dev/ttyUSB0' },
});
const result = parseWebSocketMessage(data, { onHealth });
@@ -79,7 +79,7 @@ describe('parseWebSocketMessage', () => {
expect(result.handled).toBe(true);
expect(onHealth).toHaveBeenCalledWith({
radio_connected: true,
serial_port: '/dev/ttyUSB0',
connection_info: 'Serial: /dev/ttyUSB0',
});
});
+1 -1
View File
@@ -26,7 +26,7 @@ export interface RadioConfigUpdate {
export interface HealthStatus {
status: string;
radio_connected: boolean;
serial_port: string | null;
connection_info: string | null;
database_size_mb: number;
oldest_undecrypted_timestamp: number | null;
}