mirror of
https://github.com/lnbits/lnbits.git
synced 2025-10-09 20:12:34 +02:00
refactor: camera ready
This commit is contained in:
@@ -3,6 +3,8 @@ import importlib
|
|||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask_assets import Environment, Bundle
|
from flask_assets import Environment, Bundle
|
||||||
from flask_compress import Compress
|
from flask_compress import Compress
|
||||||
|
from flask_limiter import Limiter
|
||||||
|
from flask_limiter.util import get_remote_address
|
||||||
from flask_talisman import Talisman
|
from flask_talisman import Talisman
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
@@ -19,6 +21,7 @@ valid_extensions = [ext for ext in ExtensionManager().extensions if ext.is_valid
|
|||||||
# -----------------------
|
# -----------------------
|
||||||
|
|
||||||
Compress(app)
|
Compress(app)
|
||||||
|
Limiter(app, key_func=get_remote_address, default_limits=["1 per second"])
|
||||||
Talisman(
|
Talisman(
|
||||||
app,
|
app,
|
||||||
force_https=getenv("LNBITS_WITH_ONION", 0) == 0,
|
force_https=getenv("LNBITS_WITH_ONION", 0) == 0,
|
||||||
@@ -27,6 +30,7 @@ Talisman(
|
|||||||
"'self'",
|
"'self'",
|
||||||
"'unsafe-eval'",
|
"'unsafe-eval'",
|
||||||
"'unsafe-inline'",
|
"'unsafe-inline'",
|
||||||
|
"blob:",
|
||||||
"cdnjs.cloudflare.com",
|
"cdnjs.cloudflare.com",
|
||||||
"code.ionicframework.com",
|
"code.ionicframework.com",
|
||||||
"code.jquery.com",
|
"code.jquery.com",
|
||||||
@@ -78,5 +82,5 @@ def init():
|
|||||||
init_databases()
|
init_databases()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run()
|
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:
|
with open_db() as db:
|
||||||
wallet_id = uuid4().hex
|
wallet_id = uuid4().hex
|
||||||
db.execute(
|
db.execute(
|
||||||
|
@@ -9,6 +9,13 @@ new Vue({
|
|||||||
methods: {
|
methods: {
|
||||||
createWallet: function () {
|
createWallet: function () {
|
||||||
LNbits.href.createWallet(this.walletName);
|
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.component(VueQrcode.name, VueQrcode);
|
||||||
|
Vue.use(VueQrcodeReader);
|
||||||
|
|
||||||
|
|
||||||
function generateChart(canvas, payments) {
|
function generateChart(canvas, payments) {
|
||||||
@@ -119,6 +120,10 @@ new Vue({
|
|||||||
bolt11: ''
|
bolt11: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
sendCamera: {
|
||||||
|
show: false,
|
||||||
|
camera: 'auto'
|
||||||
|
},
|
||||||
payments: [],
|
payments: [],
|
||||||
paymentsTable: {
|
paymentsTable: {
|
||||||
columns: [
|
columns: [
|
||||||
@@ -147,15 +152,26 @@ new Vue({
|
|||||||
},
|
},
|
||||||
canPay: function () {
|
canPay: function () {
|
||||||
if (!this.send.invoice) return false;
|
if (!this.send.invoice) return false;
|
||||||
return this.send.invoice.sat < this.balance;
|
return this.send.invoice.sat <= this.balance;
|
||||||
},
|
},
|
||||||
pendingPaymentsExist: function () {
|
pendingPaymentsExist: function () {
|
||||||
return (this.payments)
|
return (this.payments)
|
||||||
? _.where(this.payments, {pending: 1}).length > 0
|
? _.where(this.payments, {pending: 1}).length > 0
|
||||||
: false;
|
: false;
|
||||||
|
},
|
||||||
|
paymentsFiltered: function () {
|
||||||
|
return this.payments.filter(function (obj) {
|
||||||
|
return obj.isPaid;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
closeCamera: function () {
|
||||||
|
this.sendCamera.show = false;
|
||||||
|
},
|
||||||
|
showCamera: function () {
|
||||||
|
this.sendCamera.show = true;
|
||||||
|
},
|
||||||
showChart: function () {
|
showChart: function () {
|
||||||
this.paymentsChart.show = true;
|
this.paymentsChart.show = true;
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
@@ -180,7 +196,8 @@ new Vue({
|
|||||||
invoice: null,
|
invoice: null,
|
||||||
data: {
|
data: {
|
||||||
bolt11: ''
|
bolt11: ''
|
||||||
}
|
},
|
||||||
|
paymentChecker: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
closeReceiveDialog: function () {
|
closeReceiveDialog: function () {
|
||||||
@@ -189,6 +206,13 @@ new Vue({
|
|||||||
clearInterval(checker);
|
clearInterval(checker);
|
||||||
}, 10000);
|
}, 10000);
|
||||||
},
|
},
|
||||||
|
closeSendDialog: function () {
|
||||||
|
this.sendCamera.show = false;
|
||||||
|
var checker = this.send.paymentChecker;
|
||||||
|
setTimeout(function () {
|
||||||
|
clearInterval(checker);
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
createInvoice: function () {
|
createInvoice: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.receive.status = 'loading';
|
this.receive.status = 'loading';
|
||||||
@@ -212,6 +236,11 @@ new Vue({
|
|||||||
self.receive.status = 'pending';
|
self.receive.status = 'pending';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
decodeQR: function (res) {
|
||||||
|
this.send.data.bolt11 = res;
|
||||||
|
this.decodeInvoice();
|
||||||
|
this.sendCamera.show = false;
|
||||||
|
},
|
||||||
decodeInvoice: function () {
|
decodeInvoice: function () {
|
||||||
try {
|
try {
|
||||||
var invoice = decode(this.send.data.bolt11);
|
var invoice = decode(this.send.data.bolt11);
|
||||||
@@ -259,11 +288,11 @@ new Vue({
|
|||||||
LNbits.utils.notifyApiError(error);
|
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) {
|
LNbits.api.getPayment(self.w.wallet, self.send.invoice.hash).then(function (response) {
|
||||||
if (response.data.paid) {
|
if (response.data.paid) {
|
||||||
this.send.show = false;
|
self.send.show = false;
|
||||||
clearInterval(paymentChecker);
|
clearInterval(self.send.paymentChecker);
|
||||||
dismissPaymentMsg();
|
dismissPaymentMsg();
|
||||||
self.fetchPayments();
|
self.fetchPayments();
|
||||||
}
|
}
|
||||||
@@ -298,6 +327,8 @@ new Vue({
|
|||||||
},
|
},
|
||||||
created: function () {
|
created: function () {
|
||||||
this.fetchPayments();
|
this.fetchPayments();
|
||||||
this.checkPendingPayments();
|
setTimeout(function () {
|
||||||
|
this.checkPendingPayments();
|
||||||
|
}, 1100);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -15,22 +15,29 @@
|
|||||||
<div class="row q-col-gutter-md justify-between">
|
<div class="row q-col-gutter-md justify-between">
|
||||||
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
||||||
|
|
||||||
{% block call_to_action %}
|
<q-card>
|
||||||
<q-card>
|
<q-card-section>
|
||||||
<q-card-section>
|
{% if lnurl %}
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<q-input filled dense
|
|
||||||
v-model="walletName"
|
|
||||||
label="Name your LNbits wallet *"
|
|
||||||
></q-input>
|
|
||||||
<q-btn unelevated
|
<q-btn unelevated
|
||||||
color="deep-purple"
|
color="deep-purple"
|
||||||
:disable="walletName == ''"
|
@click="notify"
|
||||||
@click="createWallet">Add a new wallet</q-btn>
|
type="a" href="{{ url_for('core.lnurlwallet', lightning=lnurl) }}">
|
||||||
</q-form>
|
Press to claim bitcoin
|
||||||
</q-card-section>
|
</q-btn>
|
||||||
</q-card>
|
{% else %}
|
||||||
{% endblock %}
|
<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>
|
||||||
<q-card-section>
|
<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 %}
|
{% 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 %}
|
{% block scripts %}
|
||||||
{{ window_vars(user, wallet) }}
|
{{ window_vars(user, wallet) }}
|
||||||
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
{% assets filters='rjsmin', output='__bundle__/core/chart.js',
|
||||||
@@ -14,6 +18,7 @@
|
|||||||
'vendor/bolt11/utils.js',
|
'vendor/bolt11/utils.js',
|
||||||
'vendor/bolt11/decoder.js',
|
'vendor/bolt11/decoder.js',
|
||||||
'vendor/vue-qrcode@1.0.2/vue-qrcode.min.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' %}
|
'core/js/wallet.js' %}
|
||||||
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
|
||||||
{% endassets %}
|
{% endassets %}
|
||||||
@@ -59,7 +64,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-table dense flat
|
<q-table dense flat
|
||||||
:data="payments"
|
:data="paymentsFiltered"
|
||||||
row-key="payhash"
|
row-key="payhash"
|
||||||
:columns="paymentsTable.columns"
|
:columns="paymentsTable.columns"
|
||||||
:pagination.sync="paymentsTable.pagination">
|
:pagination.sync="paymentsTable.pagination">
|
||||||
@@ -74,7 +79,7 @@
|
|||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:body="props">
|
<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-td auto-width class="lnbits__q-table__icon-td">
|
||||||
<q-icon v-if="props.row.isPaid" size="14px"
|
<q-icon v-if="props.row.isPaid" size="14px"
|
||||||
:name="(props.row.sat < 0) ? 'call_made' : 'call_received'"
|
:name="(props.row.sat < 0) ? 'call_made' : 'call_received'"
|
||||||
@@ -198,7 +203,7 @@
|
|||||||
<div v-else>
|
<div v-else>
|
||||||
<div class="text-center q-mb-md">
|
<div class="text-center q-mb-md">
|
||||||
<a :href="'lightning:' + receive.paymentReq">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<!--<q-separator class="q-my-md"></q-separator>
|
<!--<q-separator class="q-my-md"></q-separator>
|
||||||
@@ -213,28 +218,38 @@
|
|||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</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-card class="q-pa-lg" style="width: 500px">
|
||||||
<q-form v-if="!send.invoice" class="q-gutter-md">
|
<div v-if="!send.invoice">
|
||||||
<q-input filled dense
|
<q-form v-if="!sendCamera.show" class="q-gutter-md">
|
||||||
v-model="send.data.bolt11"
|
<q-input filled dense
|
||||||
type="textarea"
|
v-model="send.data.bolt11"
|
||||||
label="Paste an invoice *"
|
type="textarea"
|
||||||
>
|
label="Paste an invoice *"
|
||||||
<template v-slot:after>
|
>
|
||||||
<q-btn round dense flat icon="photo_camera">
|
<template v-slot:after>
|
||||||
<q-tooltip>Use camera to scan an invoice</q-tooltip>
|
<q-btn round dense flat icon="photo_camera" @click="showCamera">
|
||||||
</q-btn>
|
<q-tooltip>Use camera to scan an invoice</q-tooltip>
|
||||||
</template>
|
</q-btn>
|
||||||
</q-input>
|
</template>
|
||||||
<div class="row justify-between">
|
</q-input>
|
||||||
<q-btn unelevated
|
<div class="row justify-between">
|
||||||
color="deep-purple"
|
<q-btn unelevated
|
||||||
:disable="send.data.bolt11 == ''"
|
color="deep-purple"
|
||||||
@click="decodeInvoice">Read invoice</q-btn>
|
:disable="send.data.bolt11 == ''"
|
||||||
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
|
@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>
|
</div>
|
||||||
</q-form>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{% raw %}
|
{% raw %}
|
||||||
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
<h6 class="q-my-none">{{ send.invoice.fsat }} sat</h6>
|
||||||
|
@@ -21,7 +21,7 @@ def favicon():
|
|||||||
|
|
||||||
@core_app.route("/")
|
@core_app.route("/")
|
||||||
def home():
|
def home():
|
||||||
return render_template("core/index.html")
|
return render_template("core/index.html", lnurl=request.args.get("lightning", None))
|
||||||
|
|
||||||
|
|
||||||
@core_app.route("/extensions")
|
@core_app.route("/extensions")
|
||||||
|
@@ -1,29 +1,23 @@
|
|||||||
import json
|
|
||||||
import requests
|
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 import LnurlWithdrawResponse, handle as handle_lnurl
|
||||||
from lnurl.exceptions import LnurlException
|
from lnurl.exceptions import LnurlException
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
from lnbits.core import core_app
|
from lnbits.core import core_app
|
||||||
|
from lnbits.helpers import Status
|
||||||
from lnbits.settings import WALLET
|
from lnbits.settings import WALLET
|
||||||
|
|
||||||
from ..crud import create_account, get_user, create_wallet, create_payment
|
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")
|
@core_app.route("/lnurlwallet")
|
||||||
def lnurlwallet():
|
def lnurlwallet():
|
||||||
try:
|
try:
|
||||||
withdraw_res = handle_lnurl(request.args.get("lightning"), response_class=LnurlWithdrawResponse)
|
withdraw_res = handle_lnurl(request.args.get("lightning"), response_class=LnurlWithdrawResponse)
|
||||||
except LnurlException:
|
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")
|
_, payhash, payment_request = WALLET.create_invoice(withdraw_res.max_sats, "LNbits LNURL funding")
|
||||||
|
|
||||||
@@ -33,8 +27,7 @@ def lnurlwallet():
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not r.ok:
|
if not r.ok:
|
||||||
return redirect(url_for("core.home")) # TODO: custom error
|
abort(Status.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.")
|
||||||
data = json.loads(r.text)
|
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
r = WALLET.get_invoice_status(payhash).raw_response
|
r = WALLET.get_invoice_status(payhash).raw_response
|
||||||
@@ -46,10 +39,7 @@ def lnurlwallet():
|
|||||||
user = get_user(create_account().id)
|
user = get_user(create_account().id)
|
||||||
wallet = create_wallet(user_id=user.id)
|
wallet = create_wallet(user_id=user.id)
|
||||||
create_payment( # TODO: not pending?
|
create_payment( # TODO: not pending?
|
||||||
wallet_id=wallet.id,
|
wallet_id=wallet.id, payhash=payhash, amount=withdraw_res.max_sats * 1000, memo="LNbits lnurl funding"
|
||||||
payhash=payhash,
|
|
||||||
amount=withdraw_res.max_sats * 1000,
|
|
||||||
memo="LNbits lnurl funding",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
|
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 {
|
.lnbits-drawer__q-list .q-item.q-item--active {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-weight: bold; }
|
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;
|
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
Reference in New Issue
Block a user