mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-22 22:25:47 +01:00
refactor: camera ready
This commit is contained in:
parent
49addb8d56
commit
75d97ddfc1
15 changed files with 126 additions and 96 deletions
|
@ -3,6 +3,8 @@ import importlib
|
|||
from flask import Flask
|
||||
from flask_assets import Environment, Bundle
|
||||
from flask_compress import Compress
|
||||
from flask_limiter import Limiter
|
||||
from flask_limiter.util import get_remote_address
|
||||
from flask_talisman import Talisman
|
||||
from os import getenv
|
||||
|
||||
|
@ -19,6 +21,7 @@ valid_extensions = [ext for ext in ExtensionManager().extensions if ext.is_valid
|
|||
# -----------------------
|
||||
|
||||
Compress(app)
|
||||
Limiter(app, key_func=get_remote_address, default_limits=["1 per second"])
|
||||
Talisman(
|
||||
app,
|
||||
force_https=getenv("LNBITS_WITH_ONION", 0) == 0,
|
||||
|
@ -27,6 +30,7 @@ Talisman(
|
|||
"'self'",
|
||||
"'unsafe-eval'",
|
||||
"'unsafe-inline'",
|
||||
"blob:",
|
||||
"cdnjs.cloudflare.com",
|
||||
"code.ionicframework.com",
|
||||
"code.jquery.com",
|
||||
|
@ -78,5 +82,5 @@ def init():
|
|||
init_databases()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
|
|
|
@ -63,7 +63,7 @@ def update_user_extension(*, user_id: str, extension: str, active: int) -> None:
|
|||
# -------
|
||||
|
||||
|
||||
def create_wallet(*, user_id: str, wallet_name: Optional[str]) -> Wallet:
|
||||
def create_wallet(*, user_id: str, wallet_name: Optional[str] = None) -> Wallet:
|
||||
with open_db() as db:
|
||||
wallet_id = uuid4().hex
|
||||
db.execute(
|
||||
|
|
|
@ -9,6 +9,13 @@ new Vue({
|
|||
methods: {
|
||||
createWallet: function () {
|
||||
LNbits.href.createWallet(this.walletName);
|
||||
},
|
||||
processing: function () {
|
||||
this.$q.notify({
|
||||
timeout: 0,
|
||||
message: 'Processing...',
|
||||
icon: null
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
methods: {
|
||||
notify: function () {
|
||||
this.$q.notify({
|
||||
timeout: 0,
|
||||
message: 'Processing...',
|
||||
icon: null
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,4 +1,5 @@
|
|||
Vue.component(VueQrcode.name, VueQrcode);
|
||||
Vue.use(VueQrcodeReader);
|
||||
|
||||
|
||||
function generateChart(canvas, payments) {
|
||||
|
@ -119,6 +120,10 @@ new Vue({
|
|||
bolt11: ''
|
||||
}
|
||||
},
|
||||
sendCamera: {
|
||||
show: false,
|
||||
camera: 'auto'
|
||||
},
|
||||
payments: [],
|
||||
paymentsTable: {
|
||||
columns: [
|
||||
|
@ -147,15 +152,26 @@ new Vue({
|
|||
},
|
||||
canPay: function () {
|
||||
if (!this.send.invoice) return false;
|
||||
return this.send.invoice.sat < this.balance;
|
||||
return this.send.invoice.sat <= this.balance;
|
||||
},
|
||||
pendingPaymentsExist: function () {
|
||||
return (this.payments)
|
||||
? _.where(this.payments, {pending: 1}).length > 0
|
||||
: false;
|
||||
},
|
||||
paymentsFiltered: function () {
|
||||
return this.payments.filter(function (obj) {
|
||||
return obj.isPaid;
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeCamera: function () {
|
||||
this.sendCamera.show = false;
|
||||
},
|
||||
showCamera: function () {
|
||||
this.sendCamera.show = true;
|
||||
},
|
||||
showChart: function () {
|
||||
this.paymentsChart.show = true;
|
||||
this.$nextTick(function () {
|
||||
|
@ -180,7 +196,8 @@ new Vue({
|
|||
invoice: null,
|
||||
data: {
|
||||
bolt11: ''
|
||||
}
|
||||
},
|
||||
paymentChecker: null
|
||||
};
|
||||
},
|
||||
closeReceiveDialog: function () {
|
||||
|
@ -189,6 +206,13 @@ new Vue({
|
|||
clearInterval(checker);
|
||||
}, 10000);
|
||||
},
|
||||
closeSendDialog: function () {
|
||||
this.sendCamera.show = false;
|
||||
var checker = this.send.paymentChecker;
|
||||
setTimeout(function () {
|
||||
clearInterval(checker);
|
||||
}, 1000);
|
||||
},
|
||||
createInvoice: function () {
|
||||
var self = this;
|
||||
this.receive.status = 'loading';
|
||||
|
@ -212,6 +236,11 @@ new Vue({
|
|||
self.receive.status = 'pending';
|
||||
});
|
||||
},
|
||||
decodeQR: function (res) {
|
||||
this.send.data.bolt11 = res;
|
||||
this.decodeInvoice();
|
||||
this.sendCamera.show = false;
|
||||
},
|
||||
decodeInvoice: function () {
|
||||
try {
|
||||
var invoice = decode(this.send.data.bolt11);
|
||||
|
@ -259,11 +288,11 @@ new Vue({
|
|||
LNbits.utils.notifyApiError(error);
|
||||
});
|
||||
|
||||
paymentChecker = setInterval(function () {
|
||||
self.send.paymentChecker = setInterval(function () {
|
||||
LNbits.api.getPayment(self.w.wallet, self.send.invoice.hash).then(function (response) {
|
||||
if (response.data.paid) {
|
||||
this.send.show = false;
|
||||
clearInterval(paymentChecker);
|
||||
self.send.show = false;
|
||||
clearInterval(self.send.paymentChecker);
|
||||
dismissPaymentMsg();
|
||||
self.fetchPayments();
|
||||
}
|
||||
|
@ -298,6 +327,8 @@ new Vue({
|
|||
},
|
||||
created: function () {
|
||||
this.fetchPayments();
|
||||
this.checkPendingPayments();
|
||||
setTimeout(function () {
|
||||
this.checkPendingPayments();
|
||||
}, 1100);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,22 +15,29 @@
|
|||
<div class="row q-col-gutter-md justify-between">
|
||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||
|
||||
{% block call_to_action %}
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-form class="q-gutter-md">
|
||||
<q-input filled dense
|
||||
v-model="walletName"
|
||||
label="Name your LNbits wallet *"
|
||||
></q-input>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
{% if lnurl %}
|
||||
<q-btn unelevated
|
||||
color="deep-purple"
|
||||
:disable="walletName == ''"
|
||||
@click="createWallet">Add a new wallet</q-btn>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
{% endblock %}
|
||||
@click="notify"
|
||||
type="a" href="{{ url_for('core.lnurlwallet', lightning=lnurl) }}">
|
||||
Press to claim bitcoin
|
||||
</q-btn>
|
||||
{% else %}
|
||||
<q-form class="q-gutter-md">
|
||||
<q-input filled dense
|
||||
v-model="walletName"
|
||||
label="Name your LNbits wallet *"
|
||||
></q-input>
|
||||
<q-btn unelevated
|
||||
color="deep-purple"
|
||||
:disable="walletName == ''"
|
||||
@click="createWallet">Add a new wallet</q-btn>
|
||||
</q-form>
|
||||
{% endif %}
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
{% extends "core/index.html" %}
|
||||
|
||||
|
||||
{% block scripts %}
|
||||
{% assets filters='rjsmin', output='__bundle__/core/lnurl.js',
|
||||
'core/js/lnurl.js' %}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{% endassets %}
|
||||
{% endblock %}
|
||||
|
||||
{% block call_to_action %}
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-btn unelevated
|
||||
color="deep-purple"
|
||||
@click="notify"
|
||||
type="a" href="{{ url_for('core.lnurlwallet', lightning=lnurl) }}">
|
||||
Press to claim bitcoin
|
||||
</q-btn>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
{% endblock %}
|
|
@ -3,6 +3,10 @@
|
|||
{% from "macros.jinja" import window_vars with context %}
|
||||
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ window_vars(user, wallet) }}
|
||||
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
||||
|
@ -14,6 +18,7 @@
|
|||
'vendor/bolt11/utils.js',
|
||||
'vendor/bolt11/decoder.js',
|
||||
'vendor/vue-qrcode@1.0.2/vue-qrcode.min.js',
|
||||
'vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js',
|
||||
'core/js/wallet.js' %}
|
||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||
{% endassets %}
|
||||
|
@ -59,7 +64,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<q-table dense flat
|
||||
:data="payments"
|
||||
:data="paymentsFiltered"
|
||||
row-key="payhash"
|
||||
:columns="paymentsTable.columns"
|
||||
:pagination.sync="paymentsTable.pagination">
|
||||
|
@ -74,7 +79,7 @@
|
|||
</q-tr>
|
||||
</template>
|
||||
<template v-slot:body="props">
|
||||
<q-tr :props="props" v-if="props.row.isPaid">
|
||||
<q-tr :props="props">
|
||||
<q-td auto-width class="lnbits__q-table__icon-td">
|
||||
<q-icon v-if="props.row.isPaid" size="14px"
|
||||
:name="(props.row.sat < 0) ? 'call_made' : 'call_received'"
|
||||
|
@ -198,7 +203,7 @@
|
|||
<div v-else>
|
||||
<div class="text-center q-mb-md">
|
||||
<a :href="'lightning:' + receive.paymentReq">
|
||||
<qrcode :value="receive.paymentReq" :options="{width: 340}"></qrcode>
|
||||
<qrcode :value="receive.paymentReq" :options="{width: 340}" class="rounded-borders"></qrcode>
|
||||
</a>
|
||||
</div>
|
||||
<!--<q-separator class="q-my-md"></q-separator>
|
||||
|
@ -213,28 +218,38 @@
|
|||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="send.show" position="top">
|
||||
<q-dialog v-model="send.show" position="top" @hide="closeSendDialog">
|
||||
<q-card class="q-pa-lg" style="width: 500px">
|
||||
<q-form v-if="!send.invoice" class="q-gutter-md">
|
||||
<q-input filled dense
|
||||
v-model="send.data.bolt11"
|
||||
type="textarea"
|
||||
label="Paste an invoice *"
|
||||
>
|
||||
<template v-slot:after>
|
||||
<q-btn round dense flat icon="photo_camera">
|
||||
<q-tooltip>Use camera to scan an invoice</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
<div class="row justify-between">
|
||||
<q-btn unelevated
|
||||
color="deep-purple"
|
||||
:disable="send.data.bolt11 == ''"
|
||||
@click="decodeInvoice">Read invoice</q-btn>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||
<div v-if="!send.invoice">
|
||||
<q-form v-if="!sendCamera.show" class="q-gutter-md">
|
||||
<q-input filled dense
|
||||
v-model="send.data.bolt11"
|
||||
type="textarea"
|
||||
label="Paste an invoice *"
|
||||
>
|
||||
<template v-slot:after>
|
||||
<q-btn round dense flat icon="photo_camera" @click="showCamera">
|
||||
<q-tooltip>Use camera to scan an invoice</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
</q-input>
|
||||
<div class="row justify-between">
|
||||
<q-btn unelevated
|
||||
color="deep-purple"
|
||||
:disable="send.data.bolt11 == ''"
|
||||
@click="decodeInvoice">Read invoice</q-btn>
|
||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||
</div>
|
||||
</q-form>
|
||||
<div v-else>
|
||||
<q-responsive :ratio="1">
|
||||
<qrcode-stream @decode="decodeQR" class="rounded-borders"></qrcode-stream>
|
||||
</q-responsive>
|
||||
<div class="row justify-between q-mt-md">
|
||||
<q-btn @click="closeCamera" flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
</q-form>
|
||||
</div>
|
||||
<div v-else>
|
||||
{% raw %}
|
||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
||||
|
|
|
@ -21,7 +21,7 @@ def favicon():
|
|||
|
||||
@core_app.route("/")
|
||||
def home():
|
||||
return render_template("core/index.html")
|
||||
return render_template("core/index.html", lnurl=request.args.get("lightning", None))
|
||||
|
||||
|
||||
@core_app.route("/extensions")
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
import json
|
||||
import requests
|
||||
|
||||
from flask import redirect, render_template, request, url_for
|
||||
from flask import abort, redirect, request, url_for
|
||||
from lnurl import LnurlWithdrawResponse, handle as handle_lnurl
|
||||
from lnurl.exceptions import LnurlException
|
||||
from time import sleep
|
||||
|
||||
from lnbits.core import core_app
|
||||
from lnbits.helpers import Status
|
||||
from lnbits.settings import WALLET
|
||||
|
||||
from ..crud import create_account, get_user, create_wallet, create_payment
|
||||
|
||||
|
||||
@core_app.route("/lnurl")
|
||||
def lnurl():
|
||||
lnurl = request.args.get("lightning")
|
||||
return render_template("core/lnurl.html", lnurl=lnurl)
|
||||
|
||||
|
||||
@core_app.route("/lnurlwallet")
|
||||
def lnurlwallet():
|
||||
try:
|
||||
withdraw_res = handle_lnurl(request.args.get("lightning"), response_class=LnurlWithdrawResponse)
|
||||
except LnurlException:
|
||||
return redirect(url_for("core.home"))
|
||||
abort(Status.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.")
|
||||
|
||||
_, payhash, payment_request = WALLET.create_invoice(withdraw_res.max_sats, "LNbits LNURL funding")
|
||||
|
||||
|
@ -33,8 +27,7 @@ def lnurlwallet():
|
|||
)
|
||||
|
||||
if not r.ok:
|
||||
return redirect(url_for("core.home")) # TODO: custom error
|
||||
data = json.loads(r.text)
|
||||
abort(Status.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.")
|
||||
|
||||
for i in range(10):
|
||||
r = WALLET.get_invoice_status(payhash).raw_response
|
||||
|
@ -46,10 +39,7 @@ def lnurlwallet():
|
|||
user = get_user(create_account().id)
|
||||
wallet = create_wallet(user_id=user.id)
|
||||
create_payment( # TODO: not pending?
|
||||
wallet_id=wallet.id,
|
||||
payhash=payhash,
|
||||
amount=withdraw_res.max_sats * 1000,
|
||||
memo="LNbits lnurl funding",
|
||||
wallet_id=wallet.id, payhash=payhash, amount=withdraw_res.max_sats * 1000, memo="LNbits lnurl funding"
|
||||
)
|
||||
|
||||
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
|
||||
|
|
|
@ -31,3 +31,6 @@
|
|||
.lnbits-drawer__q-list .q-item.q-item--active {
|
||||
color: inherit;
|
||||
font-weight: bold; }
|
||||
|
||||
video {
|
||||
border-radius: 3px; }
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
@ -51,3 +51,9 @@ body.body--dark .q-field--error {
|
|||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// QR video
|
||||
|
||||
video {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
|
1
lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.css
vendored
Normal file
1
lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.wrapper[data-v-1f90552a]{position:relative;z-index:0;width:100%;height:100%}.overlay[data-v-1f90552a],.tracking-layer[data-v-1f90552a]{position:absolute;width:100%;height:100%;top:0;left:0}.camera[data-v-1f90552a],.pause-frame[data-v-1f90552a]{display:block;object-fit:cover;width:100%;height:100%}
|
1
lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js
vendored
Normal file
1
lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Reference in a new issue