feat(decodes): offer the panel as a Main-view pane
It joins the left/right dropdowns in Settings -> General alongside the maps, the cluster and the rest, so decodes can sit beside the entry strip instead of only behind a tab. Per-profile like the other pane choices. The panel itself is now built in ONE place and rendered from both: two copies of that call would be two sets of props to keep in step, and the click handler in particular is not something to duplicate. The status refresh follows. Its "is anything showing a spot status?" test decides whether a logged QSO refreshes the NEW badges now or only marks them dirty, and a panel that had become a pane would have gone on wearing stale badges whenever it was shown that way rather than as a tab.
This commit is contained in:
+40
-25
@@ -1656,12 +1656,12 @@ export default function App() {
|
||||
// map ("map1"), the locator street map ("map2"), the cluster grid or the
|
||||
// worked-before grid. Per-profile (stored via SetUIPref → profile-prefixed),
|
||||
// so it's loaded async on mount and re-read on profile:changed below.
|
||||
type MainPaneKind = 'map1' | 'map2' | 'cluster' | 'worked' | 'flex' | 'recent' | 'icom' | 'yaesu' | 'netcontrol';
|
||||
type MainPaneKind = 'map1' | 'map2' | 'cluster' | 'worked' | 'flex' | 'recent' | 'icom' | 'yaesu' | 'netcontrol' | 'decodes';
|
||||
const [mapZoomSignal, setMapZoomSignal] = useState(0); // bump → world map auto-zooms now
|
||||
const [mainPaneLeft, setMainPaneLeft] = useState<MainPaneKind>('map1');
|
||||
const [mainPaneRight, setMainPaneRight] = useState<MainPaneKind>('map2');
|
||||
const loadMainPanes = useCallback(async () => {
|
||||
const valid = (v: string): v is MainPaneKind => v === 'map1' || v === 'map2' || v === 'cluster' || v === 'worked' || v === 'flex' || v === 'recent' || v === 'icom' || v === 'yaesu' || v === 'netcontrol';
|
||||
const valid = (v: string): v is MainPaneKind => v === 'map1' || v === 'map2' || v === 'cluster' || v === 'worked' || v === 'flex' || v === 'recent' || v === 'icom' || v === 'yaesu' || v === 'netcontrol' || v === 'decodes';
|
||||
const [l, r] = await Promise.all([
|
||||
GetUIPref('mainPaneLeft').catch(() => ''),
|
||||
GetUIPref('mainPaneRight').catch(() => ''),
|
||||
@@ -1786,6 +1786,7 @@ export default function App() {
|
||||
const spotsDirtyRef = useRef(false);
|
||||
useEffect(() => {
|
||||
const vis = mainPaneLeft === 'cluster' || mainPaneRight === 'cluster'
|
||||
|| mainPaneLeft === 'decodes' || mainPaneRight === 'decodes'
|
||||
|| activeTab === 'cluster' || activeTab === 'bandmap' || activeTab === 'decodes' || showBandMap;
|
||||
if (vis && !spotsVisibleRef.current && spotsDirtyRef.current) {
|
||||
spotsDirtyRef.current = false;
|
||||
@@ -5446,6 +5447,34 @@ export default function App() {
|
||||
</button>
|
||||
);
|
||||
|
||||
// The FT decodes panel, built in ONE place: it is offered both as a tab and as
|
||||
// a Main-view pane, and two copies of this call would be two sets of props to
|
||||
// keep in step.
|
||||
const renderDecodesPanel = () => (
|
||||
<DecodesPanel
|
||||
decodes={decodes}
|
||||
txMsgs={txMsgs}
|
||||
txState={txState}
|
||||
spotStatus={spotStatus as any}
|
||||
myCall={station.callsign}
|
||||
// A click ANSWERS the station: it hands the decode back to WSJT-X/MSHV as
|
||||
// a Reply, which is the same thing as double-clicking the line in their
|
||||
// own window.
|
||||
//
|
||||
// Deliberately NOT a rig tune, unlike a cluster spot. On FT8 the whole
|
||||
// band is inside one passband, so moving the dial changes nothing about
|
||||
// who gets answered — and it would only fight the digital application
|
||||
// for the VFO. The entry is still filled, so the QSO can be logged here.
|
||||
onCall={(d) => {
|
||||
onCallsignInput(d.call, { force: true });
|
||||
AnswerDecode(
|
||||
d.instance ?? '', d.ms ?? 0, d.snr, d.dt ?? 0,
|
||||
d.audio_hz ?? 0, d.mode ?? '', d.msg ?? '', !!d.low_conf,
|
||||
).catch((e: any) => setError(String(e?.message ?? e)));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
// Render one Main-view pane. The two sides (mainPaneLeft/Right) each pick from
|
||||
// the same four choices, configured per-profile in Settings → Main view.
|
||||
const renderMainPane = (kind: MainPaneKind) => {
|
||||
@@ -5464,6 +5493,14 @@ export default function App() {
|
||||
);
|
||||
case 'map2':
|
||||
return <LocatorMap toGrid={grid} toLabel={callsign} />;
|
||||
case 'decodes':
|
||||
// Same panel as the tab, in a pane. It brings its own filter bar and
|
||||
// column header, so it needs no frame of its own here.
|
||||
return (
|
||||
<div className="h-full w-full min-h-0 flex flex-col bg-card border border-border rounded-lg overflow-hidden">
|
||||
{renderDecodesPanel()}
|
||||
</div>
|
||||
);
|
||||
case 'cluster':
|
||||
return (
|
||||
<div className="h-full w-full min-h-0 flex flex-col bg-card border border-border rounded-lg overflow-hidden">
|
||||
@@ -7205,29 +7242,7 @@ export default function App() {
|
||||
|
||||
{decodesTabOpen && (
|
||||
<TabsContent value="decodes" className="mt-0 flex flex-col min-h-0 flex-1 data-[state=inactive]:hidden">
|
||||
<DecodesPanel
|
||||
decodes={decodes}
|
||||
txMsgs={txMsgs}
|
||||
txState={txState}
|
||||
spotStatus={spotStatus as any}
|
||||
myCall={station.callsign}
|
||||
// A click ANSWERS the station: it hands the decode back to
|
||||
// WSJT-X/MSHV as a Reply, which is the same thing as
|
||||
// double-clicking the line in their own window.
|
||||
//
|
||||
// Deliberately NOT a rig tune, unlike a cluster spot. On FT8
|
||||
// the whole band is inside one passband, so moving the dial
|
||||
// changes nothing about who gets answered — and it would only
|
||||
// fight the digital application for the VFO. The entry is
|
||||
// still filled, so the QSO can be logged here.
|
||||
onCall={(d) => {
|
||||
onCallsignInput(d.call, { force: true });
|
||||
AnswerDecode(
|
||||
d.instance ?? '', d.ms ?? 0, d.snr, d.dt ?? 0,
|
||||
d.audio_hz ?? 0, d.mode ?? '', d.msg ?? '', !!d.low_conf,
|
||||
).catch((e: any) => setError(String(e?.message ?? e)));
|
||||
}}
|
||||
/>
|
||||
{renderDecodesPanel()}
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user