fix(ftmap): tell Leaflet when its box changes size; fix(decodes): all seven continents
The FT map drew a strip of dead space along the bottom after a zoom. Leaflet measures its container ONCE, at creation — which for this panel is the instant the tab is selected, before the flex layout has settled — and then asks for tiles to fit that stale size for ever. A ResizeObserver hands it the real size, plus one pass after the first paint for the layout that settles without ever firing a resize. And the decode list's continent chips are the seven, fixed. Building them from the feed meant a row that reshuffled itself every period — a chip appeared when the first Asian station decoded and moved everything sideways under the pointer — and it could not say what the filter was capable of until something had been heard.
This commit is contained in:
+6
-2
@@ -13,7 +13,9 @@
|
|||||||
"Fixed: a “worked but not confirmed” badge turned solid again after the next QSO, so an entity worked on a band still read NEW BAND as if it had never been worked there. Four hand-written copies of the same status mapping had drifted apart — the two that re-fetch dropped the unconfirmed flags, and none of them ever carried the grid one. There is one mapping now.",
|
"Fixed: a “worked but not confirmed” badge turned solid again after the next QSO, so an entity worked on a band still read NEW BAND as if it had never been worked there. Four hand-written copies of the same status mapping had drifted apart — the two that re-fetch dropped the unconfirmed flags, and none of them ever carried the grid one. There is one mapping now.",
|
||||||
"The Chase switches (POTA, US counties, prefixes, grids) now hold in FT decodes and in Chase new, not only in the DX Cluster — they say what you hunt, not which panel is open. Unchecked, their badges and filter chips disappear from all three and only the entity verdicts remain: NEW DXCC, band, mode, slot.",
|
"The Chase switches (POTA, US counties, prefixes, grids) now hold in FT decodes and in Chase new, not only in the DX Cluster — they say what you hunt, not which panel is open. Unchecked, their badges and filter chips disappear from all three and only the entity verdicts remain: NEW DXCC, band, mode, slot.",
|
||||||
"New DXHunter page in Preferences: every Chase setting moves there from the DX Cluster page — the hunt, its confirmation sources, and the POTA / SOTA / US counties / prefixes / grids switches. They govern three screens now, so they no longer live under the name of one of them; more of DXHunter’s ideas will land beside them.",
|
"New DXHunter page in Preferences: every Chase setting moves there from the DX Cluster page — the hunt, its confirmation sources, and the POTA / SOTA / US counties / prefixes / grids switches. They govern three screens now, so they no longer live under the name of one of them; more of DXHunter’s ideas will land beside them.",
|
||||||
"Chase US states joins the other switches: unchecked, the NEW STATE badge and its filter chip go quiet everywhere."
|
"Chase US states joins the other switches: unchecked, the NEW STATE badge and its filter chip go quiet everywhere.",
|
||||||
|
"FT Map: zooming no longer leaves a dead strip along the bottom. Leaflet measures its container once, when the map is created — which here is the instant the tab is selected, before the layout has settled — and it is now told whenever that box changes size.",
|
||||||
|
"FT decodes: the continent filter shows all seven, always, instead of only those currently on the feed — the row no longer reshuffles under the pointer when the first Asian station decodes, and it says what the filter can do before anything has been heard."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"DX Cluster : un serveur déconnecté garde sa pastille et peut donc être reconnecté — le déconnecter le faisait disparaître avec le seul moyen d’y revenir.",
|
"DX Cluster : un serveur déconnecté garde sa pastille et peut donc être reconnecté — le déconnecter le faisait disparaître avec le seul moyen d’y revenir.",
|
||||||
@@ -26,7 +28,9 @@
|
|||||||
"Corrigé : un badge « contacté mais non confirmé » redevenait plein dès le QSO suivant, si bien qu’une entité contactée sur une bande affichait NEW BAND comme si elle ne l’avait jamais été. Quatre copies écrites à la main de la même conversion de statut avaient divergé — les deux qui rafraîchissent perdaient les drapeaux « non confirmé », et aucune ne transportait celui des locators. Il n’y en a plus qu’une.",
|
"Corrigé : un badge « contacté mais non confirmé » redevenait plein dès le QSO suivant, si bien qu’une entité contactée sur une bande affichait NEW BAND comme si elle ne l’avait jamais été. Quatre copies écrites à la main de la même conversion de statut avaient divergé — les deux qui rafraîchissent perdaient les drapeaux « non confirmé », et aucune ne transportait celui des locators. Il n’y en a plus qu’une.",
|
||||||
"Les cases Chasse (POTA, comtés US, préfixes, locators) s’appliquent désormais aux FT decodes et à Chase new, plus seulement au DX Cluster — elles disent ce que vous chassez, pas quel panneau est ouvert. Décochées, leurs badges et leurs puces de filtre disparaissent des trois écrans et il ne reste que les verdicts d’entité : NOUVEAU DXCC, bande, mode, slot.",
|
"Les cases Chasse (POTA, comtés US, préfixes, locators) s’appliquent désormais aux FT decodes et à Chase new, plus seulement au DX Cluster — elles disent ce que vous chassez, pas quel panneau est ouvert. Décochées, leurs badges et leurs puces de filtre disparaissent des trois écrans et il ne reste que les verdicts d’entité : NOUVEAU DXCC, bande, mode, slot.",
|
||||||
"Nouvelle page DXHunter dans les Préférences : tous les réglages de chasse y déménagent depuis la page DX Cluster — le mode de chasse, ses sources de confirmation et les cases POTA / SOTA / comtés US / préfixes / locators. Ils commandent trois écrans, ils ne vivent donc plus sous le nom d’un seul ; d’autres idées de DXHunter viendront s’y ajouter.",
|
"Nouvelle page DXHunter dans les Préférences : tous les réglages de chasse y déménagent depuis la page DX Cluster — le mode de chasse, ses sources de confirmation et les cases POTA / SOTA / comtés US / préfixes / locators. Ils commandent trois écrans, ils ne vivent donc plus sous le nom d’un seul ; d’autres idées de DXHunter viendront s’y ajouter.",
|
||||||
"Chasser les états US rejoint les autres cases : décochée, le badge NOUVEL ÉTAT et sa puce de filtre se taisent partout."
|
"Chasser les états US rejoint les autres cases : décochée, le badge NOUVEL ÉTAT et sa puce de filtre se taisent partout.",
|
||||||
|
"FT Map : le zoom ne laisse plus une bande morte en bas. Leaflet mesure son conteneur une seule fois, à la création de la carte — ici l’instant où l’onglet est sélectionné, avant que la mise en page ne se soit stabilisée — et il est désormais prévenu à chaque changement de taille.",
|
||||||
|
"FT decodes : le filtre continent affiche les sept, toujours, au lieu des seuls présents dans le flux — la rangée ne se réorganise plus sous le pointeur quand la première station asiatique décode, et elle annonce ce que le filtre sait faire avant même d’avoir entendu quoi que ce soit."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ interface Props {
|
|||||||
// that starts by hiding most of the band would be lying about what is on it.
|
// that starts by hiding most of the band would be lying about what is on it.
|
||||||
type NewCat = 'dxcc' | 'band' | 'mode' | 'slot' | 'pfx' | 'grid' | 'pota' | 'cty' | 'state';
|
type NewCat = 'dxcc' | 'band' | 'mode' | 'slot' | 'pfx' | 'grid' | 'pota' | 'cty' | 'state';
|
||||||
|
|
||||||
|
// The seven, in the order an operator reads them.
|
||||||
|
const CONTINENTS = ['AF', 'AN', 'AS', 'EU', 'NA', 'OC', 'SA'];
|
||||||
|
|
||||||
const NEW_CATS: { key: NewCat; label: string; colour: string }[] = [
|
const NEW_CATS: { key: NewCat; label: string; colour: string }[] = [
|
||||||
{ key: 'dxcc', label: 'dec.stNew', colour: 'var(--success)' },
|
{ key: 'dxcc', label: 'dec.stNew', colour: 'var(--success)' },
|
||||||
{ key: 'band', label: 'dec.stBand', colour: 'var(--warning)' },
|
{ key: 'band', label: 'dec.stBand', colour: 'var(--warning)' },
|
||||||
@@ -630,27 +633,25 @@ export function DecodesPanel({ decodes, txMsgs, txState, txStates, spotStatus, o
|
|||||||
// dropdowns were pure furniture for most operators; they appear the day a
|
// dropdowns were pure furniture for most operators; they appear the day a
|
||||||
// second instance puts a second band on the link, which is the only day they
|
// second instance puts a second band on the link, which is the only day they
|
||||||
// mean anything.
|
// mean anything.
|
||||||
const { bands, modes, conts, instances } = useMemo(() => {
|
const { bands, modes, instances } = useMemo(() => {
|
||||||
const b = new Set<string>(), m = new Set<string>(), c = new Set<string>(), i = new Set<string>();
|
const b = new Set<string>(), m = new Set<string>(), i = new Set<string>();
|
||||||
for (const d of decodes) {
|
for (const d of decodes) {
|
||||||
if (d.band) b.add(d.band);
|
if (d.band) b.add(d.band);
|
||||||
if (d.mode) m.add(d.mode);
|
if (d.mode) m.add(d.mode);
|
||||||
if (d.instance) i.add(d.instance);
|
if (d.instance) i.add(d.instance);
|
||||||
const ct = statusOf(d)?.continent;
|
|
||||||
if (ct) c.add(ct);
|
|
||||||
}
|
}
|
||||||
return { bands: [...b].sort(), modes: [...m].sort(), conts: [...c].sort(), instances: [...i].sort() };
|
return { bands: [...b].sort(), modes: [...m].sort(), instances: [...i].sort() };
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [decodes, spotStatus]);
|
}, [decodes, spotStatus]);
|
||||||
|
|
||||||
// The chips are the continents on the feed — a selector with nothing to choose
|
// All seven, always, in their usual order.
|
||||||
// is furniture — PLUS anything currently selected. Without that second half a
|
//
|
||||||
// filter can strand itself: pick AF, the last African station stops decoding,
|
// The chips used to be built from the continents ON the feed, which read as a
|
||||||
// and the list empties with no chip left to switch it back off.
|
// list that reshuffled itself every period: a chip appeared when the first
|
||||||
const contChips = useMemo(
|
// Asian station decoded and moved everything sideways under the pointer. A
|
||||||
() => [...new Set([...conts, ...contList])].sort(),
|
// fixed row can be aimed at — you learn where OC is and it stays there — and
|
||||||
[conts, contList],
|
// it also says what the filter can do before anything has been heard.
|
||||||
);
|
const contChips = CONTINENTS;
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
const q = search.trim().toUpperCase();
|
const q = search.trim().toUpperCase();
|
||||||
|
|||||||
@@ -62,7 +62,25 @@ export function FTMapPanel({ decodes, myGrid }: { decodes: FTMapDecode[]; myGrid
|
|||||||
});
|
});
|
||||||
mapRef.current = m;
|
mapRef.current = m;
|
||||||
layerRef.current = L.layerGroup().addTo(m);
|
layerRef.current = L.layerGroup().addTo(m);
|
||||||
return () => { m.remove(); mapRef.current = null; layerRef.current = null; };
|
// Leaflet measures its container ONCE, when the map is created, and then
|
||||||
|
// draws tiles for that size for ever. This panel is mounted the moment its
|
||||||
|
// tab is selected — before the flex layout has settled — and the window can
|
||||||
|
// be resized under it, so the stale measurement showed as a strip of dead
|
||||||
|
// space along the bottom where tiles were never asked for. The observer
|
||||||
|
// hands it the real size whenever the box changes.
|
||||||
|
const ro = new ResizeObserver(() => m.invalidateSize({ animate: false }));
|
||||||
|
ro.observe(divRef.current);
|
||||||
|
// Once more after the first paint: the first observation can arrive while
|
||||||
|
// the panel is still zero-height, and no further resize follows a layout
|
||||||
|
// that settles by itself.
|
||||||
|
const settle = window.setTimeout(() => m.invalidateSize({ animate: false }), 100);
|
||||||
|
return () => {
|
||||||
|
window.clearTimeout(settle);
|
||||||
|
ro.disconnect();
|
||||||
|
m.remove();
|
||||||
|
mapRef.current = null;
|
||||||
|
layerRef.current = null;
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Basemap follows the picker.
|
// Basemap follows the picker.
|
||||||
|
|||||||
Reference in New Issue
Block a user