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
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 . 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,

View File

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

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> </footer>
</body> </body>
<script
src="{{ url_for('static', filename='app.js') }}"
type="text/javascript"
></script>
</html> </html>

View File

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

View File

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