Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
139 changes: 79 additions & 60 deletions static/js/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ window.PageEventsDisplay = {
template: '#page-events-display',
data() {
return {
eventName: '',
eventErrorLabel: '',
event: null,
paymentReq: null,
redirectUrl: null,
formDialog: {
Expand All @@ -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() {
Expand All @@ -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)
}
},
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -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
}
}
}
}
}
30 changes: 20 additions & 10 deletions static/js/display.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<template id="page-events-display">
<div class="row q-col-gutter-md justify-center">
<div v-if="event" class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card>
<q-img v-if="banner" :src="banner" transition="slide-up"></q-img>
<q-img
v-if="event.banner"
:src="event.banner"
transition="slide-up"
></q-img>
<q-card-section class="q-pa-none">
<h3 class="q-my-none q-pa-lg" v-text="eventName"></h3>
<br />
<div v-html="formatDescription" class="q-pa-md"></div>
<br />
<h3 class="q-my-none q-pa-lg" v-text="event.name"></h3>
<div v-html="event.info" class="q-pa-lg"></div>
</q-card-section>
</q-card>
<q-card class="q-pa-lg">
Expand Down Expand Up @@ -44,11 +46,10 @@
:hint="`If minimum tickets (${this.extra?.min_tickets}) are not met, refund will be sent.`"
></q-input>
<q-input
v-if="hasPromoCodes"
filled
dense
v-model.trim="formDialog.data.promo_code"
label="Apply Promo Code "
label="(optional) Promo Code "
></q-input>
<div class="row q-mt-lg">
<q-btn
Expand All @@ -63,7 +64,7 @@
>Submit</q-btn
>
<q-btn @click="resetForm" flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>Clear</q-btn
>
</div>
</q-form>
Expand Down Expand Up @@ -97,7 +98,7 @@
<div class="text-center q-mb-lg">
<lnbits-qrcode
:href="'lightning:' + receive.paymentReq"
:value="'lightning:' + receive.paymentReq.toUpperCase()"
:value="'LIGHTNING:' + receive.paymentReq.toUpperCase()"
></lnbits-qrcode>
</div>
<div class="row q-mt-lg">
Expand All @@ -112,4 +113,13 @@
</q-card>
</q-dialog>
</div>
<div v-else class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card class="q-pa-lg">
<q-card-section class="q-pa-none">
<h3 class="q-my-none q-pa-lg" v-text="eventErrorLabel"></h3>
</q-card-section>
</q-card>
</div>
</div>
</template>
Loading
Loading