take JS out of HTML, fix transactions chart, add apipayments.time.

This commit is contained in:
fiatjaf 2019-12-14 17:26:26 -03:00
parent 14dfa9ecc6
commit 463ee21c9f
8 changed files with 536 additions and 564 deletions

1
.gitignore vendored
View File

@ -24,3 +24,4 @@ venv
database.sqlite3
database.sqlite3*
.pyre*

View File

@ -7,17 +7,12 @@ from flask import Flask, jsonify, render_template, request, redirect, url_for
from . import bolt11
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__)
def db_connect(db_path=DATABASE_PATH):
import sqlite3
con = sqlite3.connect(db_path)
return con
app.jinja_env.filters["megajson"] = megajson
@app.before_first_request
@ -50,7 +45,7 @@ def deletewallet():
(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:
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),
0
) AS balance,
name,
adminkey,
inkey
*
FROM wallets
WHERE user = ? AND id = ?
""",
(usr, wallet_id),
)
transactions = []
transactions = db.fetchall("SELECT * FROM apipayments WHERE wallet = ?", (wallet_id,))
return render_template(
"wallet.html", user_wallets=user_wallets, wallet=wallet, user=usr, transactions=transactions,

View File

@ -18,7 +18,8 @@ CREATE TABLE IF NOT EXISTS apipayments (
fee integer NOT NULL DEFAULT 0,
wallet text 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

View File

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

View File

@ -296,4 +296,9 @@
>
</footer>
</body>
<script
src="{{ url_for('static', filename='app.js') }}"
type="text/javascript"
></script>
</html>

View File

@ -89,23 +89,4 @@
<!-- /.content -->
</div>
<!-- /.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 %}

View File

@ -63,545 +63,171 @@
<div class="row">
<div class="col-md-6">
<!-- small box -->
<a href="#" class="small-box-footer">
<div class="small-box bg-aqua">
<div class="inner">
<h3><b>{{ wallet.balance }} sats</b></h3>
<h3>{{ wallet.name }}</h3>
</div>
<div class="icon">
<i class="ion ion-flash"></i>
</div>
<div class="small-box bg-aqua">
<div class="inner">
<h3><b>{{ wallet.balance }} sats</b></h3>
<h3>{{ wallet.name }}</h3>
</div>
<!-- ./col -->
><!-- /.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 class="icon">
<i class="ion ion-flash"></i>
</div>
</div>
<!-- ./col -->
<div id="receive"></div>
<div id="sendfunds"></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>
<!-- /.row -->
<div class="row">
<div class="col-sm-3">
<button
onclick="sendfundsinput()"
class="btn btn-block btn-primary btn-lg"
>
Send
</button>
</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 class="col-sm-3">
<button
onclick="receive()"
class="btn btn-block btn-primary btn-lg"
>
Receive
</button>
</div>
</div>
</div>
<!-- /.content -->
</a>
<div id="receive"></div>
<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 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>
<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>
<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 %}