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

Add auto-refresh option #412

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ expressApp.use(function(req, res, next) {
userSettings.localCurrency = (userSettings.localCurrency || config.displayDefaults.localCurrency);
userSettings.uiTimezone = (userSettings.uiTimezone || config.displayDefaults.timezone);
userSettings.uiTheme = (userSettings.uiTheme || config.displayDefaults.theme);
userSettings.autoRefresh = (userSettings.autoRefresh || config.displayDefaults.autoRefresh);


// make available in templates
Expand All @@ -778,6 +779,7 @@ expressApp.use(function(req, res, next) {
res.locals.uiTheme = userSettings.uiTheme;
res.locals.userTzOffset = userSettings.userTzOffset || "unset";
res.locals.browserTzOffset = userSettings.browserTzOffset || "0";
res.locals.autoRefresh = userSettings.autoRefresh;


if (!["/", "/connect"].includes(req.originalUrl)) {
Expand Down
3 changes: 2 additions & 1 deletion app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ module.exports = {
displayCurrency: (process.env.BTCEXP_DISPLAY_CURRENCY || "btc"),
localCurrency: (process.env.BTCEXP_LOCAL_CURRENCY || "usd"),
theme: (process.env.BTCEXP_UI_THEME || "dark"),
timezone: (process.env.BTCEXP_UI_TIMEZONE || "local")
timezone: (process.env.BTCEXP_UI_TIMEZONE || "local"),
autoRefresh: (process.env.BTCEXP_AUTO_REFRESH || "off")
},

cookieSecret: cookieSecret,
Expand Down
9 changes: 7 additions & 2 deletions routes/baseRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ router.get("/", asyncHandler(async (req, res, next) => {


await utils.timePromise("homepage.render", async () => {
res.render("index");
res.render("index", {"currentBlockHeight":currentBlock.height, "periodicRefresh":true});
}, perfResults);

next();
Expand Down Expand Up @@ -629,7 +629,12 @@ router.get("/blocks", asyncHandler(async (req, res, next) => {
await utils.awaitPromises(promises);

await utils.timePromise("blocks.render", async () => {
res.render("blocks");
// If this page shows the latest block at the top, provide the current block height to tell the page
// to auto-refresh (if enabled) when a new block arrives.
if (offset == 0 && sort == "desc")
res.render("blocks", {"currentBlockHeight":getblockchaininfo.blocks});
else
res.render("blocks");
}, perfResults);

next();
Expand Down
3 changes: 3 additions & 0 deletions views/blocks.pug
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ block content
a.page-link(href=(sort == "asc" ? "javascript:void(0)" : `./blocks?limit=${limit}&offset=0&sort=asc`))
span(aria-hidden="true") Oldest blocks first

if (autoRefresh == "on")
include includes/connection-warning.pug

if (blocks)
+contentSection
include includes/blocks-list.pug
Expand Down
139 changes: 132 additions & 7 deletions views/includes/blocks-list.pug
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@
a(href=`./block-height/${block.height}`) #{block.height.toLocaleString()}


- var timeAgoTime = moment.utc(new Date()).diff(moment.utc(new Date(parseInt(block.time) * 1000)));
- var timeAgo = moment.duration(timeAgoTime);
// This variable was called "timeAgoTime" but it's been renamed to avoid confusing it with the "timeAgoTime" variable
// used to pass the block time into time-ago-text.pug.
- var timeAgoTimeBlocksList = moment.utc(new Date()).diff(moment.utc(new Date(parseInt(block.time) * 1000)));
- var timeAgo = moment.duration(timeAgoTimeBlocksList);

- var timeDiff = null;

Expand All @@ -88,12 +90,17 @@

td.text-end

if (sort != "asc" && blockIndex == 0 && offset == 0 && timeAgoTime > (15 * 60 * 1000))
- var timeAgoTime = block.time;
span.text-danger.border-dotted(title="It's been > 15 min since this latest block.", data-bs-toggle="tooltip")
include ./time-ago-text.pug
- var timeAgoTime = block.time;
if (sort != "asc" && blockIndex == 0 && offset == 0)
-var latestBlock = true;
if (autoRefresh == "off" && timeAgoTimeBlocksList > (15 * 60 * 1000))
span.text-danger.border-dotted(title="It's been > 15 min since this latest block.", data-bs-toggle="tooltip")
include ./time-ago-text.pug
else
span(id="old-block-warning")
include ./time-ago-text.pug
else
- var timeAgoTime = block.time;
-var latestBlock = false;
include ./time-ago-text.pug

td.text-end.d-none.d-sm-table-cell
Expand Down Expand Up @@ -263,3 +270,121 @@
i.fas.fa-backspace.text-danger

- var lastBlock = block;

if (autoRefresh == "on")
script.
var allowTimeAgoUpdates = true;
// We don't have access to the moments library on the client side. It's easy enough to implement
// minutes/hours/days/weeks, but beyond that it gets complex. The initial string representations
// are generated on the server side by moments: if we see "mo" (months) or "y" (years) anywhere
// we disable updates. No one is likely to notice, and this seems better than letting a page
// gradually accumulate inconsistencies where times gradually increment up from "4w 3d" to "4w 4d"
// instead of "1mo 1d" or similar.
var timeAgoSpans = document.querySelectorAll('[id=timeAgo]');
timeAgoSpans.forEach(item => {
if (item.textContent.includes("mo") || item.textContent.includes("y")) {
allowTimeAgoUpdates = false;
}
});

function updateTimeAgo() {
if (!allowTimeAgoUpdates)
return;

var now = new Date();
timeAgoSpans.forEach(item => {
var latestBlock = (item.getAttribute("data-latestblock") == "true");
var timeAgoTime = new Date(parseInt(item.getAttribute("data-timeagotime")) * 1000);
var timeAgoRawSeconds = Math.floor((now - timeAgoTime) / 1000);
// We use >= here instead of > in the non-auto-refresh case because it means the age changes to 15m
// and turns red simultaneously, which I think looks neater.
if (latestBlock && timeAgoRawSeconds >= (15 * 60)) {
warningSpan = document.getElementById('old-block-warning');
warningSpan.setAttribute("class", "text-danger border-dotted");
warningSpan.setAttribute("title", "It's been > 15 min since this latest block.");
warningSpan.setAttribute("data-bs-toggle", "tooltip");
}

var timeAgoHours = Math.floor(timeAgoRawSeconds / 3600);
var timeAgoMinutes = Math.floor((timeAgoRawSeconds - timeAgoHours * 3600) / 60);
var timeAgoString;
if (timeAgoHours < 1) {
if (timeAgoMinutes < 1)
timeAgoString = `${timeAgoRawSeconds}s`
else
timeAgoString = `${timeAgoMinutes}m`
} else {
if (timeAgoHours < 24) {
timeAgoString = `${timeAgoHours}h`
if (timeAgoMinutes > 0)
timeAgoString += ` ${timeAgoMinutes}m`;
} else {
// We use "hr" instead of "h" here to match the behaviour of time-ago-text.pug.
timeAgoDays = Math.floor(timeAgoHours / 24);
timeAgoHours -= timeAgoDays * 24;
if (timeAgoDays < 7) {
timeAgoString = `${timeAgoDays}d`;
if (timeAgoHours > 0)
timeAgoString += ` ${timeAgoHours}hr`;
} else {
timeAgoWeeks = Math.floor(timeAgoDays / 7);
timeAgoDays -= timeAgoWeeks * 7;
timeAgoString = `${timeAgoWeeks}w`
if (timeAgoHours > 0)
timeAgoString += ` ${timeAgoDays}d ${timeAgoHours}hr`
else if (timeAgoDays > 0)
timeAgoString += ` ${timeAgoDays}d`
}
}
}
item.textContent = timeAgoString;
});

setTimeout(updateTimeAgo, 1000);
};

updateTimeAgo();

// Having currentBlockHeight defined here triggers polling of the current block height and a page refresh
// when it changes. This is used on the home page and on the block browser when it shows the most recent
// blocks.
if (periodicRefresh)
script.
function periodicRefresh() {
// Reload the page occasionally in order to force the network summary and predicted next block
// to refresh.
currentTime = new Date();
if ((currentTime - pageLoadTime) >= 5 * 60 * 1000) {
window.location.reload();
}
}
else
script.
function periodicRefresh() {
}
if (currentBlockHeight)
script.
var pageLoadTime = new Date();
function checkRefresh() {
// H/T: https://learnwithparam.com/blog/how-to-handle-fetch-errors/
fetch('/api/blocks/tip/height')
.then((response) => {
if (response.status >= 200 && response.status <= 299) {
return response.json();
} else {
throw Error(response.statusText);
}
})
.then((newBlockHeight) => {
// Refresh the page if the block height has changed.
if (newBlockHeight != !{currentBlockHeight}) {
window.location.reload();
}
periodicRefresh();
document.getElementById('connection-warning').style.display='none';
}).catch((error) => {
document.getElementById('connection-warning').style.display='block';
});
setTimeout(checkRefresh, 10 * 1000);
};
checkRefresh();
4 changes: 4 additions & 0 deletions views/includes/connection-warning.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
div.alert.alert-warning(id="connection-warning", style="display:none")
div.fw-bold.mb-1 Unable to connect to node to check for new blocks...

div.mb-1 Auto-refresh is on but this explorer is failing to connect to your node so new blocks cannot be detected. Retries will be attempted automatically.
13 changes: 6 additions & 7 deletions views/includes/time-ago-text.pug
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

if (timeAgo.asHours() < 1)
if (timeAgo.asMinutes() < 1)
span #{timeAgo.seconds()}s
span(id="timeAgo", data-timeagotime=timeAgoTime.toString(), data-latestblock=latestBlock.toString()) #{timeAgo.seconds()}s
else
span #{timeAgo.minutes()}m
span(id="timeAgo", data-timeagotime=timeAgoTime.toString(), data-latestblock=latestBlock.toString()) #{timeAgo.minutes()}m

else
if (timeAgo.asHours() >= 1 && timeAgo.asHours() < 24)
span #{timeAgo.hours()}h

if (timeAgo.minutes() > 0)
span #{timeAgo.minutes()}m

span(id="timeAgo", data-timeagotime=timeAgoTime.toString(), data-latestblock=latestBlock.toString()) #{timeAgo.hours()}h #{timeAgo.minutes()}m
else
span(id="timeAgo", data-timeagotime=timeAgoTime.toString(), data-latestblock=latestBlock.toString()) #{timeAgo.hours()}h
else
span #{utils.shortenTimeDiff(timeAgo.format()).split(", ").join(" ")}
span(id="timeAgo", data-timeagotime=timeAgoTime.toString(), data-latestblock=latestBlock.toString()) #{utils.shortenTimeDiff(timeAgo.format()).split(", ").join(" ")}
2 changes: 2 additions & 0 deletions views/index.pug
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ block content
span.fw-bold Progress:
span #{new Decimal(getblockchaininfo.verificationprogress).times(100).toDP(3)}%

if (autoRefresh == "on")
include includes/connection-warning.pug


include includes/index-network-summary.pug
Expand Down
10 changes: 9 additions & 1 deletion views/layout.pug
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ html(lang="en")
i.fas.fa-moon

hr.dropdown-divider.mt-2.mb-2
span.dropdown-header Dislay Timezone
span.dropdown-header Display Timezone

- var items = ["fa", "sat"];
if (config.queryExchangeRates && global.exchangeRates)
Expand All @@ -197,6 +197,14 @@ html(lang="en")
span#browser-tz-offset.badge.bg-light.text-dark.ms-1.border
i.fas.fa-info-circle.ms-1(title="This value comes from your brower. If you wish to set a custom offset, you can do so below, in 'More settings...'", data-bs-toggle="tooltip")

hr.dropdown-divider.mt-2.mb-2
span.dropdown-header Auto Refresh
- var items = ["off", "on"];

.btn-group.ms-3
a.btn.btn-sm.px-3(href=`./changeSetting?name=autoRefresh&value=off`, class=(userSettings.autoRefresh == "off" ? "btn-primary" : "btn-outline-primary")) Off
a.btn.btn-sm.px-3(href=`./changeSetting?name=autoRefresh&value=on`, class=(userSettings.autoRefresh == "off" ? "btn-outline-primary" : "btn-primary")) On

hr.dropdown-divider.mt-2.mb-2

a.dropdown-item(href=`./user-settings`) More settings...
Expand Down