mirror of
https://github.com/lnbits/lnbits.git
synced 2025-07-21 18:33:00 +02:00
take JS out of HTML, fix transactions chart, add apipayments.time.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,3 +24,4 @@ venv
|
|||||||
|
|
||||||
database.sqlite3
|
database.sqlite3
|
||||||
database.sqlite3*
|
database.sqlite3*
|
||||||
|
.pyre*
|
||||||
|
@ -7,17 +7,12 @@ from flask import Flask, jsonify, render_template, request, redirect, url_for
|
|||||||
|
|
||||||
from . import bolt11
|
from . import bolt11
|
||||||
from .db import Database
|
from .db import Database
|
||||||
from .settings import DATABASE_PATH, LNBITS_PATH, WALLET, DEFAULT_USER_WALLET_NAME
|
from .helpers import megajson
|
||||||
|
from .settings import LNBITS_PATH, WALLET, DEFAULT_USER_WALLET_NAME
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.jinja_env.filters["megajson"] = megajson
|
||||||
|
|
||||||
def db_connect(db_path=DATABASE_PATH):
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
con = sqlite3.connect(db_path)
|
|
||||||
return con
|
|
||||||
|
|
||||||
|
|
||||||
@app.before_first_request
|
@app.before_first_request
|
||||||
@ -50,7 +45,7 @@ def deletewallet():
|
|||||||
(thewal, theid),
|
(thewal, theid),
|
||||||
)
|
)
|
||||||
|
|
||||||
next_wallet = db.fetchone("SELECT hash FROM wallets WHERE user = ?", (theid,))
|
next_wallet = db.fetchone("SELECT id FROM wallets WHERE user = ?", (theid,))
|
||||||
if next_wallet:
|
if next_wallet:
|
||||||
return redirect(url_for("wallet", usr=theid, wal=next_wallet[0]))
|
return redirect(url_for("wallet", usr=theid, wal=next_wallet[0]))
|
||||||
|
|
||||||
@ -164,16 +159,14 @@ def wallet():
|
|||||||
(SELECT balance/1000 FROM balances WHERE wallet = wallets.id),
|
(SELECT balance/1000 FROM balances WHERE wallet = wallets.id),
|
||||||
0
|
0
|
||||||
) AS balance,
|
) AS balance,
|
||||||
name,
|
*
|
||||||
adminkey,
|
|
||||||
inkey
|
|
||||||
FROM wallets
|
FROM wallets
|
||||||
WHERE user = ? AND id = ?
|
WHERE user = ? AND id = ?
|
||||||
""",
|
""",
|
||||||
(usr, wallet_id),
|
(usr, wallet_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
transactions = []
|
transactions = db.fetchall("SELECT * FROM apipayments WHERE wallet = ?", (wallet_id,))
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"wallet.html", user_wallets=user_wallets, wallet=wallet, user=usr, transactions=transactions,
|
"wallet.html", user_wallets=user_wallets, wallet=wallet, user=usr, transactions=transactions,
|
||||||
|
@ -18,7 +18,8 @@ CREATE TABLE IF NOT EXISTS apipayments (
|
|||||||
fee integer NOT NULL DEFAULT 0,
|
fee integer NOT NULL DEFAULT 0,
|
||||||
wallet text NOT NULL,
|
wallet text NOT NULL,
|
||||||
pending boolean NOT NULL,
|
pending boolean NOT NULL,
|
||||||
memo text
|
memo text,
|
||||||
|
time timestamp NOT NULL DEFAULT (strftime('%s', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE VIEW IF NOT EXISTS balances AS
|
CREATE VIEW IF NOT EXISTS balances AS
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
import json
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
class MegaEncoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if type(o) == sqlite3.Row:
|
||||||
|
val = {}
|
||||||
|
for k in o.keys():
|
||||||
|
val[k] = o[k]
|
||||||
|
return val
|
||||||
|
return o
|
||||||
|
|
||||||
|
|
||||||
|
def megajson(o):
|
||||||
|
return json.dumps(o, cls=MegaEncoder)
|
||||||
|
349
LNbits/static/app.js
Normal file
349
LNbits/static/app.js
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
/** @format */
|
||||||
|
|
||||||
|
const user = window.user
|
||||||
|
const user_wallets = window.user_wallets
|
||||||
|
const wallet = window.wallet
|
||||||
|
const transactions = window.transactions
|
||||||
|
|
||||||
|
var thehash = ''
|
||||||
|
var theinvoice = ''
|
||||||
|
var outamount = ''
|
||||||
|
var outmemo = ''
|
||||||
|
|
||||||
|
// API CALLS
|
||||||
|
|
||||||
|
function postAjax(url, data, thekey, success) {
|
||||||
|
var params =
|
||||||
|
typeof data == 'string'
|
||||||
|
? data
|
||||||
|
: Object.keys(data)
|
||||||
|
.map(function(k) {
|
||||||
|
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
|
||||||
|
})
|
||||||
|
.join('&')
|
||||||
|
var xhr = window.XMLHttpRequest
|
||||||
|
? new XMLHttpRequest()
|
||||||
|
: new ActiveXObject('Microsoft.XMLHTTP')
|
||||||
|
xhr.open('POST', url)
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState > 3 && xhr.status == 200) {
|
||||||
|
success(xhr.responseText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||||
|
xhr.send(params)
|
||||||
|
return xhr
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAjax(url, thekey, success) {
|
||||||
|
var xhr = window.XMLHttpRequest
|
||||||
|
? new XMLHttpRequest()
|
||||||
|
: new ActiveXObject('Microsoft.XMLHTTP')
|
||||||
|
xhr.open('GET', url, true)
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState > 3 && xhr.status == 200) {
|
||||||
|
success(xhr.responseText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||||
|
xhr.send()
|
||||||
|
return xhr
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendfundsinput() {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<br/><br/><div class='row'><div class='col-md-4'>" +
|
||||||
|
"<textarea id='pasteinvoice' class='form-control' rows='3' placeholder='Paste an invoice'></textarea></div></div>" +
|
||||||
|
"<br/><div class='row'><div class='col-md-4'><button type='submit' onclick='sendfundspaste()' class='btn btn-primary'>" +
|
||||||
|
"Submit</button><button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='scanQRsend()'>" +
|
||||||
|
'Use camera to scan an invoice</button></div></div><br/><br/>'
|
||||||
|
document.getElementById('receive').innerHTML = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendfundspaste() {
|
||||||
|
invoice = document.getElementById('pasteinvoice').value
|
||||||
|
theinvoice = decode(invoice)
|
||||||
|
outmemo = theinvoice.data.tags[1].value
|
||||||
|
outamount = Number(theinvoice.human_readable_part.amount) / 1000
|
||||||
|
if (outamount > Number(wallet.balance)) {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<div class='row'><div class='col-md-6'>" +
|
||||||
|
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
|
||||||
|
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
|
||||||
|
'</br/></br/></div></div>'
|
||||||
|
} else {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<div class='row'><div class='col-md-6'>" +
|
||||||
|
'<h3><b>Invoice details</b></br/>Amount: ' +
|
||||||
|
outamount +
|
||||||
|
'<br/>Memo: ' +
|
||||||
|
outmemo +
|
||||||
|
'</h3>' +
|
||||||
|
"<h4 style='word-wrap: break-word;'>" +
|
||||||
|
invoice +
|
||||||
|
'</h4>' +
|
||||||
|
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" +
|
||||||
|
JSON.stringify(invoice) +
|
||||||
|
")'>Send funds</button>" +
|
||||||
|
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
|
||||||
|
'</br/></br/></div></div>'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function receive() {
|
||||||
|
document.getElementById('receive').innerHTML =
|
||||||
|
"<br/><div class='row'><div id='QRCODE'>" +
|
||||||
|
"<div class='col-sm-2'><input type='number' class='form-control' id='amount' placeholder='Amount' max='1000000' required></div>" +
|
||||||
|
"<div class='col-sm-2'><input type='text' class='form-control' id='memo' placeholder='Memo' required></div>" +
|
||||||
|
"<div class='col-sm-2'><input type='button' id='getinvoice' onclick='received()' class='btn btn-primary' value='Create invoice' /></div>" +
|
||||||
|
'</div></div><br/>'
|
||||||
|
document.getElementById('sendfunds').innerHTML = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function received() {
|
||||||
|
memo = document.getElementById('memo').value
|
||||||
|
amount = document.getElementById('amount').value
|
||||||
|
postAjax(
|
||||||
|
'/v1/invoices',
|
||||||
|
JSON.stringify({value: amount, memo: memo}),
|
||||||
|
wallet.inkey,
|
||||||
|
function(data) {
|
||||||
|
theinvoice = JSON.parse(data).pay_req
|
||||||
|
thehash = JSON.parse(data).payment_hash
|
||||||
|
document.getElementById('QRCODE').innerHTML =
|
||||||
|
"<div class='col-md-4'><div class='box'><div class='box-header'>" +
|
||||||
|
"<center><a href='lightning:" +
|
||||||
|
theinvoice +
|
||||||
|
"'><div id='qrcode'></div></a>" +
|
||||||
|
"<p style='word-wrap: break-word;'>" +
|
||||||
|
theinvoice +
|
||||||
|
'</p></div></div></div></center>'
|
||||||
|
|
||||||
|
new QRCode(document.getElementById('qrcode'), {
|
||||||
|
text: theinvoice,
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
colorDark: '#000000',
|
||||||
|
colorLight: '#ffffff',
|
||||||
|
correctLevel: QRCode.CorrectLevel.M
|
||||||
|
})
|
||||||
|
getAjax('/v1/invoice/' + thehash, wallet.inkey, function(datab) {
|
||||||
|
console.log(JSON.parse(datab).PAID)
|
||||||
|
if (JSON.parse(datab).PAID == 'TRUE') {
|
||||||
|
window.location.href = 'wallet?wal=' + wallet.id + '&usr=' + user
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelsend() {
|
||||||
|
window.location.href = 'wallet?wal=' + wallet.id + '&usr=' + user
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendfunds(invoice) {
|
||||||
|
var url = '/v1/channels/transactions'
|
||||||
|
postAjax(
|
||||||
|
url,
|
||||||
|
JSON.stringify({payment_request: invoice}),
|
||||||
|
wallet.adminkey,
|
||||||
|
function(data) {
|
||||||
|
thehash = JSON.parse(data).payment_hash
|
||||||
|
console.log(JSON.parse(data))
|
||||||
|
if (JSON.parse(data).PAID == 'TRUE') {
|
||||||
|
window.location.href = 'wallet?wal=' + wallet.id + '&usr=' + user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanQRsend() {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<br/><br/><div class='row'><div class='col-md-4'>" +
|
||||||
|
"<div id='loadingMessage'>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>" +
|
||||||
|
"<canvas id='canvas' hidden></canvas><div id='output' hidden><div id='outputMessage'></div>" +
|
||||||
|
"<br/><span id='outputData'></span></div></div></div><button type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel</button><br/><br/>"
|
||||||
|
var video = document.createElement('video')
|
||||||
|
var canvasElement = document.getElementById('canvas')
|
||||||
|
var canvas = canvasElement.getContext('2d')
|
||||||
|
var loadingMessage = document.getElementById('loadingMessage')
|
||||||
|
var outputContainer = document.getElementById('output')
|
||||||
|
var outputMessage = document.getElementById('outputMessage')
|
||||||
|
var outputData = document.getElementById('outputData')
|
||||||
|
function drawLine(begin, end, color) {
|
||||||
|
canvas.beginPath()
|
||||||
|
canvas.moveTo(begin.x, begin.y)
|
||||||
|
canvas.lineTo(end.x, end.y)
|
||||||
|
canvas.lineWidth = 4
|
||||||
|
canvas.strokeStyle = color
|
||||||
|
canvas.stroke()
|
||||||
|
}
|
||||||
|
// Use facingMode: environment to attemt to get the front camera on phones
|
||||||
|
navigator.mediaDevices
|
||||||
|
.getUserMedia({video: {facingMode: 'environment'}})
|
||||||
|
.then(function(stream) {
|
||||||
|
video.srcObject = stream
|
||||||
|
video.setAttribute('playsinline', true) // required to tell iOS safari we don't want fullscreen
|
||||||
|
video.play()
|
||||||
|
requestAnimationFrame(tick)
|
||||||
|
})
|
||||||
|
function tick() {
|
||||||
|
loadingMessage.innerText = '⌛ Loading video...'
|
||||||
|
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
||||||
|
loadingMessage.hidden = true
|
||||||
|
canvasElement.hidden = false
|
||||||
|
outputContainer.hidden = false
|
||||||
|
canvasElement.height = video.videoHeight
|
||||||
|
canvasElement.width = video.videoWidth
|
||||||
|
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height)
|
||||||
|
var imageData = canvas.getImageData(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
canvasElement.width,
|
||||||
|
canvasElement.height
|
||||||
|
)
|
||||||
|
var code = jsQR(imageData.data, imageData.width, imageData.height, {
|
||||||
|
inversionAttempts: 'dontInvert'
|
||||||
|
})
|
||||||
|
if (code) {
|
||||||
|
drawLine(
|
||||||
|
code.location.topLeftCorner,
|
||||||
|
code.location.topRightCorner,
|
||||||
|
'#FF3B58'
|
||||||
|
)
|
||||||
|
drawLine(
|
||||||
|
code.location.topRightCorner,
|
||||||
|
code.location.bottomRightCorner,
|
||||||
|
'#FF3B58'
|
||||||
|
)
|
||||||
|
drawLine(
|
||||||
|
code.location.bottomRightCorner,
|
||||||
|
code.location.bottomLeftCorner,
|
||||||
|
'#FF3B58'
|
||||||
|
)
|
||||||
|
drawLine(
|
||||||
|
code.location.bottomLeftCorner,
|
||||||
|
code.location.topLeftCorner,
|
||||||
|
'#FF3B58'
|
||||||
|
)
|
||||||
|
outputMessage.hidden = true
|
||||||
|
outputData.parentElement.hidden = false
|
||||||
|
outputData.innerText = JSON.stringify(code.data)
|
||||||
|
theinvoice = decode(code.data)
|
||||||
|
outmemo = theinvoice.data.tags[1].value
|
||||||
|
outamount = Number(theinvoice.human_readable_part.amount) / 1000
|
||||||
|
if (outamount > Number(wallet.balance)) {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<div class='row'><div class='col-md-6'>" +
|
||||||
|
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
|
||||||
|
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
|
||||||
|
'</br/></br/></div></div>'
|
||||||
|
} else {
|
||||||
|
document.getElementById('sendfunds').innerHTML =
|
||||||
|
"<div class='row'><div class='col-md-6'>" +
|
||||||
|
'<h3><b>Invoice details</b></br/>Amount: ' +
|
||||||
|
outamount +
|
||||||
|
'<br/>Memo: ' +
|
||||||
|
outmemo +
|
||||||
|
'</h3>' +
|
||||||
|
"<h4 style='word-wrap: break-word;'>" +
|
||||||
|
JSON.stringify(code.data) +
|
||||||
|
'</h4>' +
|
||||||
|
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" +
|
||||||
|
JSON.stringify(code.data) +
|
||||||
|
")'>Send funds</button>" +
|
||||||
|
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
|
||||||
|
'</br/></br/></div></div>'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outputMessage.hidden = false
|
||||||
|
outputData.parentElement.hidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestAnimationFrame(tick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deletewallet() {
|
||||||
|
var url = 'deletewallet?wal=' + wallet.id + '&usr=' + user
|
||||||
|
window.location.href = url
|
||||||
|
}
|
||||||
|
|
||||||
|
function sidebarmake() {
|
||||||
|
document.getElementById('sidebarmake').innerHTML =
|
||||||
|
"<li><div class='form-group'>" +
|
||||||
|
"<input style='width:70%;float:left;' type='text' class='form-control' id='walname' placeholder='Name wallet' required>" +
|
||||||
|
"<button style='width:30%;float:left;' type='button' class='btn btn-primary' onclick='newwallet()'>Submit</button>" +
|
||||||
|
'</div></li><br/><br/>'
|
||||||
|
}
|
||||||
|
|
||||||
|
function newwallet() {
|
||||||
|
walname = document.getElementById('walname').value
|
||||||
|
window.location.href = 'wallet?usr=' + user + '&nme=' + walname
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawChart(transactions) {
|
||||||
|
var linechart = []
|
||||||
|
var transactionsHTML = ''
|
||||||
|
var balance = 0
|
||||||
|
|
||||||
|
for (var i = 0; i < transactions.length; i++) {
|
||||||
|
var tx = transactions[i]
|
||||||
|
var datime = convertTimestamp(tx.time)
|
||||||
|
|
||||||
|
// make the transactions table
|
||||||
|
transactionsHTML +=
|
||||||
|
"<tr><td style='width: 50%'>" +
|
||||||
|
tx.memo +
|
||||||
|
'</td><td>' +
|
||||||
|
datime +
|
||||||
|
'</td><td>' +
|
||||||
|
parseFloat(tx.amount / 1000) +
|
||||||
|
'</td></tr>'
|
||||||
|
|
||||||
|
// make the line chart
|
||||||
|
balance += parseInt(tx.amount / 1000)
|
||||||
|
linechart.push({y: datime, balance: balance})
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('transactions').innerHTML = transactionsHTML
|
||||||
|
|
||||||
|
if (linechart[0] != '') {
|
||||||
|
document.getElementById('satschart').innerHTML =
|
||||||
|
"<div class='row'><div class='col-md-6'><div class='box box-info'><div class='box-header'>" +
|
||||||
|
"<h3 class='box-title'>Spending</h3></div><div class='box-body chart-responsive'>" +
|
||||||
|
"<div class='chart' id='line-chart' style='height: 300px;'></div></div></div></div></div>"
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(linechart)
|
||||||
|
var line = new Morris.Line({
|
||||||
|
element: 'line-chart',
|
||||||
|
resize: true,
|
||||||
|
data: linechart,
|
||||||
|
xkey: 'y',
|
||||||
|
ykeys: ['balance'],
|
||||||
|
labels: ['balance'],
|
||||||
|
lineColors: ['#3c8dbc'],
|
||||||
|
hideHover: 'auto'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertTimestamp(timestamp) {
|
||||||
|
var d = new Date(timestamp * 1000),
|
||||||
|
yyyy = d.getFullYear(),
|
||||||
|
mm = ('0' + (d.getMonth() + 1)).slice(-2),
|
||||||
|
dd = ('0' + d.getDate()).slice(-2),
|
||||||
|
hh = d.getHours(),
|
||||||
|
h = hh,
|
||||||
|
min = ('0' + d.getMinutes()).slice(-2),
|
||||||
|
ampm = 'AM',
|
||||||
|
time
|
||||||
|
time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min
|
||||||
|
return time
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactions.length) {
|
||||||
|
drawChart(transactions)
|
||||||
|
}
|
@ -296,4 +296,9 @@
|
|||||||
>
|
>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
<script
|
||||||
|
src="{{ url_for('static', filename='app.js') }}"
|
||||||
|
type="text/javascript"
|
||||||
|
></script>
|
||||||
</html>
|
</html>
|
||||||
|
@ -89,23 +89,4 @@
|
|||||||
<!-- /.content -->
|
<!-- /.content -->
|
||||||
</div>
|
</div>
|
||||||
<!-- /.content-wrapper -->
|
<!-- /.content-wrapper -->
|
||||||
|
|
||||||
<script>
|
|
||||||
function makeid(length) {
|
|
||||||
var result = ''
|
|
||||||
var characters =
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
||||||
var charactersLength = characters.length
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function newwallet() {
|
|
||||||
walname = document.getElementById('walname').value
|
|
||||||
window.location.href =
|
|
||||||
'wallet?usr=' + makeid(40) + '&wal=' + makeid(40) + '&nme=' + walname
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -63,545 +63,171 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<!-- small box -->
|
<!-- small box -->
|
||||||
<a href="#" class="small-box-footer">
|
<div class="small-box bg-aqua">
|
||||||
<div class="small-box bg-aqua">
|
<div class="inner">
|
||||||
<div class="inner">
|
<h3><b>{{ wallet.balance }} sats</b></h3>
|
||||||
<h3><b>{{ wallet.balance }} sats</b></h3>
|
<h3>{{ wallet.name }}</h3>
|
||||||
<h3>{{ wallet.name }}</h3>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="ion ion-flash"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- ./col -->
|
<div class="icon">
|
||||||
|
<i class="ion ion-flash"></i>
|
||||||
><!-- /.row -->
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<button
|
|
||||||
onclick="sendfundsinput()"
|
|
||||||
class="btn btn-block btn-primary btn-lg"
|
|
||||||
>
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-3">
|
|
||||||
<button
|
|
||||||
onclick="receive()"
|
|
||||||
class="btn btn-block btn-primary btn-lg"
|
|
||||||
>
|
|
||||||
Receive
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- ./col -->
|
||||||
|
|
||||||
<div id="receive"></div>
|
<!-- /.row -->
|
||||||
<div id="sendfunds"></div>
|
<div class="row">
|
||||||
|
<div class="col-sm-3">
|
||||||
<div class="row">
|
<button
|
||||||
<div class="col-md-6">
|
onclick="sendfundsinput()"
|
||||||
<div class="box">
|
class="btn btn-block btn-primary btn-lg"
|
||||||
<div class="box-header">
|
>
|
||||||
<h3 class="box-title">Transactions <b id="demo"></b></h3>
|
Send
|
||||||
</div>
|
</button>
|
||||||
<!-- /.box-header -->
|
|
||||||
<div class="box-body no-padding">
|
|
||||||
<table
|
|
||||||
id="pagnation"
|
|
||||||
class="table table-bordered table-striped"
|
|
||||||
>
|
|
||||||
<tr>
|
|
||||||
<th>Memo</th>
|
|
||||||
<th style="width: 20%">date</th>
|
|
||||||
<th style="width: 20%">amount</th>
|
|
||||||
</tr>
|
|
||||||
<tbody id="transactions"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- /.box-body -->
|
|
||||||
</div>
|
|
||||||
<!-- /.box -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
<div id="satschart"></div>
|
<button
|
||||||
|
onclick="receive()"
|
||||||
<div class="row">
|
class="btn btn-block btn-primary btn-lg"
|
||||||
<div class="col-md-6">
|
>
|
||||||
<div class="box box-solid">
|
Receive
|
||||||
<div class="box-header with-border"></div>
|
</button>
|
||||||
<!-- /.box-header -->
|
|
||||||
<div class="box-body">
|
|
||||||
<div class="box-group" id="accordion">
|
|
||||||
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it -->
|
|
||||||
<div class="panel box box-primary">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h4 class="box-title">
|
|
||||||
<a
|
|
||||||
data-toggle="collapse"
|
|
||||||
data-parent="#accordion"
|
|
||||||
href="#collapseThree"
|
|
||||||
class="collapsed"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
Wallet "{{ wallet.name }}" API info
|
|
||||||
</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="collapseThree"
|
|
||||||
class="panel-collapse collapse"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<div class="box-body" style="word-wrap: break-word;">
|
|
||||||
<b>Admin key: </b><i>{{ wallet.adminkey }}</i><br />
|
|
||||||
<b>Invoice/Read key: </b><i>{{ wallet.inkey }}</i
|
|
||||||
><br />
|
|
||||||
Generate an invoice:<br /><code
|
|
||||||
>POST /v1/invoices</code
|
|
||||||
><br />Header
|
|
||||||
<code
|
|
||||||
>{"Grpc-Metadata-macaroon": "<i
|
|
||||||
>{{ wallet.inkey }}</i
|
|
||||||
>"}</code
|
|
||||||
><br />
|
|
||||||
Body <code>{"value": "200","memo": "beer"} </code
|
|
||||||
><br />
|
|
||||||
Returns
|
|
||||||
<code>{"pay_req": string,"pay_id": string} </code
|
|
||||||
><br />
|
|
||||||
*payment will not register in the wallet until the
|
|
||||||
"check invoice" endpoint is used<br /><br />
|
|
||||||
|
|
||||||
Check invoice:<br />
|
|
||||||
Check an invoice:<br /><code
|
|
||||||
>GET /v1/invoice/*payment_hash*</code
|
|
||||||
><br />Header
|
|
||||||
<code
|
|
||||||
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
|
|
||||||
>"}</code
|
|
||||||
><br />
|
|
||||||
|
|
||||||
Returns
|
|
||||||
<code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br />
|
|
||||||
*if using LNTXBOT return will hang until paid<br /><br />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel box box-danger">
|
|
||||||
<div class="box-header with-border">
|
|
||||||
<h4 class="box-title">
|
|
||||||
<a
|
|
||||||
data-toggle="collapse"
|
|
||||||
data-parent="#accordion"
|
|
||||||
href="#collapseTwo"
|
|
||||||
class="collapsed"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
Delete wallet
|
|
||||||
</a>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="collapseTwo"
|
|
||||||
class="panel-collapse collapse"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<div class="box-body">
|
|
||||||
This whole wallet will be deleted, the funds will be
|
|
||||||
UNRECOVERABLE <br /><br /><button
|
|
||||||
class="btn btn-danger"
|
|
||||||
onclick="deletewallet()"
|
|
||||||
>
|
|
||||||
Delete wallet
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /.box-body -->
|
|
||||||
</div>
|
|
||||||
<!-- /.box -->
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- /.content -->
|
<div id="receive"></div>
|
||||||
</a>
|
<div id="sendfunds"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title">Transactions <b id="demo"></b></h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-header -->
|
||||||
|
<div class="box-body no-padding">
|
||||||
|
<table id="pagnation" class="table table-bordered table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>Memo</th>
|
||||||
|
<th style="width: 20%">date</th>
|
||||||
|
<th style="width: 20%">amount</th>
|
||||||
|
</tr>
|
||||||
|
<tbody id="transactions"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.box -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="satschart"></div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="box box-solid">
|
||||||
|
<div class="box-header with-border"></div>
|
||||||
|
<!-- /.box-header -->
|
||||||
|
<div class="box-body">
|
||||||
|
<div class="box-group" id="accordion">
|
||||||
|
<!-- we are adding the .panel class so bootstrap.js collapse plugin detects it -->
|
||||||
|
<div class="panel box box-primary">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h4 class="box-title">
|
||||||
|
<a
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-parent="#accordion"
|
||||||
|
href="#collapseThree"
|
||||||
|
class="collapsed"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
Wallet "{{ wallet.name }}" API info
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="collapseThree"
|
||||||
|
class="panel-collapse collapse"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<div class="box-body" style="word-wrap: break-word;">
|
||||||
|
<b>Admin key: </b><i>{{ wallet.adminkey }}</i><br />
|
||||||
|
<b>Invoice/Read key: </b><i>{{ wallet.inkey }}</i><br />
|
||||||
|
Generate an invoice:<br /><code>POST /v1/invoices</code
|
||||||
|
><br />Header
|
||||||
|
<code
|
||||||
|
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
|
||||||
|
>"}</code
|
||||||
|
><br />
|
||||||
|
Body <code>{"value": "200","memo": "beer"} </code><br />
|
||||||
|
Returns
|
||||||
|
<code>{"pay_req": string,"pay_id": string} </code><br />
|
||||||
|
*payment will not register in the wallet until the "check
|
||||||
|
invoice" endpoint is used<br /><br />
|
||||||
|
|
||||||
|
Check invoice:<br />
|
||||||
|
Check an invoice:<br /><code
|
||||||
|
>GET /v1/invoice/*payment_hash*</code
|
||||||
|
><br />Header
|
||||||
|
<code
|
||||||
|
>{"Grpc-Metadata-macaroon": "<i>{{ wallet.inkey }}</i
|
||||||
|
>"}</code
|
||||||
|
><br />
|
||||||
|
|
||||||
|
Returns
|
||||||
|
<code>{"PAID": "TRUE"}/{"PAID": "FALSE"} </code><br />
|
||||||
|
*if using LNTXBOT return will hang until paid<br /><br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel box box-danger">
|
||||||
|
<div class="box-header with-border">
|
||||||
|
<h4 class="box-title">
|
||||||
|
<a
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-parent="#accordion"
|
||||||
|
href="#collapseTwo"
|
||||||
|
class="collapsed"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
Delete wallet
|
||||||
|
</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
id="collapseTwo"
|
||||||
|
class="panel-collapse collapse"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<div class="box-body">
|
||||||
|
This whole wallet will be deleted, the funds will be
|
||||||
|
UNRECOVERABLE <br /><br /><button
|
||||||
|
class="btn btn-danger"
|
||||||
|
onclick="deletewallet()"
|
||||||
|
>
|
||||||
|
Delete wallet
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.box-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.box -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- /.content -->
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.user = {{ user | megajson | safe }}
|
||||||
|
window.user_wallets = {{ user_wallets | megajson | safe }}
|
||||||
|
window.wallet = {{ wallet | megajson | safe }}
|
||||||
|
window.transactions = {{ transactions | megajson | safe }}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
var inmacaroon = '{{ wallet.inkey }}'
|
|
||||||
var adminmacaroon = '{{ wallet.inkey }}'
|
|
||||||
var thehash = ''
|
|
||||||
var theinvoice = ''
|
|
||||||
var outamount = ''
|
|
||||||
var outmemo = ''
|
|
||||||
|
|
||||||
//API CALLS
|
|
||||||
|
|
||||||
function postAjax(url, data, thekey, success) {
|
|
||||||
var params =
|
|
||||||
typeof data == 'string'
|
|
||||||
? data
|
|
||||||
: Object.keys(data)
|
|
||||||
.map(function(k) {
|
|
||||||
return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
|
|
||||||
})
|
|
||||||
.join('&')
|
|
||||||
var xhr = window.XMLHttpRequest
|
|
||||||
? new XMLHttpRequest()
|
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
|
||||||
xhr.open('POST', url)
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
xhr.send(params)
|
|
||||||
return xhr
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAjax(url, thekey, success) {
|
|
||||||
var xhr = window.XMLHttpRequest
|
|
||||||
? new XMLHttpRequest()
|
|
||||||
: new ActiveXObject('Microsoft.XMLHTTP')
|
|
||||||
xhr.open('GET', url, true)
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if (xhr.readyState > 3 && xhr.status == 200) {
|
|
||||||
success(xhr.responseText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xhr.setRequestHeader('Grpc-Metadata-macaroon', thekey)
|
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json')
|
|
||||||
xhr.send()
|
|
||||||
return xhr
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendfundsinput() {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<br/><br/><div class='row'><div class='col-md-4'>" +
|
|
||||||
"<textarea id='pasteinvoice' class='form-control' rows='3' placeholder='Paste an invoice'></textarea></div></div>" +
|
|
||||||
"<br/><div class='row'><div class='col-md-4'><button type='submit' onclick='sendfundspaste()' class='btn btn-primary'>" +
|
|
||||||
"Submit</button><button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='scanQRsend()'>" +
|
|
||||||
'Use camera to scan an invoice</button></div></div><br/><br/>'
|
|
||||||
document.getElementById('receive').innerHTML = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendfundspaste() {
|
|
||||||
invoice = document.getElementById('pasteinvoice').value
|
|
||||||
theinvoice = decode(invoice)
|
|
||||||
outmemo = theinvoice.data.tags[1].value
|
|
||||||
outamount = Number(theinvoice.human_readable_part.amount) / 1000
|
|
||||||
if (outamount > Number('{{ wallet.balance }}')) {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<div class='row'><div class='col-md-6'>" +
|
|
||||||
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
|
|
||||||
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
|
|
||||||
'</br/></br/></div></div>'
|
|
||||||
} else {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<div class='row'><div class='col-md-6'>" +
|
|
||||||
'<h3><b>Invoice details</b></br/>Amount: ' +
|
|
||||||
outamount +
|
|
||||||
'<br/>Memo: ' +
|
|
||||||
outmemo +
|
|
||||||
'</h3>' +
|
|
||||||
"<h4 style='word-wrap: break-word;'>" +
|
|
||||||
invoice +
|
|
||||||
'</h4>' +
|
|
||||||
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" +
|
|
||||||
JSON.stringify(invoice) +
|
|
||||||
")'>Send funds</button>" +
|
|
||||||
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
|
|
||||||
'</br/></br/></div></div>'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function receive() {
|
|
||||||
document.getElementById('receive').innerHTML =
|
|
||||||
"<br/><div class='row'><div id='QRCODE'>" +
|
|
||||||
"<div class='col-sm-2'><input type='number' class='form-control' id='amount' placeholder='Amount' max='1000000' required></div>" +
|
|
||||||
"<div class='col-sm-2'><input type='text' class='form-control' id='memo' placeholder='Memo' required></div>" +
|
|
||||||
"<div class='col-sm-2'><input type='button' id='getinvoice' onclick='received()' class='btn btn-primary' value='Create invoice' /></div>" +
|
|
||||||
'</div></div><br/>'
|
|
||||||
document.getElementById('sendfunds').innerHTML = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function received() {
|
|
||||||
memo = document.getElementById('memo').value
|
|
||||||
amount = document.getElementById('amount').value
|
|
||||||
postAjax(
|
|
||||||
'/v1/invoices',
|
|
||||||
JSON.stringify({value: amount, memo: memo}),
|
|
||||||
'{{ wallet.inkey }}',
|
|
||||||
function(data) {
|
|
||||||
theinvoice = JSON.parse(data).pay_req
|
|
||||||
thehash = JSON.parse(data).payment_hash
|
|
||||||
document.getElementById('QRCODE').innerHTML =
|
|
||||||
"<div class='col-md-4'><div class='box'><div class='box-header'>" +
|
|
||||||
"<center><a href='lightning:" +
|
|
||||||
theinvoice +
|
|
||||||
"'><div id='qrcode'></div></a>" +
|
|
||||||
"<p style='word-wrap: break-word;'>" +
|
|
||||||
theinvoice +
|
|
||||||
'</p></div></div></div></center>'
|
|
||||||
|
|
||||||
new QRCode(document.getElementById('qrcode'), {
|
|
||||||
text: theinvoice,
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
colorDark: '#000000',
|
|
||||||
colorLight: '#ffffff',
|
|
||||||
correctLevel: QRCode.CorrectLevel.M
|
|
||||||
})
|
|
||||||
getAjax('/v1/invoice/' + thehash, '{{ wallet.inkey }}', function(datab) {
|
|
||||||
console.log(JSON.parse(datab).PAID)
|
|
||||||
if (JSON.parse(datab).PAID == 'TRUE') {
|
|
||||||
window.location.href =
|
|
||||||
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function cancelsend() {
|
|
||||||
window.location.href =
|
|
||||||
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendfunds(invoice) {
|
|
||||||
var url = '/v1/channels/transactions'
|
|
||||||
postAjax(
|
|
||||||
url,
|
|
||||||
JSON.stringify({payment_request: invoice}),
|
|
||||||
'{{ wallet.adminkey }}',
|
|
||||||
function(data) {
|
|
||||||
thehash = JSON.parse(data).payment_hash
|
|
||||||
console.log(JSON.parse(data))
|
|
||||||
if (JSON.parse(data).PAID == 'TRUE') {
|
|
||||||
window.location.href =
|
|
||||||
'wallet?wal=' + '{{ wallet.id }}' + '&usr=' + '{{ user }}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function scanQRsend() {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<br/><br/><div class='row'><div class='col-md-4'>" +
|
|
||||||
"<div id='loadingMessage'>🎥 Unable to access video stream (please make sure you have a webcam enabled)</div>" +
|
|
||||||
"<canvas id='canvas' hidden></canvas><div id='output' hidden><div id='outputMessage'></div>" +
|
|
||||||
"<br/><span id='outputData'></span></div></div></div><button type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel</button><br/><br/>"
|
|
||||||
var video = document.createElement('video')
|
|
||||||
var canvasElement = document.getElementById('canvas')
|
|
||||||
var canvas = canvasElement.getContext('2d')
|
|
||||||
var loadingMessage = document.getElementById('loadingMessage')
|
|
||||||
var outputContainer = document.getElementById('output')
|
|
||||||
var outputMessage = document.getElementById('outputMessage')
|
|
||||||
var outputData = document.getElementById('outputData')
|
|
||||||
function drawLine(begin, end, color) {
|
|
||||||
canvas.beginPath()
|
|
||||||
canvas.moveTo(begin.x, begin.y)
|
|
||||||
canvas.lineTo(end.x, end.y)
|
|
||||||
canvas.lineWidth = 4
|
|
||||||
canvas.strokeStyle = color
|
|
||||||
canvas.stroke()
|
|
||||||
}
|
|
||||||
// Use facingMode: environment to attemt to get the front camera on phones
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getUserMedia({video: {facingMode: 'environment'}})
|
|
||||||
.then(function(stream) {
|
|
||||||
video.srcObject = stream
|
|
||||||
video.setAttribute('playsinline', true) // required to tell iOS safari we don't want fullscreen
|
|
||||||
video.play()
|
|
||||||
requestAnimationFrame(tick)
|
|
||||||
})
|
|
||||||
function tick() {
|
|
||||||
loadingMessage.innerText = '⌛ Loading video...'
|
|
||||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
|
||||||
loadingMessage.hidden = true
|
|
||||||
canvasElement.hidden = false
|
|
||||||
outputContainer.hidden = false
|
|
||||||
canvasElement.height = video.videoHeight
|
|
||||||
canvasElement.width = video.videoWidth
|
|
||||||
canvas.drawImage(video, 0, 0, canvasElement.width, canvasElement.height)
|
|
||||||
var imageData = canvas.getImageData(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
canvasElement.width,
|
|
||||||
canvasElement.height
|
|
||||||
)
|
|
||||||
var code = jsQR(imageData.data, imageData.width, imageData.height, {
|
|
||||||
inversionAttempts: 'dontInvert'
|
|
||||||
})
|
|
||||||
if (code) {
|
|
||||||
drawLine(
|
|
||||||
code.location.topLeftCorner,
|
|
||||||
code.location.topRightCorner,
|
|
||||||
'#FF3B58'
|
|
||||||
)
|
|
||||||
drawLine(
|
|
||||||
code.location.topRightCorner,
|
|
||||||
code.location.bottomRightCorner,
|
|
||||||
'#FF3B58'
|
|
||||||
)
|
|
||||||
drawLine(
|
|
||||||
code.location.bottomRightCorner,
|
|
||||||
code.location.bottomLeftCorner,
|
|
||||||
'#FF3B58'
|
|
||||||
)
|
|
||||||
drawLine(
|
|
||||||
code.location.bottomLeftCorner,
|
|
||||||
code.location.topLeftCorner,
|
|
||||||
'#FF3B58'
|
|
||||||
)
|
|
||||||
outputMessage.hidden = true
|
|
||||||
outputData.parentElement.hidden = false
|
|
||||||
outputData.innerText = JSON.stringify(code.data)
|
|
||||||
theinvoice = decode(code.data)
|
|
||||||
outmemo = theinvoice.data.tags[1].value
|
|
||||||
outamount = Number(theinvoice.human_readable_part.amount) / 1000
|
|
||||||
if (outamount > Number('{{ wallet.balance }}')) {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<div class='row'><div class='col-md-6'>" +
|
|
||||||
"<h3><b style='color:red;'>Not enough funds!</b></h3>" +
|
|
||||||
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Continue</button>" +
|
|
||||||
'</br/></br/></div></div>'
|
|
||||||
} else {
|
|
||||||
document.getElementById('sendfunds').innerHTML =
|
|
||||||
"<div class='row'><div class='col-md-6'>" +
|
|
||||||
'<h3><b>Invoice details</b></br/>Amount: ' +
|
|
||||||
outamount +
|
|
||||||
'<br/>Memo: ' +
|
|
||||||
outmemo +
|
|
||||||
'</h3>' +
|
|
||||||
"<h4 style='word-wrap: break-word;'>" +
|
|
||||||
JSON.stringify(code.data) +
|
|
||||||
'</h4>' +
|
|
||||||
"<button type='submit' class='btn btn-primary' onclick='sendfunds(" +
|
|
||||||
JSON.stringify(code.data) +
|
|
||||||
")'>Send funds</button>" +
|
|
||||||
"<button style='margin-left:20px;' type='submit' class='btn btn-primary' onclick='cancelsend()'>Cancel payment</button>" +
|
|
||||||
'</br/></br/></div></div>'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outputMessage.hidden = false
|
|
||||||
outputData.parentElement.hidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
requestAnimationFrame(tick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deletewallet() {
|
|
||||||
var urll = 'deletewallet?wal={{ wallet.id }}&usr={{ user }}'
|
|
||||||
window.location.href = urll
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebarmake() {
|
|
||||||
document.getElementById('sidebarmake').innerHTML =
|
|
||||||
"<li><div class='form-group'>" +
|
|
||||||
"<input style='width:70%;float:left;' type='text' class='form-control' id='walname' placeholder='Name wallet' required>" +
|
|
||||||
"<button style='width:30%;float:left;' type='button' class='btn btn-primary' onclick='newwallet()'>Submit</button>" +
|
|
||||||
'</div></li><br/><br/>'
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeid(length) {
|
|
||||||
var result = ''
|
|
||||||
var characters =
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
||||||
var charactersLength = characters.length
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
result += characters.charAt(Math.floor(Math.random() * charactersLength))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
function newwallet() {
|
|
||||||
walname = document.getElementById('walname').value
|
|
||||||
window.location.href =
|
|
||||||
'wallet?usr=' + '{{ user }}' + '&wal=' + makeid(40) + '&nme=' + walname
|
|
||||||
}
|
|
||||||
|
|
||||||
var trans = '{{ transactions }}'
|
|
||||||
var transac = trans.substr(1)
|
|
||||||
|
|
||||||
//console.log(trans);
|
|
||||||
|
|
||||||
if (transac != '') {
|
|
||||||
var transactions = ''
|
|
||||||
var linechart = []
|
|
||||||
var tran = transac.split('!')
|
|
||||||
tran.shift()
|
|
||||||
console.log(tran)
|
|
||||||
for (var i = 0; i < tran.length; i++) {
|
|
||||||
rects = tran[i].split(',')
|
|
||||||
rectstime = String(rects[1]).split('.')
|
|
||||||
var datime = convertTimestamp(rectstime[0])
|
|
||||||
|
|
||||||
//Make the transactions table
|
|
||||||
|
|
||||||
transactions +=
|
|
||||||
"<tr><td style='width: 50%'>" +
|
|
||||||
rects[0] +
|
|
||||||
'</td><td>' +
|
|
||||||
datime +
|
|
||||||
'</td><td>' +
|
|
||||||
rects[2] +
|
|
||||||
'</td></tr>'
|
|
||||||
|
|
||||||
//Make the line chart
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
linechart.push({y: datime, item1: rects[2]})
|
|
||||||
} else {
|
|
||||||
linechart.push({y: datime, item1: rects[3]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.getElementById('transactions').innerHTML = transactions
|
|
||||||
if (linechart[0] != '') {
|
|
||||||
document.getElementById('satschart').innerHTML =
|
|
||||||
"<div class='row'><div class='col-md-6'><div class='box box-info'><div class='box-header'>" +
|
|
||||||
"<h3 class='box-title'>Spending</h3></div><div class='box-body chart-responsive'>" +
|
|
||||||
"<div class='chart' id='line-chart' style='height: 300px;'></div></div></div></div></div>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(linechart)
|
|
||||||
var data = linechart
|
|
||||||
var line = new Morris.Line({
|
|
||||||
element: 'line-chart',
|
|
||||||
resize: true,
|
|
||||||
data,
|
|
||||||
xkey: 'y',
|
|
||||||
ykeys: ['item1'],
|
|
||||||
labels: ['Item 1'],
|
|
||||||
lineColors: ['#3c8dbc'],
|
|
||||||
hideHover: 'auto'
|
|
||||||
})
|
|
||||||
|
|
||||||
function convertTimestamp(timestamp) {
|
|
||||||
var d = new Date(timestamp * 1000),
|
|
||||||
yyyy = d.getFullYear(),
|
|
||||||
mm = ('0' + (d.getMonth() + 1)).slice(-2),
|
|
||||||
dd = ('0' + d.getDate()).slice(-2),
|
|
||||||
hh = d.getHours(),
|
|
||||||
h = hh,
|
|
||||||
min = ('0' + d.getMinutes()).slice(-2),
|
|
||||||
ampm = 'AM',
|
|
||||||
time
|
|
||||||
time = yyyy + '-' + mm + '-' + dd + ' ' + h + ':' + min
|
|
||||||
return time
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Reference in New Issue
Block a user