Browse Source

refactor: camera ready

fee_issues
Eneko Illarramendi 5 years ago
parent
commit
75d97ddfc1
  1. 6
      lnbits/__init__.py
  2. 2
      lnbits/core/crud.py
  3. 7
      lnbits/core/static/js/index.js
  4. 13
      lnbits/core/static/js/lnurl.js
  5. 43
      lnbits/core/static/js/wallet.js
  6. 35
      lnbits/core/templates/core/index.html
  7. 22
      lnbits/core/templates/core/lnurl.html
  8. 61
      lnbits/core/templates/core/wallet.html
  9. 2
      lnbits/core/views/generic.py
  10. 20
      lnbits/core/views/lnurl.py
  11. 3
      lnbits/static/css/base.css
  12. BIN
      lnbits/static/images/note.jpg
  13. 6
      lnbits/static/scss/base.scss
  14. 1
      lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.css
  15. 1
      lnbits/static/vendor/vue-qrcode-reader@2.1.1/vue-qrcode-reader.min.js

6
lnbits/__init__.py

@ -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()

2
lnbits/core/crud.py

@ -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(

7
lnbits/core/static/js/index.js

@ -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
});
}
}
});

13
lnbits/core/static/js/lnurl.js

@ -1,13 +0,0 @@
new Vue({
el: '#vue',
mixins: [windowMixin],
methods: {
notify: function () {
this.$q.notify({
timeout: 0,
message: 'Processing...',
icon: null
});
}
}
});

43
lnbits/core/static/js/wallet.js

@ -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);
}
});

35
lnbits/core/templates/core/index.html

@ -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>

22
lnbits/core/templates/core/lnurl.html

@ -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 %}

61
lnbits/core/templates/core/wallet.html

@ -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>

2
lnbits/core/views/generic.py

@ -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")

20
lnbits/core/views/lnurl.py

@ -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))

3
lnbits/static/css/base.css

@ -31,3 +31,6 @@
.lnbits-drawer__q-list .q-item.q-item--active {
color: inherit;
font-weight: bold; }
video {
border-radius: 3px; }

BIN
lnbits/static/images/note.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

6
lnbits/static/scss/base.scss

@ -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

@ -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

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save