diff --git a/models.py b/models.py index 10a319f..14547d1 100644 --- a/models.py +++ b/models.py @@ -58,6 +58,17 @@ class Event(BaseModel): extra: EventExtra = Field(default_factory=EventExtra) +class PublicEvent(BaseModel): + id: str + name: str + info: str + closing_date: str + canceled: bool + event_start_date: str + event_end_date: str + banner: str | None + + class TicketExtra(BaseModel): applied_promo_code: str | None = None sats_paid: int | None = None diff --git a/static/js/display.js b/static/js/display.js index 6b3bea2..a652ba5 100644 --- a/static/js/display.js +++ b/static/js/display.js @@ -2,7 +2,8 @@ window.PageEventsDisplay = { template: '#page-events-display', data() { return { - eventName: '', + eventErrorLabel: '', + event: null, paymentReq: null, redirectUrl: null, formDialog: { @@ -23,12 +24,14 @@ window.PageEventsDisplay = { show: false, status: 'pending', paymentReq: null - } + }, + paymentDismissMsg: null, + paymentWebsocket: null } }, async created() { this.eventId = this.$route.params.id - await this.getEvent() + this.event = await this.getEvent() }, computed: { formatDescription() { @@ -42,12 +45,9 @@ window.PageEventsDisplay = { 'GET', `/events/api/v1/events/${this.eventId}` ) - this.eventName = data.event_name - this.info = data.event_info - this.banner = data.event_banner - this.extra = data.event_extra - this.hasPromoCodes = data.has_promo_codes + return data } catch (error) { + this.eventErrorLabel = 'Event unavailable.' LNbits.utils.notifyApiError(error) } }, @@ -59,10 +59,14 @@ window.PageEventsDisplay = { }, closeReceiveDialog() { - const checker = this.receive.paymentChecker - dismissMsg() - clearInterval(paymentChecker) - setTimeout(() => {}, 10000) + if (this.paymentDismissMsg) { + this.paymentDismissMsg() + this.paymentDismissMsg = null + } + if (this.paymentWebsocket) { + this.paymentWebsocket.close() + this.paymentWebsocket = null + } }, nameValidation(val) { const regex = /[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/g @@ -75,6 +79,34 @@ window.PageEventsDisplay = { const regex = /^[\w\.-]+@[a-zA-Z\d\.-]+\.[a-zA-Z]{2,}$/ return regex.test(val) || 'Please enter valid email.' }, + paymentSuccess(paymentHash) { + if (this.paymentDismissMsg) { + this.paymentDismissMsg() + this.paymentDismissMsg = null + } + this.paymentReq = null + this.formDialog.data.name = '' + this.formDialog.data.email = '' + Quasar.Notify.create({ + type: 'positive', + message: 'Sent, thank you!', + icon: null + }) + this.receive = { + show: false, + status: 'complete', + paymentReq: null + } + this.ticketLink = { + show: true, + data: { + link: `/events/ticket/${paymentHash}` + } + } + setTimeout(() => { + window.location.href = `/events/ticket/${paymentHash}` + }, 5000) + }, async createInvoice() { try { const {data} = await LNbits.api.request( @@ -84,69 +116,56 @@ window.PageEventsDisplay = { { name: this.formDialog.data.name, email: this.formDialog.data.email, - refund: this.formDialog.data.refund || null + promo_code: this.formDialog.data.promo_code || null, + refund_address: this.formDialog.data.refund || null } ) this.paymentReq = data.payment_request - this.paymentCheck = data.payment_hash + this.paymentHash = data.payment_hash - dismissMsg = Quasar.Notify.create({ + this.paymentDismissMsg = Quasar.Notify.create({ timeout: 0, message: 'Waiting for payment...' }) - this.receive = { show: true, status: 'pending', paymentReq: this.paymentReq } - //TODO: use websockets - paymentChecker = setInterval(async () => { - try { - const res = await LNbits.api.request( - 'POST', - `/events/api/v1/tickets/${this.eventId}/${this.paymentCheck}`, - { - event: this.eventId, - event_name: this.eventName, - name: this.formDialog.data.name, - email: this.formDialog.data.email - } - ) - if (res.data.paid) { - clearInterval(paymentChecker) - dismissMsg() - this.formDialog.data.name = '' - this.formDialog.data.email = '' - - Quasar.Notify.create({ - type: 'positive', - message: 'Sent, thank you!', - icon: null - }) - this.receive = { - show: false, - status: 'complete', - paymentReq: null - } - - this.ticketLink = { - show: true, - data: { - link: `/events/ticket/${res.data.ticket_id}` - } - } - setTimeout(() => { - window.location.href = `/events/ticket/${res.data.ticket_id}` - }, 5000) - } - } catch (error) { - LNbits.utils.notifyApiError(error) - } - }, 2000) + this.websocketListener(this.paymentHash) } catch (error) { LNbits.utils.notifyApiError(error) } + }, + websocketListener(paymentHash) { + if (this.paymentWebsocket) { + this.paymentWebsocket.close() + } + + const url = new URL(window.location) + url.protocol = url.protocol === 'https:' ? 'wss' : 'ws' + url.pathname = `/events/api/v1/tickets/ws/${paymentHash}` + url.search = '' + url.hash = '' + + const ws = new WebSocket(url) + this.paymentWebsocket = ws + + ws.onmessage = event => { + const data = JSON.parse(event.data) + if (data.paid) { + this.paymentSuccess(paymentHash) + ws.close() + } + } + ws.onerror = error => { + console.error('WebSocket error:', error) + } + ws.onclose = () => { + if (this.paymentWebsocket === ws) { + this.paymentWebsocket = null + } + } } } } diff --git a/static/js/display.vue b/static/js/display.vue index 253fefe..3f80180 100644 --- a/static/js/display.vue +++ b/static/js/display.vue @@ -1,13 +1,15 @@ diff --git a/static/js/index.js b/static/js/index.js index ccfa2ba..ca34383 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -1,10 +1,3 @@ -const mapEvents = function (obj) { - obj.date = LNbits.utils.formatTimestamp(obj.time) - obj.fsat = new Intl.NumberFormat(window.g.locale).format(obj.price_per_ticket) - obj.displayUrl = ['/events/', obj.id].join('') - return obj -} - window.PageEvents = { template: '#page-events', data() { @@ -104,6 +97,7 @@ window.PageEvents = { formDialog: { show: false, data: { + currency: 'sats', extra: { promo_codes: [] } @@ -117,18 +111,15 @@ window.PageEvents = { .request( 'GET', '/events/api/v1/tickets?all_wallets=true', - this.g.user.wallets[0].inkey + this.g.user.wallets[0].adminkey ) .then(response => { - this.tickets = response.data - .map(function (obj) { - return mapEvents(obj) - }) - .filter(e => e.paid) + this.tickets = response.data.filter(e => e.paid) }) }, deleteTicket(ticketId) { const tickets = _.findWhere(this.tickets, {id: ticketId}) + const wallet = _.findWhere(this.g.user.wallets, {id: tickets.wallet}) LNbits.utils .confirmDialog('Are you sure you want to delete this ticket') @@ -137,16 +128,14 @@ window.PageEvents = { .request( 'DELETE', '/events/api/v1/tickets/' + ticketId, - _.findWhere(this.g.user.wallets, {id: tickets.wallet}).inkey + wallet.adminkey ) .then(response => { this.tickets = _.reject(this.tickets, function (obj) { return obj.id == ticketId }) }) - .catch(function (error) { - LNbits.utils.notifyApiError(error) - }) + .catch(LNbits.utils.notifyApiError) }) }, exportticketsCSV() { @@ -160,9 +149,7 @@ window.PageEvents = { this.g.user.wallets[0].inkey ) .then(response => { - this.events = response.data.map(obj => { - return mapEvents(obj) - }) + this.events = response.data this.checkCanceledEvents() }) }, @@ -189,6 +176,7 @@ window.PageEvents = { this.formDialog.data = {...data} } else { this.formDialog.data = { + currency: 'sats', extra: { conditional: false, min_tickets: 1, @@ -211,7 +199,7 @@ window.PageEvents = { LNbits.api .request('POST', '/events/api/v1/events', wallet.adminkey, data) .then(response => { - this.events.push(mapEvents(response.data)) + this.events.push(response.data) this.resetEventDialog() }) .catch(LNbits.utils.notifyApiError) @@ -232,7 +220,7 @@ window.PageEvents = { this.events = _.reject(this.events, function (obj) { return obj.id == data.id }) - this.events.push(mapEvents(response.data)) + this.events.push(response.data) this.resetEventDialog() }) .catch(LNbits.utils.notifyApiError) @@ -254,7 +242,7 @@ window.PageEvents = { return obj.id == eventsId }) }) - .catch(LNbits.utils.notifyApiError(error)) + .catch(LNbits.utils.notifyApiError) }) }, exporteventsCSV() { @@ -278,9 +266,7 @@ window.PageEvents = { message: `Event ${ev.name} has been canceled and refunds have been issued.`, icon: null }) - this.events = this.events.map(e => - e.id === ev.id ? mapEvents(data) : e - ) + this.events = this.events.map(e => (e.id === ev.id ? data : e)) } }) } @@ -289,10 +275,10 @@ window.PageEvents = { if (this.g.user.wallets.length) { this.getTickets() this.getEvents() - if (g.allowedCurrencies && g.allowedCurrencies.length) { - this.currencies = ['sats', ...g.allowedCurrencies] + if (this.g.allowedCurrencies && this.g.allowedCurrencies.length > 0) { + this.currencies = ['sats', ...this.g.allowedCurrencies] } else { - this.currencies = ['sats', ...g.currencies] + this.currencies = ['sats', ...this.g.currencies] } } } diff --git a/static/js/index.vue b/static/js/index.vue index 5e8cc4c..174f0c1 100644 --- a/static/js/index.vue +++ b/static/js/index.vue @@ -31,13 +31,12 @@ >