Without a pause gate the qr-scanner's 5-fps decode loop instantly
fires another scan on whatever QR is still in frame — most
visibly, the ticket that just registered immediately re-fires as
"already scanned this session". The operator at the door never
gets a beat to confirm the result or act on it (let the attendee
in, deny entry, redirect to manual lookup, etc.).
useTicketScanner gains an `isPaused` ref that flips to true the
moment a decode resolves (success, error, or duplicate-session
de-dup) and gates further `onDecode` calls. The camera keeps
streaming so resumption is instant — only the decode handler is
muted.
The page replaces the small "Dismiss" button with a full-width
"Scan next" CTA below the result banner. Same place every time so
the operator's hand can stay in muscle memory; disabled while the
in-flight RPC is still sending. Result banner upgrades to a
slightly larger icon + label so the success/failure is readable
at arm's length over the venue.
`clearScanned` also resets `isPaused` so the operator can recover
from a stuck state via the "Clear list" affordance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to the "My tickets" chip from #71. Where "My tickets"
narrows the feed to events you're attending, "Hosting" narrows it
to events you're organizing — reading `activity.isMine` which
useActivities.tagOwnership() already populates from organizer
pubkey match + own LNbits drafts.
Naming rationale: "My events" would have been ambiguous with
favorited / bookmarked. "Hosting" is short, role-oriented, and
pairs as the natural counterpart to "My tickets" (attending vs.
organizing). Spanish/French translations lean on the verb form
("Organizo" / "J'organise") since those languages don't have a
clean noun equivalent.
- useActivityFilters: onlyHosting flag, toggleHosting action,
resetFilters clears it, hasActiveFilters lights up.
- applyFilters filters by `a.isMine === true` when the flag is
on. Composes with category / temporal / "My tickets" via the
same intersection chain.
- ActivitiesPage: chip rendered alongside "My tickets" with the
Megaphone icon (lucide). Hidden when logged out.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the activities loop: organizers scan attendees' QRs from the
standalone PWA at the door instead of dropping into the LNbits admin
register page. Every scan invokes the events_ticket_register RPC
(see aiolabs/events#19) over the nostr transport — the organizer's
signed kind-21000 event IS the authorization, no admin_key in the
browser.
- useTicketScanner: stateful driver. Parses `ticket://<id>` URIs,
dedups in-session via localStorage (`activities_scanned_<id>`,
mirroring the LNbits admin page's `events_scanned_<eventId>`
pattern), surfaces lastScan with three states (ok / duplicate-
session / error). Backend errors arrive as NostrRpcError messages
("Ticket not paid for", "Ticket already registered", etc.) and
render directly.
- ScanTicketsPage: camera viewport + last-scan banner +
scrollable session list with timestamps and (when available)
ticket-holder names. Three banner variants (success/warning/
destructive) so the organizer can read at a glance.
- Route /scan/:activityId, gated by requiresAuth. The "Scan" entry
button on ActivityDetailPage's top bar is rendered only when
`ownedLnbitsEvent !== null`, matching the existing "Edit" gating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generic client for LNbits's nostr-transport (landed upstream Sun May
24, commit f235966c). Encrypts a request envelope to the server's
transport pubkey with NIP-44 v2, signs a kind-21000 event with the
current user's Nostr key, publishes via RelayHub, and listens for a
signed response addressed back to us. Shards (Lightning.Pub's
`{part, index, totalShards, shardsId}` wrapper) are reassembled
before parsing.
Activities ticket scanner is the first consumer; wallet ops + event
CRUD are obvious next adopters (file as follow-up). Server pubkey
discovery is currently env-var (VITE_LNBITS_NOSTR_TRANSPORT_PUBKEY)
— see also the follow-up to add a `.well-known` discovery endpoint
on LNbits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>