This commit is contained in:
Lucas
2026-09-29 16:59:54 +02:00
parent d5a2afdea5
commit 6511d9d007
7 changed files with 57 additions and 36 deletions
-2
View File
@@ -46,7 +46,6 @@ pub struct DSP408Snapshot {
pub state: types::DSPState, pub state: types::DSPState,
pub input_meters: types::InputMeters, pub input_meters: types::InputMeters,
pub output_meters: types::OutputMeters, pub output_meters: types::OutputMeters,
pub mode: types::DSPMode,
} }
#[tauri::command] #[tauri::command]
@@ -104,7 +103,6 @@ pub async fn get_dsp_snapshot(
state: device.dsp.state().clone(), state: device.dsp.state().clone(),
input_meters: device.dsp.input_meters().clone(), input_meters: device.dsp.input_meters().clone(),
output_meters: device.dsp.output_meters().clone(), output_meters: device.dsp.output_meters().clone(),
mode: device.dsp.mode(),
}) })
}) })
} }
+2 -2
View File
@@ -1,6 +1,6 @@
import { useState, useEffect, useRef } from 'react'; import { useState, useEffect, useRef } from 'react';
import './App.css'; import './App.css';
import { DSP, AppPage } from './types'; import { DSP, AppPage, isConnected } from './types';
import TopBar from './components/Topbar/Topbar'; import TopBar from './components/Topbar/Topbar';
import DSPPage from './pages/DspPage/DspPage'; import DSPPage from './pages/DspPage/DspPage';
import AddDSPForm from './pages/AddDspPage/AddDspPage'; import AddDSPForm from './pages/AddDspPage/AddDspPage';
@@ -17,7 +17,7 @@ function App() {
const [selected, setSelected] = useState<number | null>(null); const [selected, setSelected] = useState<number | null>(null);
const [page, setPage] = useState<AppPage>('home'); const [page, setPage] = useState<AppPage>('home');
const { notifications, notify, removeNotification, hoverNotification } = useNotifications(); const { notifications, notify, removeNotification, hoverNotification } = useNotifications();
const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length; const connectedDspCount = dsps.filter((dsp) => isConnected(dsp)).length;
const activeDsp = dsps.find((d) => d.id === selected) ?? null; const activeDsp = dsps.find((d) => d.id === selected) ?? null;
const activeInstance = activeDsp ? createDSPInstance(activeDsp, setDsps, notify) : null; const activeInstance = activeDsp ? createDSPInstance(activeDsp, setDsps, notify) : null;
+13 -5
View File
@@ -1,5 +1,5 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { DSP, AppPage } from '../../types'; import { DSP, AppPage, isConnected } from '../../types';
import AnimatedLogo from '../../assets/icons/AnimatedLogo'; import AnimatedLogo from '../../assets/icons/AnimatedLogo';
import { PlusIcon } from '../../assets/icons/PlusIcon'; import { PlusIcon } from '../../assets/icons/PlusIcon';
import { CrownIcon } from '../../assets/icons/CrownIcon'; import { CrownIcon } from '../../assets/icons/CrownIcon';
@@ -36,7 +36,7 @@ function Tab({
onDisconnect: (dsp: DSP) => void; onDisconnect: (dsp: DSP) => void;
actions?: React.ReactNode; actions?: React.ReactNode;
}) { }) {
const isConnected = dsp.status === 'connected'; const isDspConnected = isConnected(dsp);
const handleTabKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => { const handleTabKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' || e.key === ' ') { if (e.key === 'Enter' || e.key === ' ') {
@@ -60,18 +60,26 @@ function Tab({
<button <button
type="button" type="button"
className="tab-led" className="tab-led"
aria-label={isConnected ? `Disconnect ${dsp.name}` : `Connect ${dsp.name}`} aria-label={isDspConnected ? `Disconnect ${dsp.name}` : `Connect ${dsp.name}`}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
if (isConnected) { if (isDspConnected) {
onDisconnect(dsp); onDisconnect(dsp);
} else { } else {
onConnect(dsp); onConnect(dsp);
} }
}} }}
> >
<Led status={dsp.status} /> <Led
status={
dsp.isConnecting
? 'connecting'
: isConnected(dsp)
? 'connected'
: 'disconnected'
}
/>
</button> </button>
<span className="tab-name" title={dsp.name}> <span className="tab-name" title={dsp.name}>
@@ -1,4 +1,4 @@
import { DSP } from '../../../../types'; import { DSP, isConnected } from '../../../../types';
import Button from '../../../../components/Button/Button'; import Button from '../../../../components/Button/Button';
import Card from '../../../../components/Card/Card'; import Card from '../../../../components/Card/Card';
import Led from '../../../../components/Led/Led'; import Led from '../../../../components/Led/Led';
@@ -15,12 +15,19 @@ function ConnectionPanel({
onDisconnect: () => void; onDisconnect: () => void;
}) { }) {
const statusLabel = const statusLabel =
dsp.status === 'connected' isConnected(dsp)
? 'Connected' ? 'Connected'
: dsp.status === 'connecting' : dsp.isConnecting
? 'Connecting…' ? 'Connecting…'
: 'Disconnected'; : 'Disconnected';
console.log('ConnectionPanel render:', {
id: dsp.id,
connection: dsp.type === 'DSP408' ? dsp.state.connection : undefined,
isConnected: isConnected(dsp),
isConnecting: dsp.isConnecting,
});
return ( return (
<div className="connection-section"> <div className="connection-section">
<Card className="connection-panel"> <Card className="connection-panel">
@@ -35,10 +42,10 @@ function ConnectionPanel({
<div className="connection-panel__status"> <div className="connection-panel__status">
<span className="connection-panel__led"> <span className="connection-panel__led">
<Led status={dsp.status} /> {/* <Led status={dsp.status} /> */}
</span> </span>
<span className={`status-label status-label--${dsp.status}`}>{statusLabel}</span> {/* <span className={`status-label status-label--${dsp.status}`}>{statusLabel}</span> */}
</div> </div>
</div> </div>
@@ -64,13 +71,13 @@ function ConnectionPanel({
</dl> </dl>
<div className="connection-panel__actions"> <div className="connection-panel__actions">
{dsp.status === 'connected' ? ( {isConnected(dsp) ? (
<Button variant="outline" onClick={onDisconnect}> <Button variant="outline" onClick={onDisconnect}>
Disconnect Disconnect
</Button> </Button>
) : ( ) : (
<Button variant="primary" onClick={onConnect} disabled={dsp.status === 'connecting'}> <Button variant="primary" onClick={onConnect} disabled={dsp.isConnecting}>
{dsp.status === 'connecting' ? 'Connecting…' : 'Connect'} {dsp.isConnecting ? 'Connecting…' : 'Connect'}
</Button> </Button>
)} )}
</div> </div>
+10 -12
View File
@@ -12,6 +12,7 @@ import {
PEQBand, PEQBand,
InputSource, InputSource,
DSP408Snapshot, DSP408Snapshot,
DSPMode,
} from './types'; } from './types';
import { SetDsps, NotifyFn, updateDSP, setPollingPaused } from '../../dsp/dspShared'; import { SetDsps, NotifyFn, updateDSP, setPollingPaused } from '../../dsp/dspShared';
import { registerPollStopper } from '../../dsp/pollRegistry'; import { registerPollStopper } from '../../dsp/pollRegistry';
@@ -117,8 +118,8 @@ export async function create(params: {
ip: params.ip, ip: params.ip,
port: params.port, port: params.port,
deviceId: params.deviceId, deviceId: params.deviceId,
mode: snapshot.mode, mode: params.demo ? DSPMode.Demo: DSPMode.Normal,
status: 'disconnected', isConnecting: false,
pollingPaused: true, pollingPaused: true,
state: snapshot.state, state: snapshot.state,
input_meters: snapshot.input_meters, input_meters: snapshot.input_meters,
@@ -130,6 +131,7 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
async function updateDSPState(): Promise<DSP408['state']> { async function updateDSPState(): Promise<DSP408['state']> {
try { try {
const state = await invoke<DSP408['state']>('get_dsp_state', { id: dsp.id }); const state = await invoke<DSP408['state']>('get_dsp_state', { id: dsp.id });
updateDSP(setDsps, dsp.id, (d) => ({ ...d, state }) as DSP408); updateDSP(setDsps, dsp.id, (d) => ({ ...d, state }) as DSP408);
return state; return state;
@@ -143,11 +145,11 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
async function abortLocked() { async function abortLocked() {
await invoke('disconnect_dsp408', { id: dsp.id }).catch(() => {}); await invoke('disconnect_dsp408', { id: dsp.id }).catch(() => {});
updateDSP(setDsps, dsp.id, (d) => ({ ...d, status: 'disconnected', passwordRequired: true })); updateDSP(setDsps, dsp.id, (d) => ({ ...d, passwordRequired: true }));
} }
async function connectInternal(password?: string) { async function connectInternal(password?: string) {
updateDSP(setDsps, dsp.id, (d) => ({ ...d, status: 'connecting', passwordRequired: false })); updateDSP(setDsps, dsp.id, (d) => ({ ...d, isConnecting: true, passwordRequired: false }));
try { try {
await invoke('connect_dsp408', { id: dsp.id }); await invoke('connect_dsp408', { id: dsp.id });
@@ -159,13 +161,10 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
return; return;
} }
try { let r = await invoke('unlock_device', { id: dsp.id, password });
await invoke('unlock_device', { id: dsp.id, password }); // adjust name/signature
} catch {
// wrong password
}
state = await updateDSPState(); state = await updateDSPState();
if (isLocked(state)) { if (isLocked(state)) {
await abortLocked(); await abortLocked();
notify('Wrong password', 'error', dsp.name); notify('Wrong password', 'error', dsp.name);
@@ -173,11 +172,11 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
} }
} }
updateDSP(setDsps, dsp.id, (d) => ({ ...d, status: 'connected', pollingPaused: false })); updateDSP(setDsps, dsp.id, (d) => ({ ...d, isConnecting: false, pollingPaused: false }));
notify('Connected', 'success', dsp.name); notify('Connected', 'success', dsp.name);
startPolling(dsp, setDsps); startPolling(dsp, setDsps);
} catch (err) { } catch (err) {
updateDSP(setDsps, dsp.id, (d) => ({ ...d, status: 'disconnected' })); updateDSP(setDsps, dsp.id, (d) => ({ ...d, isConnecting: false }));
notify(`Connection failed: ${String(err)}`, 'error', dsp.name); notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
} }
} }
@@ -216,7 +215,6 @@ export function forDSP408(dsp: DSP408, setDsps: SetDsps, notify: NotifyFn) {
updateDSP(setDsps, dsp.id, (d) => ({ updateDSP(setDsps, dsp.id, (d) => ({
...d, ...d,
status: 'disconnected',
pollingPaused: true, pollingPaused: true,
})); }));
+10 -2
View File
@@ -243,15 +243,23 @@ export type DSPState = {
flags: DeviceFlags; flags: DeviceFlags;
presets: PresetBank; presets: PresetBank;
current_config: DSPConfigState; current_config: DSPConfigState;
connection: ConnectionStatus,
}; };
export type DSPMode = 'demo' | 'disconnected' | 'connected'; export enum ConnectionStatus {
Connected,
Disconnected,
}
export enum DSPMode {
Demo,
Normal,
}
export type DSP408Snapshot = { export type DSP408Snapshot = {
state: DSPState; state: DSPState;
input_meters: DSP408InputMeters; input_meters: DSP408InputMeters;
output_meters: DSP408OutputMeters; output_meters: DSP408OutputMeters;
mode: DSPMode;
}; };
export interface Meter { export interface Meter {
+7 -5
View File
@@ -3,10 +3,9 @@ import {
DSPMode, DSPMode,
DSP408OutputMeters, DSP408OutputMeters,
DSP408InputMeters, DSP408InputMeters,
ConnectionStatus,
} from './features/dsp408/types'; } from './features/dsp408/types';
export type DSPStatus = 'connected' | 'connecting' | 'disconnected';
export type NotificationType = 'success' | 'error' | 'warning'; export type NotificationType = 'success' | 'error' | 'warning';
export type Notification = { export type Notification = {
id: number; id: number;
@@ -39,12 +38,15 @@ interface DSPBase {
mode: DSPMode; mode: DSPMode;
passwordRequired?: boolean; passwordRequired?: boolean;
isConnecting: boolean;
status: DSPStatus;
pollingPaused: boolean; pollingPaused: boolean;
} }
export function isConnected(dsp: DSP): boolean {
return dsp.type === 'DSP408'
&& dsp.state.connection === ConnectionStatus.Connected;
}
export type DSP408 = DSPBase & { export type DSP408 = DSPBase & {
type: 'DSP408'; type: 'DSP408';
state: DSP408State; state: DSP408State;