fix(awards): the RDA comparison list no longer jumps back to the top
Two faults, one behind the other. RDAPanel is nested inside SettingsModal so it can read its state, and it was written as <RDAPanel />. A nested function is a new component TYPE on every render: React cannot know it is the same panel, so it unmounted the tree and mounted a fresh one — and a fresh scroll container starts at the top. It is called now, like the other panels, which produces the same elements in place and never disturbs the scroll position. What it was reacting to should not have reached it either. The PSK Reporter and grid-cache poll ran every three seconds from wherever you were in Preferences, re-rendering the whole dialog for a count shown only on the cluster section. It now runs while that section is open, and the one-time loads it was sharing an effect with have stayed where they were rather than refetching on every click in the sidebar. The visible result: a hundred contacts to correct can be read from top to bottom.
This commit is contained in:
@@ -2050,14 +2050,22 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
|
||||
try { const n = await GetSpotTTLMinutes(); setSpotTTL(n); setSpotTTLText(String(n)); } catch { /* defaults stand */ }
|
||||
try { const n = await GetSpotMax(); setSpotMaxText(String(n)); } catch { /* defaults stand */ }
|
||||
})();
|
||||
// Poll the feed while the panel is open: a live count is the only thing that
|
||||
// distinguishes "connected" from "connected and receiving nothing".
|
||||
}, []);
|
||||
// Poll the feed only while the section that SHOWS it is open.
|
||||
//
|
||||
// A live count is the one thing that separates "connected" from "connected
|
||||
// and receiving nothing", so it has to be polled — but it was polled from
|
||||
// everywhere, re-rendering the whole dialog every three seconds whichever
|
||||
// panel was in front. That is a heartbeat through every list and every form
|
||||
// in Preferences for a number nobody is looking at.
|
||||
useEffect(() => {
|
||||
if (selected !== 'cluster') return;
|
||||
const t = window.setInterval(async () => {
|
||||
try { setPskrStatus(await GetPSKReporterStatus()); } catch { /* ignore */ }
|
||||
try { setGridStat(await GetGridCacheStatus()); } catch { /* ignore */ }
|
||||
}, 3000);
|
||||
return () => window.clearInterval(t);
|
||||
}, []);
|
||||
}, [selected]);
|
||||
const [selfSpot, setSelfSpot] = useState({ enabled: false, minutes: SELF_SPOT_MIN_MIN });
|
||||
const [selfSpotText, setSelfSpotText] = useState(String(SELF_SPOT_MIN_MIN));
|
||||
const [clusterStatuses, setClusterStatuses] = useState<ClusterServerStatus[]>([]);
|
||||
@@ -7560,7 +7568,16 @@ function SettingsModalImpl({ onClose, onSaved, initialSection, onMainPaneChanged
|
||||
uscounties: USCountiesPanel,
|
||||
databases: DatabasesPanel,
|
||||
autostart: () => <AutostartPanelComponent />,
|
||||
awards: () => (<div className="space-y-6"><AwardsSelectionPanel profile={activeProfile ?? undefined} /><RDAPanel /></div>),
|
||||
// RDAPanel is CALLED, not written as <RDAPanel />.
|
||||
//
|
||||
// It is nested inside this component, so as an element it would be a new
|
||||
// component TYPE on every render — React cannot know it is the same panel,
|
||||
// so it unmounts the old tree and mounts a fresh one. A fresh scroll
|
||||
// container starts at the top, which is what threw the district comparison
|
||||
// back to the first row every three seconds. Calling it produces the same
|
||||
// elements in place, and the scroll position is simply never disturbed.
|
||||
// (Safe because RDAPanel holds no hooks of its own — see PanelHost.)
|
||||
awards: () => (<div className="space-y-6"><AwardsSelectionPanel profile={activeProfile ?? undefined} />{RDAPanel()}</div>),
|
||||
cat: CATPanel,
|
||||
rotator: RotatorPanel,
|
||||
winkeyer: WinkeyerPanel,
|
||||
|
||||
Reference in New Issue
Block a user