From cc6411a618e07b23d4cf3fb025a650ccbca66df1 Mon Sep 17 00:00:00 2001 From: rouggy Date: Mon, 20 Jul 2026 14:45:29 +0200 Subject: [PATCH] fix: Antenna Genius buttons ignored clicks while the CW decoder ran MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PortBtn was defined inside AntGeniusPanel, so every render produced a new component type and React unmounted/remounted every port button. Normally re-renders are rare so it's invisible, but the CW decoder floods the app with cw:text/cw:status events (re-render many times a second), tearing the buttons down between mousedown and mouseup — the click never completed and the antenna never switched. Move PortBtn to module scope (stable identity) and pass onActivate/t as props. --- frontend/src/components/AntGeniusPanel.tsx | 56 +++++++++++++--------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/AntGeniusPanel.tsx b/frontend/src/components/AntGeniusPanel.tsx index 203b8f0..e0f6fbe 100644 --- a/frontend/src/components/AntGeniusPanel.tsx +++ b/frontend/src/components/AntGeniusPanel.tsx @@ -33,6 +33,37 @@ function pretty(name: string): string { return t.charAt(0).toUpperCase() + t.slice(1).toLowerCase(); } +// PortBtn is defined at MODULE scope on purpose. Defined inside AntGeniusPanel it +// would be a new component *type* on every render, so React would unmount and +// remount every port button each time the panel re-renders — harmless when that's +// rare, but with the CW decoder running the parent re-renders many times a second +// and the buttons were being torn down mid-click (mousedown and mouseup landing on +// different element instances), so antenna changes silently did nothing. +function PortBtn({ port, index, active, tx, onActivate, t }: { + port: 1 | 2; index: number; active: boolean; tx: boolean; + onActivate: (port: number, antenna: number) => void; + t: (key: string, vars?: Record) => string; +}) { + const letter = port === 1 ? 'A' : 'B'; + const cls = tx + ? 'bg-gradient-to-b from-red-500 to-rose-600 text-white border-red-400/50 shadow-[0_0_10px_rgba(244,63,94,0.5)] animate-pulse' + : active + ? (port === 1 + ? 'bg-gradient-to-b from-emerald-400 to-emerald-600 text-white border-emerald-300/60 shadow-[0_0_9px_rgba(16,185,129,0.45)]' + : 'bg-gradient-to-b from-sky-400 to-sky-600 text-white border-sky-300/60 shadow-[0_0_9px_rgba(14,165,233,0.45)]') + : 'bg-card text-muted-foreground border-border hover:bg-muted hover:text-foreground'; + return ( + + ); +} + // AntGeniusPanel — antenna-switch widget for a 4O3A Antenna Genius, styled to // match the app's light theme with soft gradients + glows. Each antenna row has // a port-A button (left) and port-B button (right). Colours: green = selected on @@ -61,27 +92,6 @@ export function AntGeniusPanel({ status, onActivate, onClose, band }: { if (filtered.length > 0) list = filtered; } - const PortBtn = ({ port, index, active, tx }: { port: 1 | 2; index: number; active: boolean; tx: boolean }) => { - const letter = port === 1 ? 'A' : 'B'; - const cls = tx - ? 'bg-gradient-to-b from-red-500 to-rose-600 text-white border-red-400/50 shadow-[0_0_10px_rgba(244,63,94,0.5)] animate-pulse' - : active - ? (port === 1 - ? 'bg-gradient-to-b from-emerald-400 to-emerald-600 text-white border-emerald-300/60 shadow-[0_0_9px_rgba(16,185,129,0.45)]' - : 'bg-gradient-to-b from-sky-400 to-sky-600 text-white border-sky-300/60 shadow-[0_0_9px_rgba(14,165,233,0.45)]') - : 'bg-card text-muted-foreground border-border hover:bg-muted hover:text-foreground'; - return ( - - ); - }; - return (
@@ -124,11 +134,11 @@ export function AntGeniusPanel({ status, onActivate, onClose, band }: { : 'bg-card/70 text-foreground/80 border-border hover:bg-muted/60'; return (
- +
{pretty(a.name)}
- +
); })}