From 4189c03a8297276587d123a9c20f1f76ebbfc33e Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Tue, 14 Jul 2026 23:12:01 -0700 Subject: [PATCH] mischief: tighten the detail page's live poll and drop dead state The who-page poll now keeps the location line honest both ways (a mark that came back to town stops showing its old expedition) and stops polling once the adventurer leaves the board. Also collapses a redundant branch in ResolveMischiefOrder and removes the always-false whoPage.Stale. --- internal/storage/mischief.go | 13 ++++++------- internal/web/templates/who.html | 14 ++++++++++++-- internal/web/who.go | 2 -- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/internal/storage/mischief.go b/internal/storage/mischief.go index efd14ae..5320ef1 100644 --- a/internal/storage/mischief.go +++ b/internal/storage/mischief.go @@ -117,18 +117,17 @@ func ResolveMischiefOrder(guid, status, detail string) (MischiefOrder, error) { return MischiefOrder{}, fmt.Errorf("mischief: bad verdict %q", status) } now := nowUnix() - res, err := Get().Exec( + if _, err := Get().Exec( `UPDATE mischief_orders SET status = ?, detail = ?, updated_at = ? WHERE guid = ? AND status = ?`, status, detail, now, guid, MischiefPending, - ) - if err != nil { + ); err != nil { return MischiefOrder{}, fmt.Errorf("mischief: resolve order: %w", err) } - if n, _ := res.RowsAffected(); n == 0 { - // Either it doesn't exist or it's already terminal. Tell the caller which. - return MischiefOrderByGUID(guid) - } + // Whether the update moved a pending order or matched nothing (missing, or + // already terminal from an earlier verdict), the current row is the + // authoritative answer — reading it back is the single path either way, and + // MischiefOrderByGUID turns a missing row into ErrNoSuchOrder for the caller. return MischiefOrderByGUID(guid) } diff --git a/internal/web/templates/who.html b/internal/web/templates/who.html index 47beff6..1d2df75 100644 --- a/internal/web/templates/who.html +++ b/internal/web/templates/who.html @@ -167,13 +167,17 @@ function txt(id, v) { var el = document.getElementById(id); if (el && v != null) el.textContent = v; } + var timer = null; function refresh() { fetch('/api/adventure/who/' + encodeURIComponent(token), { headers: { 'Accept': 'application/json' } }) .then(function (r) { return r.ok ? r.json() : null; }) .then(function (data) { if (!data) return; if (!data.live) { + // Off the board entirely (expedition ended, opted out): nothing more to + // poll for, so stop the timer rather than hammer a token that's gone. txt('who-where', 'Back in town'); + if (timer) clearInterval(timer); return; } var d = data.detail || {}; @@ -187,11 +191,17 @@ txt('who-threat', d.threat_level); setBar(); } - if (mark.OnRun) txt('who-where', mark.Where); + // Keep the location line honest in both directions: a mark that came back + // to town while still on the board must stop showing its old expedition. + if (mark.OnRun) { + txt('who-where', mark.Where); + } else { + txt('who-where', 'In town' + (mark.Idle ? ' · ' + mark.Idle : '')); + } }) .catch(function () { /* transient — next tick will do */ }); } - setInterval(refresh, 60000); + timer = setInterval(refresh, 60000); })(); {{end}} diff --git a/internal/web/who.go b/internal/web/who.go index 386fde9..1f22f45 100644 --- a/internal/web/who.go +++ b/internal/web/who.go @@ -54,7 +54,6 @@ var abilityLabels = [6]string{"STR", "DEX", "CON", "INT", "WIS", "CHA"} type whoPage struct { pageData Mark RosterView - Stale bool HasDetail bool Detail whoDetail Abilities []abilityRow @@ -90,7 +89,6 @@ func (s *Server) handleAdventureWho(w http.ResponseWriter, r *http.Request) { page := whoPage{ pageData: base, Mark: toRosterView(entry), - Stale: storage.RosterSnapshotAt() == 0, } if d, abil, ok := decodeWhoDetail(entry.Detail); ok { page.HasDetail = true