feat(activities): backend-truth counts + scanned list, tabs + popup result on Scan Tickets #76

Merged
padreug merged 1 commit from ticket-scanner-counts-rpc into dev 2026-05-24 21:34:22 +00:00
Owner

Summary

Follow-up to #73 (organizer ticket scanner over nostr-transport) that addresses three loose ends spotted while smoking the page on a real event:

  • localStorage divergence — the original scanner showed a per-device count of "scanned" tickets. The second a co-organizer scanned on another device, or the operator switched from mobile to laptop mid-event, or refreshed in incognito, the on-screen number stopped matching reality. Now the counts strip and the Scanned list come from the new events_list_event_tickets RPC on the backend, so every session agrees.
  • Cramped Scanned view — was an inline ScrollArea h-72 below the camera. Now it's a dedicated tab with full viewport height for a long event-day roster.
  • Easy-to-miss result banner — at a busy door, the small inline banner got skimmed past and the operator scanned the next QR before confirming the previous outcome. Now success/duplicate/error renders as a full-viewport coloured overlay; the operator must tap to dismiss before the next decode lands.

Changes

  • useTicketScanner exposes eventStats / statsLoading / statsError / refreshStats(). Auto-fetches on mount and after every decode (success or failure).
  • localStorage cache demoted to silent decode dedup only — the Clear-list button + its confirm dialog are gone, since the cache is no longer the authoritative state to clear.
  • ScanTicketsPage rewired to render Scanner / Scanned tabs, the counts strip from eventStats, a manual Refresh in the top bar, and the tap-to-dismiss result overlay.

Dependencies

  • Requires aiolabs/events ≥ v1.6.1-aio.3 (the version that ships the events_list_event_tickets RPC). Already in the catalog as of this morning.
  • Requires NOSTR_TRANSPORT_ENABLED=true + a NOSTR_TRANSPORT_RELAYS value on the LNbits host. Demo's deploy config is being updated in server-deploy separately.

Test plan

  • On dev with a freshly upgraded LNbits + events extension, open /scan/<eventId> for an event you own. Counts strip populates within a second from the RPC (not zero with a fallback).
  • Scan a paid ticket → green overlay, "Registered". Counts strip recompute: Scanned +1, Remaining -1.
  • Tap to dismiss the overlay → decode loop resumes for next QR.
  • Scan an already-registered ticket → red overlay "Ticket already registered".
  • Scan an unpaid ticket → red overlay "Ticket not paid for".
  • Open same /scan/<eventId> from a different device mid-event → counts match the first device (no localStorage divergence).
  • Switch to Scanned tab → list shows the same registered tickets, ordered, with timestamps.
  • Refresh button in the top bar manually re-fetches without affecting the camera state.
## Summary Follow-up to #73 (organizer ticket scanner over nostr-transport) that addresses three loose ends spotted while smoking the page on a real event: - **localStorage divergence** — the original scanner showed a per-device count of "scanned" tickets. The second a co-organizer scanned on another device, or the operator switched from mobile to laptop mid-event, or refreshed in incognito, the on-screen number stopped matching reality. Now the counts strip and the Scanned list come from the new `events_list_event_tickets` RPC on the backend, so every session agrees. - **Cramped Scanned view** — was an inline `ScrollArea h-72` below the camera. Now it's a dedicated tab with full viewport height for a long event-day roster. - **Easy-to-miss result banner** — at a busy door, the small inline banner got skimmed past and the operator scanned the next QR before confirming the previous outcome. Now success/duplicate/error renders as a full-viewport coloured overlay; the operator must tap to dismiss before the next decode lands. ## Changes - `useTicketScanner` exposes `eventStats` / `statsLoading` / `statsError` / `refreshStats()`. Auto-fetches on mount and after every decode (success or failure). - localStorage cache demoted to silent decode dedup only — the Clear-list button + its confirm dialog are gone, since the cache is no longer the authoritative state to clear. - `ScanTicketsPage` rewired to render Scanner / Scanned tabs, the counts strip from `eventStats`, a manual Refresh in the top bar, and the tap-to-dismiss result overlay. ## Dependencies - Requires `aiolabs/events ≥ v1.6.1-aio.3` (the version that ships the `events_list_event_tickets` RPC). Already in the catalog as of this morning. - Requires `NOSTR_TRANSPORT_ENABLED=true` + a `NOSTR_TRANSPORT_RELAYS` value on the LNbits host. Demo's deploy config is being updated in `server-deploy` separately. ## Test plan - [x] On dev with a freshly upgraded LNbits + events extension, open `/scan/<eventId>` for an event you own. Counts strip populates within a second from the RPC (not zero with a `—` fallback). - [x] Scan a paid ticket → green overlay, "Registered". Counts strip recompute: `Scanned` +1, `Remaining` -1. - [x] Tap to dismiss the overlay → decode loop resumes for next QR. - [x] Scan an already-registered ticket → red overlay "Ticket already registered". - [x] Scan an unpaid ticket → red overlay "Ticket not paid for". - [x] Open same `/scan/<eventId>` from a different device mid-event → counts match the first device (no localStorage divergence). - [x] Switch to Scanned tab → list shows the same registered tickets, ordered, with timestamps. - [ ] Refresh button in the top bar manually re-fetches without affecting the camera state.
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).
padreug deleted branch ticket-scanner-counts-rpc 2026-05-24 21:34:22 +00:00
Sign in to join this conversation.
No description provided.