284 lines
9.3 KiB
HTML
284 lines
9.3 KiB
HTML
{% extends "public.html" %} {% block toolbar_title %} LNbits Relay
|
|
<q-icon name="sensors" class="q-ml-lg" />
|
|
{% endblock %} {% block footer %}{% endblock %} {% block page_container %}
|
|
|
|
<q-page-container>
|
|
<q-page>
|
|
<div class="row q-col-gutter-md">
|
|
<div class="col-12 col-md-2 q-gutter-y-md"></div>
|
|
<div class="col-12 col-md-6 q-gutter-y-md q-pa-xl">
|
|
<q-card>
|
|
<q-card-section>
|
|
<h4 v-text="relay.name" class="q-my-none"></h4>
|
|
</q-card-section>
|
|
<div v-if="relay.description" class="q-ma-lg">
|
|
<q-separator></q-separator>
|
|
<span class="text-subtitle1" v-text="relay.description"></span>
|
|
</div>
|
|
</q-card>
|
|
<q-card class="q-pb-xl">
|
|
<q-card-section>
|
|
<div class="row">
|
|
<div class="col-2 q-pt-sm">
|
|
<span class="text-bold">Relay Link:</span>
|
|
</div>
|
|
<div class="col-8">
|
|
<q-input
|
|
filled
|
|
dense
|
|
readonly
|
|
v-model.trim="wssLink"
|
|
type="text"
|
|
label="Relay Link"
|
|
></q-input>
|
|
</div>
|
|
<div class="col-2">
|
|
<q-btn
|
|
outline
|
|
color="grey"
|
|
class="float-right"
|
|
@click="copyText(wssLink)"
|
|
>Copy</q-btn
|
|
>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
<q-card-section v-if="relay.meta.isPaidRelay">
|
|
<div class="row">
|
|
<div class="col-2 q-pt-sm">
|
|
<span class="text-bold">Public Key:</span>
|
|
</div>
|
|
<div class="col-8">
|
|
<q-input
|
|
filled
|
|
dense
|
|
v-model.trim="pubkey"
|
|
type="text"
|
|
label="User Public Key"
|
|
></q-input>
|
|
</div>
|
|
<div class="col-2"></div>
|
|
</div>
|
|
</q-card-section>
|
|
<q-card-section v-if="relay.meta.isPaidRelay">
|
|
<div class="row">
|
|
<div class="col-2">
|
|
<span class="text-bold">Cost to join: </span>
|
|
</div>
|
|
<div class="col-6">
|
|
<div>
|
|
<span v-text="relay.meta.costToJoin"></span>
|
|
<span class="text-bold q-ml-sm">sats</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-4">
|
|
<div v-if="relay.meta.costToJoin">
|
|
<q-btn
|
|
@click="createInvoice('join')"
|
|
unelevated
|
|
color="primary"
|
|
class="float-right"
|
|
>Pay to Join</q-btn
|
|
>
|
|
</div>
|
|
<div v-else>
|
|
<q-badge color="green" class="float-right"
|
|
><span>Free to join</span>
|
|
</q-badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
<q-card-section v-if="relay.meta.isPaidRelay">
|
|
<div class="row q-mt-md q-mb-md">
|
|
<div class="col-2 q-pt-sm">
|
|
<span class="text-bold">Storage cost: </span>
|
|
</div>
|
|
<div class="col-3 q-pt-sm">
|
|
<span v-text="relay.meta.storageCostValue"></span>
|
|
<span class="text-bold q-ml-sm"> sats per</span>
|
|
<q-badge color="orange">
|
|
<span v-text="relay.meta.storageCostUnit"></span>
|
|
</q-badge>
|
|
</div>
|
|
<div class="col-2">
|
|
<q-input
|
|
v-if="relay.meta.storageCostValue"
|
|
filled
|
|
dense
|
|
v-model="unitsToBuy"
|
|
type="number"
|
|
min="0"
|
|
:label="relay.meta.storageCostUnit"
|
|
></q-input>
|
|
</div>
|
|
<div class="col-2 q-pt-sm">
|
|
<div v-if="relay.meta.storageCostValue">
|
|
<span class="text-bold q-ml-md" v-text="storageCost"></span>
|
|
<span>sats</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-3">
|
|
<div v-if="relay.meta.storageCostValue">
|
|
<q-btn
|
|
@click="createInvoice('storage')"
|
|
unelevated
|
|
color="primary"
|
|
class="float-right"
|
|
>Buy storage space</q-btn
|
|
>
|
|
</div>
|
|
<div v-else>
|
|
<q-badge color="green" class="float-right"
|
|
><span>Free storage</span>
|
|
</q-badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<q-separator></q-separator>
|
|
</q-card-section>
|
|
<q-card-section v-else>
|
|
<q-badge color="yellow" text-color="black">
|
|
<h5 class="text-subtitle1 q-my-none">
|
|
This is a free Nostr Relay
|
|
</h5>
|
|
</q-badge>
|
|
</q-card-section>
|
|
<q-card-section>
|
|
<q-expansion-item
|
|
v-if="invoice"
|
|
group="join-invoice"
|
|
label="Invoice"
|
|
:content-inset-level="0.5"
|
|
default-opened
|
|
>
|
|
<div class="row q-ma-md">
|
|
<div class="col-3"></div>
|
|
<div class="col-6 text-center">
|
|
<q-btn outline color="grey" @click="copyText(invoice)"
|
|
>Copy invoice</q-btn
|
|
>
|
|
</div>
|
|
<div class="col-3"></div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-3"></div>
|
|
<div class="col-6">
|
|
<q-responsive :ratio="1">
|
|
<qrcode
|
|
:value="'lightning:'+invoice"
|
|
:options="{width: 340}"
|
|
class="rounded-borders"
|
|
></qrcode>
|
|
</q-responsive>
|
|
</div>
|
|
<div class="col-3"></div>
|
|
</div>
|
|
</q-expansion-item>
|
|
<q-expansion-item v-else-if="invoiceResponse">
|
|
<div class="row">
|
|
<div class="col-3"></div>
|
|
<div class="col-6">
|
|
<q-icon
|
|
v-if="invoiceResponse.success"
|
|
name="check"
|
|
style="color: green; font-size: 21.4em"
|
|
></q-icon>
|
|
<span v-else v-text="invoiceResponse.message"></span>
|
|
</div>
|
|
<div class="col-3"></div>
|
|
</div>
|
|
</q-expansion-item>
|
|
</q-card-section>
|
|
</q-card>
|
|
</div>
|
|
</div>
|
|
</q-page>
|
|
</q-page-container>
|
|
{% endblock %} {% block scripts %}
|
|
<script>
|
|
window.app = Vue.createApp({
|
|
el: '#vue',
|
|
mixins: [windowMixin],
|
|
data() {
|
|
return {
|
|
relay: JSON.parse('{{relay | tojson | safe}}'),
|
|
pubkey: '',
|
|
invoice: '',
|
|
invoiceResponse: null,
|
|
unitsToBuy: 0
|
|
}
|
|
},
|
|
computed: {
|
|
storageCost: function () {
|
|
if (!this.relay || !this.relay.meta.storageCostValue) return 0
|
|
return this.unitsToBuy * this.relay.meta.storageCostValue
|
|
},
|
|
wssLink: function () {
|
|
this.relay.meta.domain =
|
|
this.relay.meta.domain || window.location.hostname
|
|
return (
|
|
'wss://' + this.relay.meta.domain + '/nostrrelay/' + this.relay.id
|
|
)
|
|
}
|
|
},
|
|
methods: {
|
|
createInvoice: async function (action) {
|
|
if (!action) return
|
|
this.invoice = ''
|
|
if (!this.pubkey) {
|
|
this.$q.notify({
|
|
timeout: 5000,
|
|
type: 'warning',
|
|
message: 'Public key is missing'
|
|
})
|
|
return
|
|
}
|
|
try {
|
|
const reqData = {
|
|
action,
|
|
relay_id: this.relay.id,
|
|
pubkey: this.pubkey,
|
|
units_to_buy: this.unitsToBuy
|
|
}
|
|
const {data} = await LNbits.api.request(
|
|
'PUT',
|
|
'/nostrrelay/api/v1/pay',
|
|
'',
|
|
reqData
|
|
)
|
|
this.invoice = data.invoice
|
|
const paymentHashTag = decode(data.invoice).data.tags.find(
|
|
t => t && t.description === 'payment_hash'
|
|
)
|
|
if (paymentHashTag) {
|
|
await this.waitForPaidInvoice(paymentHashTag.value)
|
|
}
|
|
} catch (error) {
|
|
LNbits.utils.notifyApiError(error)
|
|
}
|
|
},
|
|
waitForPaidInvoice: function (paymentHash) {
|
|
try {
|
|
const scheme = location.protocol === 'http:' ? 'ws' : 'wss'
|
|
const port = location.port ? `:${location.port}` : ''
|
|
const wsUrl = `${scheme}://${document.domain}${port}/api/v1/ws/${paymentHash}`
|
|
const wsConnection = new WebSocket(wsUrl)
|
|
wsConnection.onmessage = e => {
|
|
this.invoiceResponse = JSON.parse(e.data)
|
|
this.invoice = null
|
|
wsConnection.close()
|
|
}
|
|
} catch (error) {
|
|
Quasar.Notify.create({
|
|
timeout: 5000,
|
|
type: 'warning',
|
|
message: 'Failed to get invoice status',
|
|
caption: `${error}`
|
|
})
|
|
}
|
|
}
|
|
}
|
|
})
|
|
</script>
|
|
{% endblock %}
|