feat(cluster): quiet the worked spots, light up the empty slots
Two options that change what the eye is pulled towards, both applying to the cluster list AND the band map. Mute worked before: a spot that brings nothing new loses its colour and its badges. It stays in the list - the operator asked for less noise, not less information. "Brings nothing new" reuses the dimming rule the cluster list already had rather than inventing a second notion of done, so it keeps obeying the same-slot option and the digital-mode grouping for free. A new-band or new-slot status is NOT muted: having worked that callsign once on another band says nothing about the band in front of you. Highlight unworked in this slot: colours any callsign not yet worked on this band and this mode, whatever the entity says. For an operator filling slots a common entity on a fresh band+mode is the whole point, and the entity-level status flatly calls it worked. It reuses the existing new-slot status, so no new colour, no new legend, no new badge - both panels already knew how to draw it. WorkedSlot is computed independently of the same-slot preference: it is what this option reads, and it must not change meaning because a different option was toggled. The slot index is now built when either option needs it, and the status cache is keyed on both so a toggle invalidates it. The rules live in one module used by both panels. Marker colours already taught us what happens when the two derive the same thing separately.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
// Two operator options that change how a spot LOOKS, shared by the DX-cluster
|
||||
// list and the band map so the two panels can never disagree about the same
|
||||
// spot — the lesson already learnt with the marker colours.
|
||||
//
|
||||
// muteWorked — a station already worked gets no colour and no badge. It
|
||||
// stays in the list, it simply stops competing for attention.
|
||||
// slotHighlight — a callsign not yet worked on THIS band and mode is coloured,
|
||||
// whatever the entity says. For an operator filling slots, a
|
||||
// common entity on a new band+mode is the whole point, and the
|
||||
// entity-level status calls it "worked".
|
||||
//
|
||||
// They compose deliberately: mute what is done, light up what is not.
|
||||
|
||||
export type SpotDisplayOptions = { muteWorked: boolean; slotHighlight: boolean };
|
||||
|
||||
export function readSpotDisplayOptions(): SpotDisplayOptions {
|
||||
try {
|
||||
return {
|
||||
muteWorked: localStorage.getItem('opslog.clusterMuteWorked') === '1',
|
||||
slotHighlight: localStorage.getItem('opslog.clusterSlotHighlight') === '1',
|
||||
};
|
||||
} catch {
|
||||
return { muteWorked: false, slotHighlight: false };
|
||||
}
|
||||
}
|
||||
|
||||
type Entry = {
|
||||
status?: string;
|
||||
worked_call?: boolean;
|
||||
worked_slot?: boolean;
|
||||
new_county?: boolean;
|
||||
new_pota?: boolean;
|
||||
new_pfx?: boolean;
|
||||
} | undefined;
|
||||
|
||||
// bringsNothingNew: the entity is resolved and worked, and no other dimension
|
||||
// (county, park, prefix) is new. Same test the cluster list already used to dim
|
||||
// a row — muting reuses it rather than inventing a second notion of "done".
|
||||
//
|
||||
// Note what is NOT muted: a status of new-band / new-slot survives, because
|
||||
// having worked that callsign once on another band says nothing about the band
|
||||
// in front of you.
|
||||
function bringsNothingNew(s: Entry): boolean {
|
||||
if (!s || !s.status) return false; // unresolved — never hide, it would flicker
|
||||
if (s.status === 'new' || s.status === 'new-band' || s.status === 'new-mode' || s.status === 'new-slot') return false;
|
||||
return !(s.new_pota || s.new_county || s.new_pfx);
|
||||
}
|
||||
|
||||
// applySpotDisplay rewrites a status entry per the options, so every consumer —
|
||||
// colour, badge, status text — follows from one decision instead of each panel
|
||||
// re-deriving it.
|
||||
export function applySpotDisplay<T extends Entry>(s: T, o: SpotDisplayOptions): T {
|
||||
if (!s) return s;
|
||||
if (o.muteWorked && bringsNothingNew(s)) {
|
||||
// Strip everything that paints: the row keeps its data, loses its emphasis.
|
||||
return { ...s, status: '', worked_call: false } as T;
|
||||
}
|
||||
if (o.slotHighlight && s.worked_slot === false) {
|
||||
// Not worked on this band+mode. If the entity check found nothing new, say
|
||||
// so with the slot status rather than leaving the spot colourless — that is
|
||||
// exactly the row this option exists to surface.
|
||||
if (!s.status || s.status === 'worked') return { ...s, status: 'new-slot' } as T;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
Reference in New Issue
Block a user