diff --git a/.env.example b/.env.example index ecae912..99327b7 100644 --- a/.env.example +++ b/.env.example @@ -11,13 +11,6 @@ VITE_API_KEY=your-api-key-here VITE_LNBITS_DEBUG=false VITE_WEBSOCKET_ENABLED=true -# LNbits Nostr-transport server pubkey (kind-21000 RPC endpoint). -# Logged by the LNbits server at startup: -# `Nostr transport: starting with pubkey ... on N relay(s)` -# Required for the activities ticket scanner; legacy HTTP path still -# works without it. -VITE_LNBITS_NOSTR_TRANSPORT_PUBKEY= - # Lightning Address Domain (optional) # Override the domain used for Lightning Addresses # If not set, domain will be extracted from VITE_LNBITS_BASE_URL diff --git a/src/core/di-container.ts b/src/core/di-container.ts index f6c87cd..15aa084 100644 --- a/src/core/di-container.ts +++ b/src/core/di-container.ts @@ -149,9 +149,6 @@ export const SERVICE_TOKENS = { // Nostr metadata services NOSTR_METADATA_SERVICE: Symbol('nostrMetadataService'), - // Nostr transport (kind-21000 RPC over relays — LNbits backend) - NOSTR_TRANSPORT_SERVICE: Symbol('nostrTransportService'), - // Activities services (Nostr-native events + ticketing module) ACTIVITIES_NOSTR_SERVICE: Symbol('activitiesNostrService'), ACTIVITIES_TICKET_API: Symbol('activitiesTicketApi'), diff --git a/src/lib/config/lnbits.ts b/src/lib/config/lnbits.ts index cc33aff..dec6c8e 100644 --- a/src/lib/config/lnbits.ts +++ b/src/lib/config/lnbits.ts @@ -4,12 +4,6 @@ export const LNBITS_CONFIG = { // This should point to your LNBits instance API_BASE_URL: `${import.meta.env.VITE_LNBITS_BASE_URL || ''}/api/v1`, - // LNbits Nostr-transport server pubkey. The webapp encrypts its - // signed kind-21000 RPC events to this pubkey and listens for - // signed responses from it. Logged by the LNbits server at startup - // (`Nostr transport: starting with pubkey ...`). - NOSTR_TRANSPORT_PUBKEY: import.meta.env.VITE_LNBITS_NOSTR_TRANSPORT_PUBKEY || '', - // Whether to enable debug logging DEBUG: import.meta.env.VITE_LNBITS_DEBUG === 'true', diff --git a/src/modules/activities/composables/useTicketScanner.ts b/src/modules/activities/composables/useTicketScanner.ts deleted file mode 100644 index 638b8bc..0000000 --- a/src/modules/activities/composables/useTicketScanner.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { ref, type Ref } from 'vue' -import { useLocalStorage } from '@vueuse/core' -import { injectService, SERVICE_TOKENS } from '@/core/di-container' -import type { NostrTransportService } from '@/modules/base/services/NostrTransportService' - -export type ScanStatus = 'ok' | 'duplicate-session' | 'error' - -export interface ScanResult { - status: ScanStatus - ticketId: string - /** Backend response payload on OK. */ - ticket?: Record - /** Error string from the backend or local validation. */ - message?: string -} - -export interface ScanRecord { - ticketId: string - /** Holder display name from the backend, if any. */ - name?: string | null - registeredAt: string -} - -/** - * Stateful scanner driver. Owns the camera lifecycle (delegated to - * useQRScanner upstream), the QR decode, the - * `events_ticket_register` RPC call, and a session-local scanned - * list persisted to localStorage so a page refresh doesn't ask the - * organizer to rescan tickets they already counted. - * - * Mirrors the LNbits admin Quasar register page's - * `events_scanned_` localStorage key with the - * `activities_scanned_` prefix. - */ -export function useTicketScanner(activityId: Ref) { - const transport = injectService( - SERVICE_TOKENS.NOSTR_TRANSPORT_SERVICE, - ) - - const isProcessing = ref(false) - const lastScan = ref(null) - const scanned = useLocalStorage( - () => `activities_scanned_${activityId.value}`, - [], - ) - - function parseTicketId(qrText: string): string { - return qrText.startsWith('ticket://') - ? qrText.slice('ticket://'.length) - : qrText - } - - async function onDecode(qrText: string): Promise { - if (isProcessing.value) return - const ticketId = parseTicketId(qrText).trim() - if (!ticketId) return - - // Session-local de-dup. Distinct from the backend's "already - // registered" — this guards against the QR being held in front - // of the camera for multiple decode frames. - if (scanned.value.some(r => r.ticketId === ticketId)) { - lastScan.value = { status: 'duplicate-session', ticketId } - return - } - - isProcessing.value = true - try { - const ticket = await transport.call>( - 'events_ticket_register', - { - event_id: activityId.value, - ticket_id: ticketId, - }, - ) - const name = (ticket?.name as string | null | undefined) ?? null - scanned.value = [ - { ticketId, name, registeredAt: new Date().toISOString() }, - ...scanned.value, - ] - lastScan.value = { status: 'ok', ticketId, ticket } - } catch (e) { - // Backend RPC errors arrive as NostrRpcError with the - // string in `.message`: "Ticket not paid for", "Ticket - // already registered", "Ticket does not exist on this - // event", "You do not own this event", etc. - lastScan.value = { - status: 'error', - ticketId, - message: e instanceof Error ? e.message : String(e), - } - } finally { - isProcessing.value = false - } - } - - function clearScanned() { - scanned.value = [] - lastScan.value = null - } - - function dismissLastScan() { - lastScan.value = null - } - - return { - isProcessing, - lastScan, - scanned, - onDecode, - clearScanned, - dismissLastScan, - } -} diff --git a/src/modules/activities/index.ts b/src/modules/activities/index.ts index 4d7bc9c..ef0b957 100644 --- a/src/modules/activities/index.ts +++ b/src/modules/activities/index.ts @@ -78,15 +78,6 @@ export const activitiesModule = createModulePlugin({ requiresAuth: true, }, }, - { - path: '/scan/:activityId', - name: 'scan-tickets', - component: () => import('./views/ScanTicketsPage.vue'), - meta: { - title: 'Scan Tickets', - requiresAuth: true, - }, - }, { path: '/events', name: 'events', diff --git a/src/modules/activities/views/ActivityDetailPage.vue b/src/modules/activities/views/ActivityDetailPage.vue index fe9d322..84a0c64 100644 --- a/src/modules/activities/views/ActivityDetailPage.vue +++ b/src/modules/activities/views/ActivityDetailPage.vue @@ -8,7 +8,7 @@ import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Separator } from '@/components/ui/separator' import { - Calendar, MapPin, ArrowLeft, Pencil, Ticket, CheckCircle2, ScanLine, + Calendar, MapPin, ArrowLeft, Pencil, Ticket, CheckCircle2, } from 'lucide-vue-next' import { useActivityDetail } from '../composables/useActivityDetail' import BookmarkButton from '../components/BookmarkButton.vue' @@ -64,10 +64,6 @@ function openEditDialog() { activitiesStore.showCreateDialog = true } -function openScannerPage() { - router.push({ name: 'scan-tickets', params: { activityId } }) -} - const dateDisplay = computed(() => { if (!activity.value) return '' const a = activity.value @@ -161,17 +157,6 @@ function goToMyTickets() { Back
-