fix(cluster): muting a worked spot must not brighten it

"No colour on worked" blanked the spot status as well as the worked-call flag,
on the theory that a spot bringing no novelty should stop painting entirely.

But the status is exactly what the cluster list reads to DIM a row. Blanking it
made isDull() fall through its "unresolved, never dim" guard, so every quiet grey
row came back at full brightness. The option meant to calm the list was the one
making it shout. It also forced a `muted` flag to exist purely so tooltips could
say that an empty status did not, this time, mean "entity not resolved".

It now removes the blue already-worked mark and nothing else, which is all it
ever needed to do: a worked entity already renders with no colour of its own and
is already dimmed. The flag, its two tooltip strings and the special-cased band
map styling all go with it.
This commit is contained in:
2026-08-10 19:55:02 +02:00
parent bde136b98b
commit d273d21f1a
4 changed files with 31 additions and 60 deletions
+9 -20
View File
@@ -38,9 +38,6 @@ type SpotStatusEntry = {
new_county?: boolean;
new_pota?: boolean;
new_pfx?: boolean;
// muted: the status was emptied on purpose by the "no colour on worked
// stations" option, so an empty status here is a choice, not an unknown.
muted?: boolean;
};
// The extra markers are ORTHOGONAL to the entity status: a spot can be a worked
@@ -152,7 +149,7 @@ function LegendDot({ cls, colour, label }: { cls?: string; colour?: string; labe
// Human-readable label for a spot status — used in the pill hover tooltip
// so the operator can see WHY a spot is coloured the way it is.
function statusLabel(s: string, t: (k: string) => string, muted = false): string {
function statusLabel(s: string, t: (k: string) => string): string {
switch (s) {
case 'new': return t('bmp.statusNew');
case 'new-band': return t('bmp.statusNewBand');
@@ -160,11 +157,10 @@ function statusLabel(s: string, t: (k: string) => string, muted = false): string
case 'new-slot': return t('bmp.statusNewSlot');
case 'new-call': return t('bmp.statusNewCall');
case 'worked': return t('bmp.statusWorked');
// An empty status means the entity could not be resolved — EXCEPT when the
// "no colour on worked stations" option emptied it on purpose. Saying
// "entity not resolved" there was a flat contradiction of the country
// printed two words earlier in the same tooltip.
default: return muted ? t('bmp.statusMuted') : t('bmp.statusUnresolved');
// An empty status means the entity could not be resolved. Nothing else
// empties it: the mute option leaves the status alone and takes only the
// already-worked-callsign mark.
default: return t('bmp.statusUnresolved');
}
}
@@ -177,14 +173,7 @@ const QUIET_STYLE = {
dot: 'fill-border',
};
function statusStyle(s: string, muted = false): { pill: string; bar: string; line: string; dot: string } {
// A muted spot must land on QUIET, never on the default branch below. That
// default paints bg-primary/60 — the theme's burnt orange at 60 % over a dark
// card, i.e. brown — because it was written for the rare unresolved entity and
// orange is this app's "look here" colour. With the mute option on, most of
// the map is muted, so the whole band map turned brown and the quietest spots
// shouted the loudest.
if (muted) return QUIET_STYLE;
function statusStyle(s: string): { pill: string; bar: string; line: string; dot: string } {
// pill = full pill background+text+border
// bar = thick left accent inside the pill
// line = SVG leader stroke (visible on hover)
@@ -581,7 +570,7 @@ export function BandMap({ band, spots, spotStatus: spotStatusRaw, currentFreqHz,
{placed.map((p, i) => {
const k = spotStatusKey(p.spot.dx_call, p.spot.band ?? '', p.spot.comment ?? '', p.spot.freq_hz);
const st = spotStatus[k]?.status ?? '';
const style = statusStyle(st, spotStatus[k]?.muted);
const style = statusStyle(st);
const labelMidY = p.labelY + PILL_H / 2;
const bumped = Math.abs(p.freqY - labelMidY) > 0.5;
return (
@@ -629,7 +618,7 @@ export function BandMap({ band, spots, spotStatus: spotStatusRaw, currentFreqHz,
const k = spotStatusKey(p.spot.dx_call, p.spot.band ?? '', p.spot.comment ?? '', p.spot.freq_hz);
const entry = spotStatus[k];
const st = entry?.status ?? '';
const style = statusStyle(st, entry?.muted);
const style = statusStyle(st);
const mode = inferSpotMode(p.spot.comment ?? '', p.spot.freq_hz);
return (
<button
@@ -642,7 +631,7 @@ export function BandMap({ band, spots, spotStatus: spotStatusRaw, currentFreqHz,
'hover:translate-x-0.5 hover:shadow',
style.pill,
)}
title={`${p.spot.dx_call}${entry?.country ? ' · ' + entry.country : ''} · ${p.spot.freq_khz.toFixed(1)} kHz · ${statusLabel(st, t, entry?.muted)}${markersFor(entry).map((m) => ' · ' + t(BMP_MARKER_LABEL[m.key])).join('')}${p.spot.comment ? ' · ' + p.spot.comment : ''}${p.spot.spotter ? ' · de ' + p.spot.spotter : ''}`}
title={`${p.spot.dx_call}${entry?.country ? ' · ' + entry.country : ''} · ${p.spot.freq_khz.toFixed(1)} kHz · ${statusLabel(st, t)}${markersFor(entry).map((m) => ' · ' + t(BMP_MARKER_LABEL[m.key])).join('')}${p.spot.comment ? ' · ' + p.spot.comment : ''}${p.spot.spotter ? ' · de ' + p.spot.spotter : ''}`}
>
{/* Left accent strip. With no extra marker it repeats the status
colour, exactly as before; otherwise it splits into one