lnurl-pay and lnurl-withdraw UI.
This commit is contained in:
parent
7a5159f293
commit
3cd15c40fc
|
@ -14,12 +14,8 @@ function generateChart(canvas, payments) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(
|
_.each(
|
||||||
payments
|
payments.filter(p => !p.pending).sort((a, b) => a.time - b.time),
|
||||||
.filter(p => !p.pending)
|
tx => {
|
||||||
.sort(function (a, b) {
|
|
||||||
return a.time - b.time
|
|
||||||
}),
|
|
||||||
function (tx) {
|
|
||||||
txs.push({
|
txs.push({
|
||||||
hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'),
|
hour: Quasar.utils.date.formatDate(tx.date, 'YYYY-MM-DDTHH:00'),
|
||||||
sat: tx.sat
|
sat: tx.sat
|
||||||
|
@ -27,19 +23,15 @@ function generateChart(canvas, payments) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
_.each(_.groupBy(txs, 'hour'), function (value, day) {
|
_.each(_.groupBy(txs, 'hour'), (value, day) => {
|
||||||
var income = _.reduce(
|
var income = _.reduce(
|
||||||
value,
|
value,
|
||||||
function (memo, tx) {
|
(memo, tx) => (tx.sat >= 0 ? memo + tx.sat : memo),
|
||||||
return tx.sat >= 0 ? memo + tx.sat : memo
|
|
||||||
},
|
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
var outcome = _.reduce(
|
var outcome = _.reduce(
|
||||||
value,
|
value,
|
||||||
function (memo, tx) {
|
(memo, tx) => (tx.sat < 0 ? memo + Math.abs(tx.sat) : memo),
|
||||||
return tx.sat < 0 ? memo + Math.abs(tx.sat) : memo
|
|
||||||
},
|
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
n = n + income - outcome
|
n = n + income - outcome
|
||||||
|
@ -124,23 +116,27 @@ new Vue({
|
||||||
show: false,
|
show: false,
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
paymentReq: null,
|
paymentReq: null,
|
||||||
|
minMax: [0, 2100000000000000],
|
||||||
|
lnurl: null,
|
||||||
data: {
|
data: {
|
||||||
amount: null,
|
amount: null,
|
||||||
memo: ''
|
memo: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
send: {
|
parse: {
|
||||||
show: false,
|
show: false,
|
||||||
invoice: null,
|
invoice: null,
|
||||||
lnurl: {},
|
lnurlpay: null,
|
||||||
data: {
|
data: {
|
||||||
request: ''
|
request: '',
|
||||||
|
amount: 0
|
||||||
|
},
|
||||||
|
paymentChecker: null,
|
||||||
|
camera: {
|
||||||
|
show: false,
|
||||||
|
camera: 'auto'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
theCamera: {
|
|
||||||
show: false,
|
|
||||||
camera: 'auto'
|
|
||||||
},
|
|
||||||
payments: [],
|
payments: [],
|
||||||
paymentsTable: {
|
paymentsTable: {
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -197,8 +193,8 @@ new Vue({
|
||||||
return LNbits.utils.search(this.payments, q)
|
return LNbits.utils.search(this.payments, q)
|
||||||
},
|
},
|
||||||
canPay: function () {
|
canPay: function () {
|
||||||
if (!this.send.invoice) return false
|
if (!this.parse.invoice) return false
|
||||||
return this.send.invoice.sat <= this.balance
|
return this.parse.invoice.sat <= this.balance
|
||||||
},
|
},
|
||||||
pendingPaymentsExist: function () {
|
pendingPaymentsExist: function () {
|
||||||
return this.payments
|
return this.payments
|
||||||
|
@ -206,56 +202,55 @@ new Vue({
|
||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
filters: {
|
||||||
|
msatoshiFormat: function (value) {
|
||||||
|
return LNbits.utils.formatSat(value / 1000)
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeCamera: function () {
|
closeCamera: function () {
|
||||||
this.theCamera.show = false
|
this.parse.camera.show = false
|
||||||
},
|
},
|
||||||
showCamera: function () {
|
showCamera: function () {
|
||||||
this.theCamera.show = true
|
this.parse.camera.show = true
|
||||||
},
|
},
|
||||||
showChart: function () {
|
showChart: function () {
|
||||||
this.paymentsChart.show = true
|
this.paymentsChart.show = true
|
||||||
this.$nextTick(function () {
|
this.$nextTick(() => {
|
||||||
generateChart(this.$refs.canvas, this.payments)
|
generateChart(this.$refs.canvas, this.payments)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
showReceiveDialog: function () {
|
showReceiveDialog: function () {
|
||||||
this.receive = {
|
this.receive.show = true
|
||||||
show: true,
|
this.receive.status = 'pending'
|
||||||
status: 'pending',
|
this.receive.paymentReq = null
|
||||||
paymentReq: null,
|
this.receive.data.amount = null
|
||||||
data: {
|
this.receive.data.memo = null
|
||||||
amount: null,
|
this.receive.paymentChecker = null
|
||||||
memo: ''
|
this.receive.minMax = [0, 2100000000000000]
|
||||||
},
|
this.receive.lnurl = null
|
||||||
paymentChecker: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
showSendDialog: function () {
|
showParseDialog: function () {
|
||||||
this.send = {
|
this.parse.show = true
|
||||||
show: true,
|
this.parse.invoice = null
|
||||||
invoice: null,
|
this.parse.lnurlpay = null
|
||||||
lnurl: {},
|
this.parse.data.request = ''
|
||||||
data: {
|
this.parse.data.paymentChecker = null
|
||||||
request: ''
|
this.parse.camera.show = false
|
||||||
},
|
|
||||||
paymentChecker: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
closeReceiveDialog: function () {
|
closeReceiveDialog: function () {
|
||||||
var checker = this.receive.paymentChecker
|
var checker = this.receive.paymentChecker
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
clearInterval(checker)
|
clearInterval(checker)
|
||||||
}, 10000)
|
}, 10000)
|
||||||
},
|
},
|
||||||
closeSendDialog: function () {
|
closeParseDialog: function () {
|
||||||
var checker = this.send.paymentChecker
|
var checker = this.parse.paymentChecker
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
clearInterval(checker)
|
clearInterval(checker)
|
||||||
}, 1000)
|
}, 1000)
|
||||||
},
|
},
|
||||||
createInvoice: function () {
|
createInvoice: function () {
|
||||||
var self = this
|
|
||||||
this.receive.status = 'loading'
|
this.receive.status = 'loading'
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.createInvoice(
|
.createInvoice(
|
||||||
|
@ -263,59 +258,96 @@ new Vue({
|
||||||
this.receive.data.amount,
|
this.receive.data.amount,
|
||||||
this.receive.data.memo
|
this.receive.data.memo
|
||||||
)
|
)
|
||||||
.then(function (response) {
|
.then(response => {
|
||||||
self.receive.status = 'success'
|
this.receive.status = 'success'
|
||||||
self.receive.paymentReq = response.data.payment_request
|
this.receive.paymentReq = response.data.payment_request
|
||||||
|
|
||||||
self.receive.paymentChecker = setInterval(function () {
|
if (this.receive.lnurl) {
|
||||||
|
// send invoice to lnurl callback
|
||||||
|
console.log('sending', this.receive.lnurl)
|
||||||
|
LNbits.api.sendInvoiceToLnurlWithdraw(this.receive.paymentReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.receive.paymentChecker = setInterval(() => {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.getPayment(self.g.wallet, response.data.payment_hash)
|
.getPayment(this.g.wallet, response.data.payment_hash)
|
||||||
.then(function (response) {
|
.then(response => {
|
||||||
if (response.data.paid) {
|
if (response.data.paid) {
|
||||||
self.fetchPayments()
|
this.fetchPayments()
|
||||||
self.receive.show = false
|
this.receive.show = false
|
||||||
clearInterval(self.receive.paymentChecker)
|
clearInterval(this.receive.paymentChecker)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 2000)
|
}, 2000)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(err => {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(err)
|
||||||
self.receive.status = 'pending'
|
this.receive.status = 'pending'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
decodeQR: function (res) {
|
decodeQR: function (res) {
|
||||||
this.send.data.request = res
|
this.parse.data.request = res
|
||||||
this.decodeRequest()
|
this.decodeRequest()
|
||||||
this.sendCamera.show = false
|
this.parse.camera.show = false
|
||||||
},
|
},
|
||||||
decodeRequest: function () {
|
decodeRequest: function () {
|
||||||
if (this.send.data.request.startsWith('lightning:')) {
|
this.parse.show = true
|
||||||
this.send.data.request = this.send.data.request.slice(10)
|
|
||||||
|
if (this.parse.data.request.startsWith('lightning:')) {
|
||||||
|
this.parse.data.request = this.parse.data.request.slice(10)
|
||||||
}
|
}
|
||||||
if (this.send.data.request.startsWith('lnurl:')) {
|
if (this.parse.data.request.startsWith('lnurl:')) {
|
||||||
this.send.data.request = this.send.data.request.slice(6)
|
this.parse.data.request = this.parse.data.request.slice(6)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.send.data.request.toLowerCase().startsWith('lnurl1')) {
|
if (this.parse.data.request.toLowerCase().startsWith('lnurl1')) {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.request(
|
.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/api/v1/lnurlscan/' + this.send.data.request,
|
'/api/v1/lnurlscan/' + this.parse.data.request,
|
||||||
this.g.user.wallets[0].adminkey
|
this.g.user.wallets[0].adminkey
|
||||||
)
|
)
|
||||||
.then(function (response) {
|
.catch(err => {
|
||||||
this.send.lnurl[response.kind] = Object.freeze(response)
|
LNbits.utils.notifyApiError(err)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.then(response => {
|
||||||
LNbits.utils.notifyApiError(error)
|
let data = response.data
|
||||||
|
|
||||||
|
if (data.status === 'ERROR') {
|
||||||
|
Quasar.plugins.Notify.create({
|
||||||
|
timeout: 5000,
|
||||||
|
type: 'warning',
|
||||||
|
message: data.reason,
|
||||||
|
caption: `${data.domain} returned an error to the lnurl call.`,
|
||||||
|
icon: null
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.kind === 'pay') {
|
||||||
|
this.parse.lnurlpay = Object.freeze(data)
|
||||||
|
this.parse.data.amount = data.minSendable / 1000
|
||||||
|
} else if (data.kind === 'withdraw') {
|
||||||
|
this.parse.show = false
|
||||||
|
this.receive.show = true
|
||||||
|
this.receive.status = 'pending'
|
||||||
|
this.receive.data.amount = data.maxWithdrawable
|
||||||
|
this.receive.data.memo = data.defaultDescription
|
||||||
|
this.receive.minMax = [data.minWithdrawable, data.maxWithdrawable]
|
||||||
|
this.receive.lnurl = {
|
||||||
|
domain: data.domain,
|
||||||
|
callback: data.callback,
|
||||||
|
k1: data.k1,
|
||||||
|
fixed: data.fixed
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let invoice
|
let invoice
|
||||||
try {
|
try {
|
||||||
invoice = decode(this.send.data.bolt11)
|
invoice = decode(this.parse.data.bolt11)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
|
@ -324,6 +356,7 @@ new Vue({
|
||||||
caption: '400 BAD REQUEST',
|
caption: '400 BAD REQUEST',
|
||||||
icon: null
|
icon: null
|
||||||
})
|
})
|
||||||
|
this.parse.show = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +366,7 @@ new Vue({
|
||||||
fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
|
fsat: LNbits.utils.formatSat(invoice.human_readable_part.amount / 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(invoice.data.tags, function (tag) {
|
_.each(invoice.data.tags, tag => {
|
||||||
if (_.isObject(tag) && _.has(tag, 'description')) {
|
if (_.isObject(tag) && _.has(tag, 'description')) {
|
||||||
if (tag.description === 'payment_hash') {
|
if (tag.description === 'payment_hash') {
|
||||||
cleanInvoice.hash = tag.value
|
cleanInvoice.hash = tag.value
|
||||||
|
@ -352,11 +385,9 @@ new Vue({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.send.invoice = Object.freeze(cleanInvoice)
|
this.parse.invoice = Object.freeze(cleanInvoice)
|
||||||
},
|
},
|
||||||
payInvoice: function () {
|
payInvoice: function () {
|
||||||
var self = this
|
|
||||||
|
|
||||||
let dismissPaymentMsg = this.$q.notify({
|
let dismissPaymentMsg = this.$q.notify({
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
message: 'Processing payment...',
|
message: 'Processing payment...',
|
||||||
|
@ -364,55 +395,80 @@ new Vue({
|
||||||
})
|
})
|
||||||
|
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.payInvoice(this.g.wallet, this.send.data.bolt11)
|
.payInvoice(this.g.wallet, this.parse.data.bolt11)
|
||||||
.then(function (response) {
|
.then(response => {
|
||||||
self.send.paymentChecker = setInterval(function () {
|
this.parse.paymentChecker = setInterval(() => {
|
||||||
LNbits.api
|
LNbits.api
|
||||||
.getPayment(self.g.wallet, response.data.payment_hash)
|
.getPayment(this.g.wallet, response.data.payment_hash)
|
||||||
.then(function (res) {
|
.then(res => {
|
||||||
if (res.data.paid) {
|
if (res.data.paid) {
|
||||||
self.send.show = false
|
this.parse.show = false
|
||||||
clearInterval(self.send.paymentChecker)
|
clearInterval(this.parse.paymentChecker)
|
||||||
dismissPaymentMsg()
|
dismissPaymentMsg()
|
||||||
self.fetchPayments()
|
this.fetchPayments()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 2000)
|
}, 2000)
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
.catch(err => {
|
||||||
dismissPaymentMsg()
|
dismissPaymentMsg()
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(err)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
payLnurl: function () {
|
||||||
|
let dismissPaymentMsg = this.$q.notify({
|
||||||
|
timeout: 0,
|
||||||
|
message: 'Processing payment...',
|
||||||
|
icon: null
|
||||||
|
})
|
||||||
|
|
||||||
|
LNbits.api
|
||||||
|
.payInvoice(this.g.wallet, this.parse.data.bolt11)
|
||||||
|
.then(response => {
|
||||||
|
this.parse.paymentChecker = setInterval(() => {
|
||||||
|
LNbits.api
|
||||||
|
.getPayment(this.g.wallet, response.data.payment_hash)
|
||||||
|
.then(res => {
|
||||||
|
if (res.data.paid) {
|
||||||
|
this.parse.show = false
|
||||||
|
clearInterval(this.parse.paymentChecker)
|
||||||
|
dismissPaymentMsg()
|
||||||
|
this.fetchPayments()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
dismissPaymentMsg()
|
||||||
|
LNbits.utils.notifyApiError(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteWallet: function (walletId, user) {
|
deleteWallet: function (walletId, user) {
|
||||||
LNbits.utils
|
LNbits.utils
|
||||||
.confirmDialog('Are you sure you want to delete this wallet?')
|
.confirmDialog('Are you sure you want to delete this wallet?')
|
||||||
.onOk(function () {
|
.onOk(() => {
|
||||||
LNbits.href.deleteWallet(walletId, user)
|
LNbits.href.deleteWallet(walletId, user)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchPayments: function (checkPending) {
|
fetchPayments: function (checkPending) {
|
||||||
var self = this
|
|
||||||
|
|
||||||
return LNbits.api
|
return LNbits.api
|
||||||
.getPayments(this.g.wallet, checkPending)
|
.getPayments(this.g.wallet, checkPending)
|
||||||
.then(function (response) {
|
.then(response => {
|
||||||
self.payments = response.data
|
this.payments = response.data
|
||||||
.map(function (obj) {
|
.map(obj => {
|
||||||
return LNbits.map.payment(obj)
|
return LNbits.map.payment(obj)
|
||||||
})
|
})
|
||||||
.sort(function (a, b) {
|
.sort((a, b) => {
|
||||||
return b.time - a.time
|
return b.time - a.time
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchBalance: function () {
|
fetchBalance: function () {
|
||||||
var self = this
|
LNbits.api.getWallet(this.g.wallet).then(response => {
|
||||||
LNbits.api.getWallet(self.g.wallet).then(function (response) {
|
this.balance = Math.round(response.data.balance / 1000)
|
||||||
self.balance = Math.round(response.data.balance / 1000)
|
|
||||||
EventHub.$emit('update-wallet-balance', [
|
EventHub.$emit('update-wallet-balance', [
|
||||||
self.g.wallet.id,
|
this.g.wallet.id,
|
||||||
self.balance
|
this.balance
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -423,7 +479,7 @@ new Vue({
|
||||||
icon: null
|
icon: null
|
||||||
})
|
})
|
||||||
|
|
||||||
this.fetchPayments(true).then(function () {
|
this.fetchPayments(true).then(() => {
|
||||||
dismissMsg()
|
dismissMsg()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
unelevated
|
unelevated
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
@click="showSendDialog"
|
@click="showParseDialog"
|
||||||
>Paste Request</q-btn
|
>Paste Request</q-btn
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
@ -249,18 +249,26 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
|
<q-dialog v-model="receive.show" @hide="closeReceiveDialog">
|
||||||
|
{% raw %}
|
||||||
<q-card
|
<q-card
|
||||||
v-if="!receive.paymentReq"
|
v-if="!receive.paymentReq"
|
||||||
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||||
>
|
>
|
||||||
<q-form @submit="createInvoice" class="q-gutter-md">
|
<q-form @submit="createInvoice" class="q-gutter-md">
|
||||||
|
<p v-if="receive.lnurl" class="text-h6 text-center q-my-none">
|
||||||
|
<b>{{receive.lnurl.domain}}</b> is requesting an invoice:
|
||||||
|
</p>
|
||||||
|
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.number="receive.data.amount"
|
v-model.number="receive.data.amount"
|
||||||
type="number"
|
type="number"
|
||||||
label="Amount (sat) *"
|
label="Amount (sat) *"
|
||||||
|
min="receive.minMax[0]"
|
||||||
|
max="receive.minMax[1]"
|
||||||
|
:readonly="receive.lnurl && receive.lnurl.fixed"
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
|
@ -275,8 +283,12 @@
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
:disable="receive.data.amount == null || receive.data.amount <= 0"
|
:disable="receive.data.amount == null || receive.data.amount <= 0"
|
||||||
type="submit"
|
type="submit"
|
||||||
>Create invoice</q-btn
|
|
||||||
>
|
>
|
||||||
|
<span v-if="receive.lnurl">
|
||||||
|
Withdraw from {{receive.lnurl.domain}}
|
||||||
|
</span>
|
||||||
|
<span v-else> Create invoice </span>
|
||||||
|
</q-btn>
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
</div>
|
</div>
|
||||||
<q-spinner
|
<q-spinner
|
||||||
|
@ -305,20 +317,79 @@
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
{% endraw %}
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="send.show" position="top" @hide="closeSendDialog">
|
<q-dialog v-model="parse.show" @hide="closeParseDialog">
|
||||||
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
||||||
<div v-if="!send.invoice">
|
<div v-if="parse.invoice">
|
||||||
|
{% raw %}
|
||||||
|
<h6 class="q-my-none">{{ parse.invoice.fsat }} sat</h6>
|
||||||
|
<q-separator class="q-my-sm"></q-separator>
|
||||||
|
<p style="word-break: break-all">
|
||||||
|
<strong>Description:</strong> {{ parse.invoice.description }}<br />
|
||||||
|
<strong>Expire date:</strong> {{ parse.invoice.expireDate }}<br />
|
||||||
|
<strong>Hash:</strong> {{ parse.invoice.hash }}
|
||||||
|
</p>
|
||||||
|
{% endraw %}
|
||||||
|
<div v-if="canPay" class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="deep-purple" @click="payInvoice">Pay</q-btn>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
|
</div>
|
||||||
|
<div v-else class="row q-mt-lg">
|
||||||
|
<q-btn unelevated disabled color="yellow" text-color="black"
|
||||||
|
>Not enough funds!</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="parse.lnurlpay">
|
||||||
|
{% raw %}
|
||||||
|
<q-form @submit="payLnurl" class="q-gutter-md">
|
||||||
|
<p v-if="parse.lnurlpay.fixed" class="q-my-none text-h6">
|
||||||
|
{{ parse.lnurlpay.maxSendable | msatoshiFormat }}
|
||||||
|
</p>
|
||||||
|
<p v-else class="q-my-none text-h6 text-center">
|
||||||
|
<b>{{ parse.lnurlpay.domain }}</b> is requesting <br />
|
||||||
|
between <b>{{ parse.lnurlpay.minSendable | msatoshiFormat }}</b> and
|
||||||
|
<b>{{ parse.lnurlpay.maxSendable | msatoshiFormat }}</b> sat
|
||||||
|
</p>
|
||||||
|
<q-separator class="q-my-sm"></q-separator>
|
||||||
|
<p class="text-justify text-italic">{{ parse.lnurlpay.description }}</p>
|
||||||
|
<p v-if="parse.lnurlpay.image">
|
||||||
|
<q-img :src="parse.lnurlpay.image" width="50%" />
|
||||||
|
</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.number="parse.data.amount"
|
||||||
|
type="number"
|
||||||
|
label="Amount (sat) *"
|
||||||
|
min="parse.lnurlpay.minSendable / 1000"
|
||||||
|
max="parse.lnurlpay.maxSendable / 1000"
|
||||||
|
:readonly="parse.lnurlpay.fixed"
|
||||||
|
></q-input>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="deep-purple" type="submit"
|
||||||
|
>Send satoshis</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
{% endraw %}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
<q-form
|
<q-form
|
||||||
v-if="!theCamera.show"
|
v-if="!parse.camera.show"
|
||||||
@submit="decodeInvoice"
|
@submit="decodeRequest"
|
||||||
class="q-gutter-md"
|
class="q-gutter-md"
|
||||||
>
|
>
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
dense
|
dense
|
||||||
v-model.trim="send.data.request"
|
v-model.trim="parse.data.request"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
label="Paste an invoice, payment request or lnurl code *"
|
label="Paste an invoice, payment request or lnurl code *"
|
||||||
>
|
>
|
||||||
|
@ -327,7 +398,7 @@
|
||||||
<q-btn
|
<q-btn
|
||||||
unelevated
|
unelevated
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
:disable="send.data.request == ''"
|
:disable="parse.data.request == ''"
|
||||||
type="submit"
|
type="submit"
|
||||||
>Read</q-btn
|
>Read</q-btn
|
||||||
>
|
>
|
||||||
|
@ -344,62 +415,16 @@
|
||||||
></qrcode-stream>
|
></qrcode-stream>
|
||||||
</q-responsive>
|
</q-responsive>
|
||||||
<div class="row q-mt-lg">
|
<div class="row q-mt-lg">
|
||||||
<q-btn @click="closeCamera" flat color="grey" class="q-ml-auto"
|
<q-btn @click="closeCamera" flat color="grey" class="q-ml-auto">
|
||||||
>Cancel</q-btn
|
Cancel
|
||||||
>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="send.lnurl.withdraw">
|
|
||||||
{% raw %}
|
|
||||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
|
||||||
<q-separator class="q-my-sm"></q-separator>
|
|
||||||
<p style="word-break: break-all">
|
|
||||||
<strong>Description:</strong> {{ send.invoice.description }}<br />
|
|
||||||
<strong>Payment hash:</strong> {{ send.invoice.hash }}<br />
|
|
||||||
<strong>Expire date:</strong> {{ send.invoice.expireDate }}
|
|
||||||
</p>
|
|
||||||
{% endraw %}
|
|
||||||
<div v-if="canPay" class="row q-mt-lg">
|
|
||||||
<q-btn unelevated color="deep-purple" @click="payInvoice"
|
|
||||||
>Send satoshis</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
|
||||||
</div>
|
|
||||||
<div v-else class="row q-mt-lg">
|
|
||||||
<q-btn unelevated disabled color="yellow" text-color="black"
|
|
||||||
>Not enough funds!</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
{% raw %}
|
|
||||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
|
||||||
<q-separator class="q-my-sm"></q-separator>
|
|
||||||
<p style="word-break: break-all">
|
|
||||||
<strong>Description:</strong> {{ send.invoice.description }}<br />
|
|
||||||
<strong>Expire date:</strong> {{ send.invoice.expireDate }}<br />
|
|
||||||
<strong>Hash:</strong> {{ send.invoice.hash }}
|
|
||||||
</p>
|
|
||||||
{% endraw %}
|
|
||||||
<div v-if="canPay" class="row q-mt-lg">
|
|
||||||
<q-btn unelevated color="deep-purple" @click="payInvoice"
|
|
||||||
>Send satoshis</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
|
||||||
</div>
|
|
||||||
<div v-else class="row q-mt-lg">
|
|
||||||
<q-btn unelevated disabled color="yellow" text-color="black"
|
|
||||||
>Not enough funds!</q-btn
|
|
||||||
>
|
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="theCamera.show" position="top">
|
<q-dialog v-model="parse.camera.show">
|
||||||
<q-card class="q-pa-lg q-pt-xl">
|
<q-card class="q-pa-lg q-pt-xl">
|
||||||
<div class="text-center q-mb-lg">
|
<div class="text-center q-mb-lg">
|
||||||
<qrcode-stream @decode="decodeQR" class="rounded-borders"></qrcode-stream>
|
<qrcode-stream @decode="decodeQR" class="rounded-borders"></qrcode-stream>
|
||||||
|
@ -412,7 +437,7 @@
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
<q-dialog v-model="paymentsChart.show" position="top">
|
<q-dialog v-model="paymentsChart.show">
|
||||||
<q-card class="q-pa-sm" style="width: 800px; max-width: unset">
|
<q-card class="q-pa-sm" style="width: 800px; max-width: unset">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<canvas ref="canvas" width="600" height="400"></canvas>
|
<canvas ref="canvas" width="600" height="400"></canvas>
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
<<<<<<< HEAD
|
|
||||||
import trio # type: ignore
|
import trio # type: ignore
|
||||||
import json
|
import json
|
||||||
|
import lnurl
|
||||||
|
import httpx
|
||||||
import traceback
|
import traceback
|
||||||
from quart import g, jsonify, request, make_response
|
from quart import g, jsonify, request, make_response
|
||||||
=======
|
|
||||||
import lnurl
|
|
||||||
from quart import g, jsonify, request
|
|
||||||
>>>>>>> da8fd9a... send/create buttons wip.
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from binascii import unhexlify
|
from binascii import unhexlify
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
from lnbits import bolt11
|
from lnbits import bolt11
|
||||||
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
||||||
|
@ -137,7 +135,6 @@ async def api_payment(payment_hash):
|
||||||
return jsonify({"paid": not payment.pending}), HTTPStatus.OK
|
return jsonify({"paid": not payment.pending}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
||||||
@api_check_wallet_key("invoice")
|
@api_check_wallet_key("invoice")
|
||||||
async def api_payments_sse():
|
async def api_payments_sse():
|
||||||
|
@ -190,8 +187,6 @@ async def api_payments_sse():
|
||||||
)
|
)
|
||||||
response.timeout = None
|
response.timeout = None
|
||||||
return response
|
return response
|
||||||
=======
|
|
||||||
return jsonify({"paid": False}), HTTPStatus.OK
|
|
||||||
|
|
||||||
|
|
||||||
@core_app.route("/api/v1/lnurlscan/<code>", methods=["GET"])
|
@core_app.route("/api/v1/lnurlscan/<code>", methods=["GET"])
|
||||||
|
@ -206,20 +201,30 @@ async def api_lnurlscan(code: str):
|
||||||
if url.is_login:
|
if url.is_login:
|
||||||
return jsonify({"domain": domain, "kind": "auth", "error": "unsupported"})
|
return jsonify({"domain": domain, "kind": "auth", "error": "unsupported"})
|
||||||
|
|
||||||
data: lnurl.LnurlResponseModel = lnurl.get(url.url)
|
r = httpx.get(url.url)
|
||||||
if not data.ok:
|
if r.is_error:
|
||||||
return jsonify({"domain": domain, "error": "failed to get parameters"})
|
return jsonify({"domain": domain, "error": "failed to get parameters"})
|
||||||
|
|
||||||
|
try:
|
||||||
|
jdata = json.loads(r.text)
|
||||||
|
data: lnurl.LnurlResponseModel = lnurl.LnurlResponse.from_dict(jdata)
|
||||||
|
except (json.decoder.JSONDecodeError, lnurl.exceptions.LnurlResponseException):
|
||||||
|
return jsonify({"domain": domain, "error": f"got invalid response '{r.text[:200]}'"})
|
||||||
|
|
||||||
if type(data) is lnurl.LnurlChannelResponse:
|
if type(data) is lnurl.LnurlChannelResponse:
|
||||||
return jsonify({"domain": domain, "kind": "channel", "error": "unsupported"})
|
return jsonify({"domain": domain, "kind": "channel", "error": "unsupported"})
|
||||||
|
|
||||||
params = data.dict()
|
params: Dict = data.dict()
|
||||||
if type(data) is lnurl.LnurlWithdrawResponse:
|
if type(data) is lnurl.LnurlWithdrawResponse:
|
||||||
params.update(kind="withdraw", fixed=data.min_withdrawable == data.max_withdrawable)
|
params.update(kind="withdraw", fixed=data.min_withdrawable == data.max_withdrawable)
|
||||||
|
|
||||||
if type(data) is lnurl.LnurlPayResponse:
|
if type(data) is lnurl.LnurlPayResponse:
|
||||||
params.update(kind="pay", fixed=data.min_sendable == data.max_sendable)
|
params.update(kind="pay", fixed=data.min_sendable == data.max_sendable)
|
||||||
|
params.update(description=data.metadata.text)
|
||||||
|
if data.metadata.images:
|
||||||
|
image = min(data.metadata.images, key=lambda image: len(image[1]))
|
||||||
|
data_uri = "data:" + image[0] + "," + image[1]
|
||||||
|
params.update(image=data_uri)
|
||||||
|
|
||||||
params.update(domain=domain)
|
params.update(domain=domain)
|
||||||
return jsonify(params)
|
return jsonify(params)
|
||||||
>>>>>>> da8fd9a... send/create buttons wip.
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user