Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move navbar to the bottom of the page & share it across all pages #381

Open
wants to merge 9 commits into
base: v1.10.0
Choose a base branch
from
88 changes: 53 additions & 35 deletions gui/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<script src="{% static 'helpers.js' %}"></script>
<script>
const base_ch_template = {
"remote_pubkey": ch => ({innerHTML: `<a href="/channel?=${ch.chan_id}" target="_blank">${ch.short_chan_id}</a>`, title: `${ch.funding_txid}:${ch.output_index}`}),
"chan_id": ch => ({innerHTML: `<a href="{{graph_links}}/{{network}}node/${ch.remote_pubkey}" target="_blank">${ch.alias == '' ? ch.remote_pubkey.slice(12) : ch.alias}</a>${ch.notes.length > 0 ? ` <sup href class="w3-round w3-border-small w3-border-grey" title="${ch.notes}">i</sup>` : ''}`, title: ch.remote_pubkey}),
"remote_pubkey": ch => ({innerHTML: `<a href="/channel?=${ch.chan_id}">${ch.short_chan_id}</a>`, title: `${ch.funding_txid}:${ch.output_index}`}),
"chan_id": ch => ({innerHTML: `<a href="{{graph_links}}/{{network}}node/${ch.remote_pubkey}">${ch.alias == '' ? ch.remote_pubkey.slice(12) : ch.alias}</a>${ch.notes.length > 0 ? ` <sup href class="w3-round w3-border-small w3-border-grey" title="${ch.notes}">i</sup>` : ''}`, title: ch.remote_pubkey}),
"local_balance": ch => ({innerHTML: `${ch.local_balance.intcomma()} <small class="w3-round w3-border-small w3-border-grey">${ch.percent_outbound}%</small>`}),
"capacity": ch => ({innerHTML: `<label>${ch.capacity.intcomma()}</label>
<div class="progress w3-round w3-grey">
Expand All @@ -19,14 +19,14 @@
}
const routed_template = {
"forward_date": f => ({innerHTML: formatDate(f.forward_date), title: adjustTZ(f.forward_date) }),
"chan_id_in": f => ({innerHTML: `<a href="/channel?=${f.chan_id_in}" target="_blank">${(BigInt(f.chan_id_in)>>40n)+'x'+(BigInt(f.chan_id_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(f.chan_id_in) & BigInt('0xFFFF'))}</a>`}),
"chan_id_in": f => ({innerHTML: `<a href="/channel?=${f.chan_id_in}">${(BigInt(f.chan_id_in)>>40n)+'x'+(BigInt(f.chan_id_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(f.chan_id_in) & BigInt('0xFFFF'))}</a>`}),
"chan_in_alias": f => ({innerHTML: f.chan_in_alias || f.chan_id_in}),
"amt_in": f => ({innerHTML: f.amt_in > 1000 ? f.amt_in.intcomma() : f.amt_in, style: {paddingRight: "0px", width: "160px"} }),
"symbolIn": f => ({innerHTML: "●━━━", style: {paddingRight: "0px", paddingLeft: "0px", textAlign: 'right'} }),
"symbolOut": f => ({innerHTML: "━━━○", style: {paddingLeft: "0px", paddingRight: "0px", textAlign: 'left'} }),
"amt_out": f => ({innerHTML: (f.amt_out > 1000 ? f.amt_out.intcomma() : f.amt_out) + ` <small class="w3-round w3-border-small w3-border-grey w3-tiny">+${f.fee.intcomma().toLocaleString()}</small>`, style: {paddingLeft: "0px", width: "160px"} }),
"chan_out_alias": f => ({innerHTML: f.chan_out_alias || f.chan_id_out}),
"chan_id_out": f => ({innerHTML: `<a href="/channel?=${f.chan_id_out}" target="_blank">${(BigInt(f.chan_id_out)>>40n)+'x'+(BigInt(f.chan_id_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(f.chan_id_out) & BigInt('0xFFFF'))}</a>`}),
"chan_id_out": f => ({innerHTML: `<a href="/channel?=${f.chan_id_out}">${(BigInt(f.chan_id_out)>>40n)+'x'+(BigInt(f.chan_id_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(f.chan_id_out) & BigInt('0xFFFF'))}</a>`}),
"fee": f => ({innerHTML: parseFloat(f.fee.toFixed(3)).toLocaleString()}),
"ppm": f => ({innerHTML: parseInt(f.ppm.toFixed(0)).toLocaleString()}),
}
Expand All @@ -38,8 +38,8 @@
}, {ppm}, {
"status": p => ({innerHTML: ['Unknown', 'In-Flight', 'Succeeded', 'Failed'][p.status]}),
"chan_out_alias": p => ({innerHTML: p.chan_out_alias||'---'}),
"chan_out": p => ({innerHTML: p.status == 2 && p.chan_out !== 'MPP' ? `<a href="/channel?=${p.chan_out}" target="_blank">${(BigInt(p.chan_out)>>40n)+'x'+(BigInt(p.chan_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_out) & BigInt('0xFFFF'))}` : p.chan_out || '---' }),
"payment_hash": p => ({innerHTML: `<a href="/route?=${p.payment_hash}" target="_blank">${p.payment_hash.substring(0,7)}</a>`}),
"chan_out": p => ({innerHTML: p.status == 2 && p.chan_out !== 'MPP' ? `<a href="/channel?=${p.chan_out}">${(BigInt(p.chan_out)>>40n)+'x'+(BigInt(p.chan_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_out) & BigInt('0xFFFF'))}` : p.chan_out || '---' }),
"payment_hash": p => ({innerHTML: `<a href="/route?=${p.payment_hash}">${p.payment_hash.substring(0,7)}</a>`}),
"rebal_chan": p => ({innerHTML: (p.rebal_chan || '').length > 0 ? 'Yes' : 'No'}),
"keysend_preimage": p => ({innerHTML: p.keysend_preimage ? 'Yes' : 'No'})
}
Expand All @@ -51,31 +51,31 @@
"amt_paid": i => ({innerHTML: i.amt_paid.intcomma()}),
"state": i => ({innerHTML: ['Open', 'Settled', 'Cancelled', 'Accepted'][i.state]}),
"chan_in_alias": i => ({innerHTML: i.chan_in_alias||'---'}),
"chan_in": i => ({innerHTML: i.chan_in? `<a href="/channel?=${i.chan_in}" target="_blank">${(BigInt(i.chan_in)>>40n)+'x'+(BigInt(i.chan_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(i.chan_in) & BigInt('0xFFFF'))}</a>` : '---'}),
"r_hash": i => ({innerHTML: `<a href="/route?=${i.r_hash}" target="_blank">${i.r_hash.substring(0,7)}</a>`}),
"chan_in": i => ({innerHTML: i.chan_in? `<a href="/channel?=${i.chan_in}">${(BigInt(i.chan_in)>>40n)+'x'+(BigInt(i.chan_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(i.chan_in) & BigInt('0xFFFF'))}</a>` : '---'}),
"r_hash": i => ({innerHTML: `<a href="/route?=${i.r_hash}">${i.r_hash.substring(0,7)}</a>`}),
"keysend_preimage": i => ({innerHTML: keysend_preimage(i).innerHTML, title: i.message }),
}
)
const wire_failures = "0|1|2|3|4|5|6|Expiry Too Soon|8|9|10|Amount Below Minimum|Fee Insufficient|13|14|Temporary Channel Failure|16|17|Unknown Next Peer|19|20|21|Expiry Too Far".split('|')
const failure_details = "0|---|2|Link Not Eligible|4|HTLC Exceeds Max|Insufficient Balance|7|8|9|10|11|12|Invoice Not Open|14|15|16|17|18|19|Invalid Keysend|21|Circular Route".split('|')
const failedHTLCs_template = {
"timestamp": htlc => ({innerHTML: formatDate(htlc.timestamp), title: adjustTZ(htlc.timestamp) }),
"chan_id_in": htlc => ({innerHTML: `<a href="/channel?=${htlc.chan_id_in}" target="_blank">${(BigInt(htlc.chan_id_in)>>40n)+'x'+(BigInt(htlc.chan_id_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(htlc.chan_id_in) & BigInt('0xFFFF'))}</a>`}),
"chan_in_alias": htlc => ({innerHTML: `<a href="/failed_htlcs?=${htlc.chan_id_in}_I" target="_blank">${htlc.chan_in_alias || htlc.chan_id_in}</a>`}),
"chan_id_in": htlc => ({innerHTML: `<a href="/channel?=${htlc.chan_id_in}">${(BigInt(htlc.chan_id_in)>>40n)+'x'+(BigInt(htlc.chan_id_in)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(htlc.chan_id_in) & BigInt('0xFFFF'))}</a>`}),
"chan_in_alias": htlc => ({innerHTML: `<a href="/failed_htlcs?=${htlc.chan_id_in}_I">${htlc.chan_in_alias || htlc.chan_id_in}</a>`}),
"fw_amount": htlc => ({innerHTML: htlc.amount.intcomma() + ` <small title="Missed fee" class="w3-round w3-border-small w3-border-grey w3-tiny w3-text-red">+${htlc.missed_fee.intcomma()}</small>`, style: {paddingRight: "4px", width: "160px"} }),
"symbolIn" : _ => ({innerHTML: "●━━━", style: {paddingRight: "0px", paddingLeft: "0px", textAlign: 'right'} }),
"symbolOut" : _ => ({innerHTML: "━━━▏", style: {paddingLeft: "0px", paddingRight: "0px", textAlign: 'left'} }),
"chan_out_liq": htlc => ({innerHTML: `${(htlc.chan_out_liq || 0).intcomma()} <small title="Pending at HTLC time" class="w3-round w3-border-small w3-border-grey">${htlc.chan_out_pending}</small>`, style: {paddingLeft: "0px", width: "160px"} }),
"chan_out_alias": htlc => ({innerHTML: `<a href="/failed_htlcs?=${htlc.chan_id_out}_O" target="_blank">${htlc.chan_out_alias || htlc.chan_id_out}</a>`}),
"chan_id_out": htlc => ({innerHTML: `<a href="/channel?=${htlc.chan_id_out}" target="_blank">${(BigInt(htlc.chan_id_out)>>40n)+'x'+(BigInt(htlc.chan_id_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(htlc.chan_id_out) & BigInt('0xFFFF'))}</a>`}),
"chan_out_alias": htlc => ({innerHTML: `<a href="/failed_htlcs?=${htlc.chan_id_out}_O">${htlc.chan_out_alias || htlc.chan_id_out}</a>`}),
"chan_id_out": htlc => ({innerHTML: `<a href="/channel?=${htlc.chan_id_out}">${(BigInt(htlc.chan_id_out)>>40n)+'x'+(BigInt(htlc.chan_id_out)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(htlc.chan_id_out) & BigInt('0xFFFF'))}</a>`}),
"missed_fee": htlc => ({innerHTML: parseFloat(htlc.missed_fee.toFixed(3)).toLocaleString()}),
"wire_failure": htlc => ({innerHTML: htlc.wire_failure > wire_failures.length ? htlc.wire_failure : `${wire_failures[htlc.wire_failure]}${htlc.wire_failure===12?` (${Math.ceil(htlc.missed_fee/htlc.amount*1000000)})`:''}`}),
"failure_detail": htlc => ({innerHTML: htlc.failure_detail > failure_details.length ? htlc.failure_detail : failure_details[htlc.failure_detail]}),
}
const peer_events_template = {
"timestamp": p => ({innerHTML: formatDate(p.timestamp), title: adjustTZ(p.timestamp) }),
"chan_id": p => ({innerHTML: `<a href="/channel?=${p.chan_id}" target="_blank">${(BigInt(p.chan_id)>>40n)+'x'+(BigInt(p.chan_id)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_id) & BigInt('0xFFFF'))}</a>`}),
"peer_alias": p => ({innerHTML: `<a href="/peerevents?=${p.chan_id}" target="_blank">${p.peer_alias || p.chan_id}</a>`}),
"chan_id": p => ({innerHTML: `<a href="/channel?=${p.chan_id}">${(BigInt(p.chan_id)>>40n)+'x'+(BigInt(p.chan_id)>>16n & BigInt('0xFFFFFF'))+'x'+(BigInt(p.chan_id) & BigInt('0xFFFF'))}</a>`}),
"peer_alias": p => ({innerHTML: `<a href="/peerevents?=${p.chan_id}">${p.peer_alias || p.chan_id}</a>`}),
"event": p => ({innerHTML: p.event}),
"old_value": p => ({innerHTML: p.event == 'Connection' & p.old_value == 1 ? 'Online' : p.event == 'Connection' & p.old_value == 0 ? 'Offline' : p.event == 'Disabled' & p.old_value == 1 ? 'True' : p.event == 'Disabled' & p.old_value == 0 ? 'False' : p.old_value == null ? '---' : p.old_value }),
"new_value": p => ({innerHTML: p.event == 'Connection' & p.new_value == 1 ? 'Online' : p.event == 'Connection' & p.new_value == 0 ? 'Offline' : p.event == 'Disabled' & p.new_value == 1 ? 'True' : p.event == 'Disabled' & p.new_value == 0 ? 'False' : p.event == 'MinHTLC' | p.event == 'MaxHTLC' ? (p.new_value/1000).intcomma() : p.new_value.intcomma(), style: {backgroundColor: p.event == 'Disabled' & p.new_value == 0 ? 'rgba(46,160,67,0.15)' : p.event == 'Connection' & p.new_value == 1 ? 'rgba(46,160,67,0.15)' : p.event == 'Disabled' & p.new_value == 1 ? 'rgba(248,81,73,0.15)' : p.new_value > p.old_value & p.old_value != null ? 'rgba(46,160,67,0.15)' : 'rgba(248,81,73,0.15)'} }),
Expand All @@ -102,34 +102,52 @@ <h1 style="word-wrap:break-word">{{ message.message }}</h1>
{% endfor %}
</div>
{% endif %}
<div class="w3-container">
<h1><a href="/">My Lnd Overview</a></h1>
</div>
{% block content %}{% endblock %}
</div>
</body>
<footer>
<div id="footer">
<div class="w3-container w3-padding-small">
<center><button id="toggleTheme" onclick="toggleTheme()">{% if request.COOKIES.darkmode == 'true' %}Light{%else%}Dark{% endif %} Theme</button></center>
</div>
<div class="w3-container w3-padding-small">
<center>LNDg v1.9.0</center>
<center><a href="{% url 'logout' %}">Logout</a></center>
</div>
<footer id="footer">
<div class="w3-container w3-padding-small" style="margin-bottom:50px">
<center>LNDg v1.8.0</center>
</div>
<div class="w3-bar w3-black" style="position:fixed;bottom:0px;width:100%;z-index:100">
<a class="w3-bar-item w3-hover-blue" style="padding:0" title="Dashboard" href="/"><img height="38px" src="/favicon.ico"></img></a>
<a class="w3-bar-item w3-hover-blue" href="/income">P&L</a>
<a class="w3-bar-item w3-hover-blue" href="/closures">Closures</a>
<a class="w3-bar-item w3-hover-blue" href="/opens">New Peers</a>
<a class="w3-bar-item w3-hover-blue" href="/peerevents">Peer Events</a>
<a class="w3-bar-item w3-hover-blue" href="/actions">AR Actions</a>
<a class="w3-bar-item w3-hover-blue" href="/fees">Fee Rates</a>
<a class="w3-bar-item w3-hover-blue" href="/autopilot">Autopilot</a>
<a class="w3-bar-item w3-hover-blue" href="/autofees">Fee Log</a>
<a class="w3-bar-item w3-hover-blue" href="/channels">Channel Performance</a>
<a class="w3-bar-item w3-hover-blue" href="/keysends">Keysends</a>
<a class="w3-bar-item w3-hover-blue" href="/rebalancing">Rebalancing</a>
<a class="w3-bar-item w3-hover-blue" href="/towers">Towers</a>
<a class="w3-bar-item w3-hover-blue" href="/batch">Batching</a>
<a class="w3-bar-item w3-hover-blue" href="/trades">Trades</a>
<a class="w3-bar-item w3-hover-blue" href="/advanced">Advanced Settings</a>
<a class="w3-bar-item w3-hover-blue" href="/logs/?tail=20">Logs</a>
<span class="w3-padding-small w3-right" style="max-height:30px;">
<a class="w3-bar-item w3-hover w3-text-white" title="Last Update" id="refresh_stats" style="padding:8px 0px"></a>
<a class="w3-bar-item w3-hover w3-btn" title="Toggle Theme" id="themeToggler" style="padding:8px">{% if request.COOKIES.darkmode == 'true' %}☀️{%else%}🌙{% endif %}</a>
<a class="w3-bar-item w3-hover w3-text-red" title="Logout" href="{% url 'logout' %}" style="padding:8px">&#9211;</a>
</span>
</div>
</footer>
<script>
var darkMode = {% if request.COOKIES.darkmode == 'true' %}true{%else%}false{% endif %}
//BASE CONFIG
function toggleTheme() {
var element = document.body;
element.classList.toggle("dark-mode")
darkMode = element.classList.contains("dark-mode")
document.cookie = `darkmode=${darkMode};max-age=34560000`
byId('toggleTheme').innerHTML = `${darkMode ? 'Light' : 'Dark'} Theme`
function toggleTheme(ev) {
const el = ev.target
document.body.classList.toggle("dark-mode")
darkMode = document.body.classList.contains("dark-mode")
document.cookie = `darkmode=${darkMode};max-age=34560000;samesite=strict`
el.innerHTML = darkMode ? '☀️' : '🌙'
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => e.matches ^ darkMode ? toggleTheme() : '')
byId('themeToggler').addEventListener('click', toggleTheme)
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => e.matches ^ darkMode ? toggleTheme({target: byId('themeToggler')}) : '')


document.querySelectorAll(".table").forEach(table =>
table.addEventListener("scroll", function (evt) {
const el = evt.currentTarget, sT = el.scrollTop;
Expand Down Expand Up @@ -159,8 +177,8 @@ <h1><a href="/">My Lnd Overview</a></h1>
await sleep(100)
}
await async_callback()
}
}
//END: BASE CONFIG
//-------------------------------------------------------------------------------------------------------------------------
</script>
</html>
</html>
37 changes: 5 additions & 32 deletions gui/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
{% block title %} {{ block.super }} - Dashboard{% endblock %}
{% block content %}
{% load humanize %}
<span style="position: absolute;top: 0;right: 0;padding: 5px;">Dashboard Last Updated: <span id='datetime'></span> <label title="Auto Page Refresh" class="switch"><input type="checkbox" {% if request.COOKIES.refresh != 'false' %}checked="true"{% endif %}><span onclick="toggleAutoRefresh()" class="slider round"></span></label></span>
<div class="w3-container w3-padding-small">
<script language="Javascript">
function qr_gen(addr){
Expand All @@ -16,13 +15,6 @@
function qr_addr(addr){
var qrcode = new QRCode(byId(addr), {text:addr, width:256, height:256})
}
function toggleAutoRefresh() {
if(document.cookie.includes('refresh=false')){
document.cookie = `refresh=true;max-age=34560000`
}else{
document.cookie = `refresh=false;max-age=34560000`
}
}
</script>
<h3>
<span style="font-size: 12px;vertical-align: middle; padding-left: 18px; border:1px solid {{ node_info.color }}; background-color: {{ node_info.color }}" class="w3-circle" title="{{ node_info.color }}"></span>
Expand Down Expand Up @@ -135,25 +127,6 @@ <h6 style="display: none" id="private_stats">
</tr>
</table>
</div>
<br/>
<div class="w3-bar w3-border-top" style="background-color: transparent">
<a class="w3-bar-item w3-hover-blue" href="/income" target="_blank">P&L</a>
<a class="w3-bar-item w3-hover-blue" href="/closures" target="_blank">Closures</a>
<a class="w3-bar-item w3-hover-blue" href="/opens" target="_blank">New Peers</a>
<a class="w3-bar-item w3-hover-blue" href="/peerevents" target="_blank">Peer Events</a>
<a class="w3-bar-item w3-hover-blue" href="/actions" target="_blank">AR Actions</a>
<a class="w3-bar-item w3-hover-blue" href="/fees" target="_blank">Fee Rates</a>
<a class="w3-bar-item w3-hover-blue" href="/autopilot" target="_blank">Autopilot</a>
<a class="w3-bar-item w3-hover-blue" href="/autofees" target="_blank">Fee Log</a>
<a class="w3-bar-item w3-hover-blue" href="/channels" target="_blank">Channel Performance</a>
<a class="w3-bar-item w3-hover-blue" href="/keysends" target="_blank">Keysends</a>
<a class="w3-bar-item w3-hover-blue" href="/rebalancing" target="_blank">Rebalancing</a>
<a class="w3-bar-item w3-hover-blue" href="/towers" target="_blank">Towers</a>
<a class="w3-bar-item w3-hover-blue" href="/batch" target="_blank">Batching</a>
<a class="w3-bar-item w3-hover-blue" href="/trades" target="_blank">Trades</a>
<a class="w3-bar-item w3-hover-blue" href="/advanced" target="_blank">Advanced Settings</a>
<a class="w3-bar-item w3-hover-blue" href="/logs/?tail=20" target="_blank">Logs</a>
</div>
<div id="active_channels_container" style="display:none" class="w3-container w3-padding-small">
<div class="w3-container w3-padding-small table" style="overflow:auto;max-height:1000px">
<table class="w3-table-all w3-centered w3-hoverable">
Expand Down Expand Up @@ -936,6 +909,8 @@ <h2>Sign a Message</h2>
channels.forEach(ch => table.append(template.render(ch)))
}
async function buildMain() {
const refreshStats = byId('refresh_stats')
refreshStats.innerHTML = "Updating..."
const node_info = await GET('node_info', {data: {limit:1}})
buildMainStats(node_info)
const inv_rev_task = GET('invoices', {data: {state__lt: 2, is_revenue: true, settle_date__gte: _7days_ago}})
Expand Down Expand Up @@ -979,12 +954,10 @@ <h2>Sign a Message</h2>
build_payments(payments7d)
build_invoices((await invoices_task).results)
build_failedHTLC((await failedHTLCs_task).results)
byId("datetime").innerHTML = new Date().toLocaleTimeString();
const refresh = document.cookie.includes("refresh=true")
refreshStats.innerHTML = `${new Date().toLocaleTimeString()} <label title="Auto Page Refresh" class="switch"><input type="checkbox" ${refresh?'checked':''}><span onclick="document.cookie='refresh=${!refresh};max-age=34560000;samesite=strict'" class="slider round"></span></label>`;
await auto_refresh(buildMain)
}
document.addEventListener('DOMContentLoaded', async () => {
const builder = buildMain()
await builder
})
document.addEventListener('DOMContentLoaded', async () => await buildMain())
</script>
{% endblock %}