feat: day batch — upload results surfaced as toasts (silent LoTW failures from the right-click send looked like nothing was sent); live selection count + Select all/Unselect all toggle in the grid toolbar; active filters bypass the row limit (all matches shown, 10k safety cap); NET Control: same right-click menu on worked-before + drag&drop roster<->on-air (drop on roster logs the QSO); compact mode restores previous window size/position; advanced-filter field renamed Station callsign; optional DXCC-style digital-mode grouping (Settings->General) for matrix badges + cluster colouring; changelog 0.20.9 entries (version NOT bumped)
This commit is contained in:
+37
-12
@@ -116,6 +116,9 @@ type ModePreset = ModePresetForm;
|
||||
type WB = WorkedBeforeView;
|
||||
type CATState = Omit<catModels.RigState, 'convertValues'>;
|
||||
|
||||
// Safety cap on how many rows an ACTIVE filter may load (the normal row
|
||||
// threshold is bypassed while filtering — see buildActiveFilter).
|
||||
const FILTERED_LIMIT_CAP = 10000;
|
||||
const DEFAULT_BANDS = ['160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m','2m','70cm','23cm'];
|
||||
const DEFAULT_MODES = ['SSB','CW','FT8','FT4','RTTY','PSK31','AM','FM','DIGITALVOICE'];
|
||||
// Modes the QSO recorder captures (phone only). Mirrors recordableMode() in
|
||||
@@ -1431,13 +1434,21 @@ export default function App() {
|
||||
|
||||
// The full filter sent to the backend = the builder conditions + the quick
|
||||
// callsign search box (always ANDed) + the on-screen row threshold.
|
||||
const buildActiveFilter = useCallback((): QueryFilter => ({
|
||||
quick_callsign: filterCallsign,
|
||||
conditions: activeFilter.conditions ?? [],
|
||||
match: activeFilter.match ?? 'AND',
|
||||
limit: qsoLimit,
|
||||
offset: 0,
|
||||
}), [filterCallsign, activeFilter, qsoLimit]);
|
||||
// When a filter is ACTIVE the row threshold is lifted to a high safety cap:
|
||||
// the threshold exists to keep the UNFILTERED 30k-QSO log fast, but applying
|
||||
// it to a filter meant "200 matches, only the first 100 shown". A filtered
|
||||
// result set is almost always small, and the cap protects against a
|
||||
// broad filter (e.g. band=20m) pulling half the log.
|
||||
const buildActiveFilter = useCallback((): QueryFilter => {
|
||||
const hasFilter = !!(filterCallsign || (activeFilter.conditions ?? []).length);
|
||||
return {
|
||||
quick_callsign: filterCallsign,
|
||||
conditions: activeFilter.conditions ?? [],
|
||||
match: activeFilter.match ?? 'AND',
|
||||
limit: hasFilter ? FILTERED_LIMIT_CAP : qsoLimit,
|
||||
offset: 0,
|
||||
};
|
||||
}, [filterCallsign, activeFilter, qsoLimit]);
|
||||
|
||||
// refresh reloads the grid. Returns false on failure. When silent, it doesn't
|
||||
// surface the error — used by the startup retry, since the logbook DB (a remote
|
||||
@@ -3729,7 +3740,12 @@ export default function App() {
|
||||
case 'netcontrol':
|
||||
return (
|
||||
<div className="h-full w-full min-h-0 flex flex-col rounded-lg overflow-hidden border border-border">
|
||||
<NetControlPanel onLogged={refresh} countries={countries} bands={bands} modes={modes} />
|
||||
<NetControlPanel onLogged={refresh} countries={countries} bands={bands} modes={modes}
|
||||
qsoMenuHandlers={{
|
||||
onUpdateFromCty: bulkUpdateFromCty, onUpdateFromQRZ: bulkUpdateFromQRZ, onUpdateFromClublog: bulkUpdateFromClublog,
|
||||
onSendTo: bulkSendTo, onSendRecording: bulkSendRecording, onSendEQSL: (ids: number[]) => setEqslQsoId(ids[0] ?? null),
|
||||
onDelete: (ids: number[]) => setDeletingIds(ids),
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
case 'recent':
|
||||
@@ -4796,9 +4812,13 @@ export default function App() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{qsos.length >= qsoLimit && qsos.length < total && (
|
||||
<span className="text-warning">Limit reached — raise Max to see more.</span>
|
||||
)}
|
||||
{(activeFilter.conditions?.length || filterCallsign)
|
||||
? (matchCount != null && matchCount > FILTERED_LIMIT_CAP && (
|
||||
<span className="text-warning">{matchCount.toLocaleString('en-US')} matches — only {FILTERED_LIMIT_CAP.toLocaleString('en-US')} shown, narrow the filter (or use the filtered ADIF export).</span>
|
||||
))
|
||||
: (qsos.length >= qsoLimit && qsos.length < total && (
|
||||
<span className="text-warning">Limit reached — raise Max to see more.</span>
|
||||
))}
|
||||
<Label className="text-[11px] text-muted-foreground">Max</Label>
|
||||
<Input
|
||||
type="number"
|
||||
@@ -5052,7 +5072,12 @@ export default function App() {
|
||||
tune the rig. */}
|
||||
{netEnabled && (
|
||||
<TabsContent value="net" className="mt-0 flex flex-col min-h-0 flex-1">
|
||||
<NetControlPanel onLogged={refresh} countries={countries} bands={bands} modes={modes} />
|
||||
<NetControlPanel onLogged={refresh} countries={countries} bands={bands} modes={modes}
|
||||
qsoMenuHandlers={{
|
||||
onUpdateFromCty: bulkUpdateFromCty, onUpdateFromQRZ: bulkUpdateFromQRZ, onUpdateFromClublog: bulkUpdateFromClublog,
|
||||
onSendTo: bulkSendTo, onSendRecording: bulkSendRecording, onSendEQSL: (ids: number[]) => setEqslQsoId(ids[0] ?? null),
|
||||
onDelete: (ids: number[]) => setDeletingIds(ids),
|
||||
}} />
|
||||
</TabsContent>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user