feat(bandmap,dxped): drop the ctrl+wheel zoom, badge the news
ctrl+wheel is the WINDOW zoom everywhere else in OpsLog, and one gesture that resizes the whole app over one panel and one band map over another is a gesture nobody can trust. The + and - buttons keep the zoom, and the footer hint stops advertising what is gone. The DX-World headlines now carry a chase badge like the announcements — but ENTITY-LEVEL only. A headline names no band, and asking the slot question without one answers 'new band' for every entity ever worked: the one mistake that costs a QSO. So the news says NEW DXCC or worked, and the sharper verdicts stay where the bands and modes are known.
This commit is contained in:
@@ -471,19 +471,10 @@ export function BandMap({ band, spots, spotStatus: spotStatusRaw, currentFreqHz,
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [band, containerH, currentFreqHz, range, lo, hi, pxPerKHz, fitToBand]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = scrollerRef.current;
|
||||
if (!el) return;
|
||||
const onWheel = (e: WheelEvent) => {
|
||||
if (!range) return;
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault();
|
||||
changeZoom(e.deltaY > 0 ? -1 : 1);
|
||||
}
|
||||
};
|
||||
el.addEventListener('wheel', onWheel, { passive: false });
|
||||
return () => el.removeEventListener('wheel', onWheel);
|
||||
}, [range]);
|
||||
// No ctrl+wheel zoom here any more: ctrl+wheel is the WINDOW zoom everywhere
|
||||
// else in OpsLog (View ▸ Zoom in/out), and one gesture that resizes the whole
|
||||
// app over one panel and one band map over another is a gesture nobody can
|
||||
// trust. The + / − buttons keep the zoom, deliberately and visibly.
|
||||
|
||||
// Ctrl+↑ / Ctrl+↓ hop to the next spot above / below the rig frequency and tune
|
||||
// to it. Higher freq is UP on the map (see freqToY), so ↑ = next higher spot.
|
||||
|
||||
@@ -26,6 +26,10 @@ type DXped = {
|
||||
type News = {
|
||||
title: string; link: string; pub_date: string; excerpt: string;
|
||||
creator: string; image_url: string; tag: string; calls?: string[];
|
||||
// Entity-level only — a headline names no band, so the backend answers
|
||||
// 'new' or 'worked' and nothing finer.
|
||||
status_chase?: string;
|
||||
unconfirmed?: boolean;
|
||||
};
|
||||
|
||||
// One badge per expedition, the strongest verdict winning — the same palette
|
||||
@@ -180,6 +184,7 @@ export function DXpeditionsPanel() {
|
||||
)}
|
||||
{news.map((n, i) => {
|
||||
const newsCalls = n.calls ?? [];
|
||||
const newsBadge = CHASE_BADGE[n.status_chase ?? ''];
|
||||
const newsAllWatched = newsCalls.length > 0 && newsCalls.every((c) => watched.has(c.toUpperCase()));
|
||||
return (
|
||||
<article key={`${n.link}-${i}`} className="rounded-md border border-border bg-muted/20 p-2">
|
||||
@@ -193,6 +198,15 @@ export function DXpeditionsPanel() {
|
||||
{n.tag && (
|
||||
<span className="px-1 py-px rounded text-[9px] font-bold uppercase bg-primary/15 text-primary">{n.tag}</span>
|
||||
)}
|
||||
{newsBadge && (
|
||||
<span className="px-1 py-px rounded text-[9px] font-bold uppercase tracking-wide border shrink-0"
|
||||
title={n.unconfirmed ? t('dec.unconfTip') : undefined}
|
||||
style={n.unconfirmed
|
||||
? { color: newsBadge.colour, borderColor: newsBadge.colour, borderStyle: 'dashed', opacity: 0.6 }
|
||||
: { color: newsBadge.colour, borderColor: newsBadge.colour }}>
|
||||
{t(newsBadge.label)}
|
||||
</span>
|
||||
)}
|
||||
<button type="button" onClick={() => n.link && BrowserOpenURL(n.link)}
|
||||
className="text-xs font-medium text-left hover:underline">{n.title}</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user