From 8f64e3fee360fa27d028227d479f1dd0f1b8b1c7 Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 29 Aug 2026 00:50:14 +0200 Subject: [PATCH] =?UTF-8?q?feat(watchlist):=20the=20auto-contest=20pattern?= =?UTF-8?q?=20=E2=80=94=20DXHunter's=20contest=5Fprefix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A field in the tab's toolbar (e.g. WWA): while non-empty, any spotted callsign CONTAINING it joins the watchlist as a contest entry by itself — a special-event fleet (HB9WWA, DL0WWA, F4WWA/P…) is collected as it appears instead of typed in one by one, and each is judged per UTC day like any contest entry. Contains, not prefix, because the event string sits anywhere in those calls. Global setting, cached in an atomic so the spot pipeline never touches the settings store per spot. Emptying the field stops the collecting but keeps what was collected — DXHunter deletes non-matching contest entries on a pattern change, and a setting that silently empties an operator's list is the one behaviour of the original this port refuses. The tab also refreshes on backend auto-adds (event) and on a slow tick, so last-seen and the counters stay honest while it sits open. --- app.go | 15 ++++++++----- app_watchlist.go | 37 +++++++++++++++++++++++++++++++ frontend/wailsjs/go/main/App.d.ts | 4 ++++ frontend/wailsjs/go/main/App.js | 8 +++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index abb2f2d..c11a9cf 100644 --- a/app.go +++ b/app.go @@ -96,12 +96,13 @@ const ( keyLookupFailsafe = "lookup.failsafe" keyLookupImages = "lookup.download_images" // 1 = expose QRZ ImageURL to UI - keyStationCallsign = "station.callsign" - keyStationOperator = "station.operator" - keyStationMyGrid = "station.my_grid" - keyStationCountry = "station.my_country" - keyStationSOTA = "station.my_sota_ref" - keyStationPOTA = "station.my_pota_ref" + keyStationCallsign = "station.callsign" + keyStationOperator = "station.operator" + keyWatchlistContestPattern = "watchlist.contest_pattern" // auto-add contest calls containing this + keyStationMyGrid = "station.my_grid" + keyStationCountry = "station.my_country" + keyStationSOTA = "station.my_sota_ref" + keyStationPOTA = "station.my_pota_ref" keyListsBands = "lists.bands" keyListsModes = "lists.modes" @@ -739,6 +740,7 @@ type App struct { watchlist *watchlist.Store // Tools → Watchlist (global watchlist.json) watchAlertMu sync.Mutex // throttles watchlist alerts… watchAlertAt map[string]time.Time // …per entry + watchPattern atomic.Value // auto-contest pattern (string), loaded at startup operating *operating.Repo udp *udp.Manager udpRepo *udp.Repo @@ -1391,6 +1393,7 @@ func (a *App) startup(ctx context.Context) { a.pota = pota.New(func(format string, args ...any) { applog.Printf(format, args...) }) a.watchlist = watchlist.New(filepath.Join(a.dataDir, "watchlist.json")) a.watchAlertAt = map[string]time.Time{} + a.watchPattern.Store(strings.ToUpper(strings.TrimSpace(a.settingOr(keyWatchlistContestPattern, "")))) go a.pota.Run(a.ctx) // DX Cluster (multi-server): the spot callback enriches each spot diff --git a/app_watchlist.go b/app_watchlist.go index 64c9d88..5f64bb4 100644 --- a/app_watchlist.go +++ b/app_watchlist.go @@ -11,12 +11,35 @@ import ( "strings" "time" + "hamlog/internal/applog" "hamlog/internal/qso" "hamlog/internal/watchlist" wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) +// The auto-contest pattern, DXHunter's contest_prefix: while non-empty, any +// spotted callsign CONTAINING it is added to the watchlist as a contest entry +// by itself — a special-event fleet (HB9WWA, DL0WWA, F4WWA…) is collected as +// it appears instead of typed in one by one. Held in an atomic so the spot +// pipeline never touches the settings store per spot. +func (a *App) GetWatchlistContestPattern() string { + if v := a.watchPattern.Load(); v != nil { + return v.(string) + } + return "" +} + +// SetWatchlistContestPattern stores the pattern (global, like the list itself). +// Emptying it stops the auto-add; entries already collected stay — deleting an +// operator's list because a setting changed is DXHunter behaviour this port +// deliberately drops. +func (a *App) SetWatchlistContestPattern(p string) { + p = strings.ToUpper(strings.TrimSpace(p)) + a.watchPattern.Store(p) + a.setSettingGlobal(keyWatchlistContestPattern, p) +} + // WatchlistEntries returns the list for the tab. func (a *App) WatchlistEntries() []watchlist.Entry { if a.watchlist == nil { @@ -160,6 +183,20 @@ func (a *App) watchSpot(dxCall, band, mode, country, comment string, freqHz int6 return } entry, notify, ok := a.watchlist.MarkSeen(dxCall) + if !ok { + // Not watched yet — the auto-contest pattern may claim it. Contains, not + // prefix: the event string sits anywhere in these calls (HB9WWA, F4WWA/P). + if p := a.GetWatchlistContestPattern(); p != "" && + strings.Contains(strings.ToUpper(dxCall), p) { + if err := a.watchlist.Add(dxCall, true); err == nil { + applog.Printf("watchlist: auto-added %s (contest pattern %q)", dxCall, p) + entry, notify, ok = a.watchlist.MarkSeen(dxCall) + if a.ctx != nil { + wruntime.EventsEmit(a.ctx, "watchlist:changed") + } + } + } + } if !ok || !notify || a.ctx == nil { return } diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 91c44fa..7df62f4 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -602,6 +602,8 @@ export function GetUltrabeamSettings():Promise; export function GetUltrabeamStatus():Promise; +export function GetWatchlistContestPattern():Promise; + export function GetWebPublishConfig():Promise; export function GetWebPublishStatus():Promise; @@ -1228,6 +1230,8 @@ export function SetUIPref(arg1:string,arg2:string):Promise; export function SetUltrabeamDirection(arg1:number):Promise; +export function SetWatchlistContestPattern(arg1:string):Promise; + export function SetWinkeyerTrace(arg1:boolean):Promise; export function SetWorkedCallVariants(arg1:boolean):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index ecc094f..5f7c8df 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -1142,6 +1142,10 @@ export function GetUltrabeamStatus() { return window['go']['main']['App']['GetUltrabeamStatus'](); } +export function GetWatchlistContestPattern() { + return window['go']['main']['App']['GetWatchlistContestPattern'](); +} + export function GetWebPublishConfig() { return window['go']['main']['App']['GetWebPublishConfig'](); } @@ -2394,6 +2398,10 @@ export function SetUltrabeamDirection(arg1) { return window['go']['main']['App']['SetUltrabeamDirection'](arg1); } +export function SetWatchlistContestPattern(arg1) { + return window['go']['main']['App']['SetWatchlistContestPattern'](arg1); +} + export function SetWinkeyerTrace(arg1) { return window['go']['main']['App']['SetWinkeyerTrace'](arg1); }