The Scan Tickets page now sources its counts strip and scanned-ticket
roster from the new `events_list_event_tickets` RPC instead of a
per-device localStorage cache. The previous design diverged the
moment a second organizer scanned, or the operator switched from
mobile to laptop, or refreshed in incognito — backend truth keeps
all sessions consistent.
Webapp-side changes:
- useTicketScanner now exposes `eventStats` (sold / registered /
remaining + per-ticket roster), `statsLoading`, `statsError`, and
`refreshStats()`. Initial load on mount, refresh after every
decode (success or failure) so the UI reflects state seconds
after a scan lands.
- localStorage cache demoted to silent decode dedup only. The
Clear-list button + its confirm dialog are gone — the cache
isn't authoritative state to clear anymore.
- ScanTicketsPage gets two tabs: Scanner (camera + result) and
Scanned ({count} from backend). Counts strip up top reads from
`eventStats` (with the nostr-event `tickets_sold` tag as a
fallback before the RPC roundtrip completes). A manual Refresh
button in the top bar covers the rare case where a second device
scans during your session.
- Result of each scan now lands as a full-viewport tap-to-dismiss
overlay (success green / warning amber / destructive red) so
the door operator can't skim past it on a busy entry.
Depends on aiolabs/events v1.6.1-aio.3 (already in the catalog).