diff --git a/public/audio/609335__kenneth_cooney__levelup.wav b/public/audio/609335__kenneth_cooney__levelup.wav new file mode 100644 index 0000000..731ff80 Binary files /dev/null and b/public/audio/609335__kenneth_cooney__levelup.wav differ diff --git a/routes/snippetRouter.js b/routes/snippetRouter.js index 2cbb3a0..e5b12b3 100644 --- a/routes/snippetRouter.js +++ b/routes/snippetRouter.js @@ -84,6 +84,59 @@ router.get("/next-block", asyncHandler(async (req, res, next) => { res.render("snippets/index-next-block"); })); +router.get("/index-halving-countdown", asyncHandler(async (req, res, next) => { + try { + const getblockchaininfo = await utils.timePromise("snippet.index-halving-countdown.getBlockchainInfo", async () => { + return await coreApi.getBlockchainInfo(); + }); + + let promises = []; + + res.locals.getblockchaininfo = getblockchaininfo; + res.locals.difficultyPeriod = parseInt(Math.floor(getblockchaininfo.blocks / coinConfig.difficultyAdjustmentBlockCount)); + + let blockHeights = []; + if (getblockchaininfo.blocks) { + for (let i = 0; i < 1; i++) { + blockHeights.push(getblockchaininfo.blocks - i); + } + } else if (global.activeBlockchain == "regtest") { + // hack: default regtest node returns getblockchaininfo.blocks=0, despite + // having a genesis block; hack this to display the genesis block + blockHeights.push(0); + } + + promises.push(utils.timePromise("snippet.index-halving-countdown.getBlockHeaderByHeight", async () => { + let h = coinConfig.difficultyAdjustmentBlockCount * res.locals.difficultyPeriod; + res.locals.difficultyPeriodFirstBlockHeader = await coreApi.getBlockHeaderByHeight(h); + })); + + promises.push(utils.timePromise("snippet.index-halving-countdown.getBlocksByHeight", async () => { + const latestBlocks = await coreApi.getBlocksByHeight(blockHeights); + + res.locals.latestBlocks = latestBlocks; + })); + + await utils.awaitPromises(promises); + + + let nextHalvingData = utils.nextHalvingEstimates(res.locals.difficultyPeriodFirstBlockHeader, res.locals.latestBlocks[0]); + + res.locals.nextHalvingData = nextHalvingData; + + await utils.timePromise("snippet.index-halving-countdown.render", async () => { + res.render("snippets/index-halving-countdown"); + }); + + } catch (e) { + res.locals.pageErrors.push(utils.logError("390wrgehburfuge", e)); + + await utils.timePromise("snippet.index-halving-countdown.render", async () => { + res.render("snippets/index-halving-countdown"); + }); + } +})); + router.get("/utxo-set", asyncHandler(async (req, res, next) => { const promises = []; diff --git a/views/includes/index-network-summary.pug b/views/includes/index-network-summary.pug index fa36bc9..20c807d 100644 --- a/views/includes/index-network-summary.pug +++ b/views/includes/index-network-summary.pug @@ -5,7 +5,37 @@ if (exchangeRates || networkVolume) - var utxoCalculatingDesc = "At startup the app pulls a summary of the UTXO set. Until this summary is retrieved this data can't be displayed. Wait for the summary request to your node to return, then refresh this page."; +- let halvingSoon = false; +- let halvingVerySoon = false; +- let halvingProgressPercent = 0; +if (nextHalvingData) + - halvingProgressPercent = new Decimal(100).minus(new Decimal(nextHalvingData.blocksUntilNextHalving).dividedBy(nextHalvingData.halvingBlockInterval).times(100)); + + if (halvingProgressPercent > 65) + - halvingSoon = true; + + if (halvingProgressPercent > 98) + -halvingVerySoon = true; + + +if (halvingVerySoon) + #index-halving-countdown-wrapper + .d-flex.justify-content-start + .text-success + +sectionTitle("Halving Countdown 👀") + + .ms-2 + span.spinner-border.spinner-border-sm + + + .mb-section + .card.mb-section.shadow-sm.border-2.border-success + .card-body + div#index-halving-countdown + .text-center.text-reset + .mt-2 + .spinner-border.spinner-border-sm if (exchangeRates) +contentSection("Market") @@ -80,13 +110,7 @@ if (exchangeRates) +contentSection - - let halvingShown = false; - - let halvingProgressPercent = new Decimal(100).minus(new Decimal(nextHalvingData.blocksUntilNextHalving).dividedBy(nextHalvingData.halvingBlockInterval).times(100)); - - if (nextHalvingData && halvingProgressPercent > 65) - - halvingShown = true; - - +summaryRow(2 + (halvingShown ? 1 : 0) + (hashrate7d ? 1 : 0) + (chainTxStats && chainTxStats[7 * targetBlocksPerDay] ? 1 : 0) + (difficultyAdjustmentData ? 1 : 0)) + +summaryRow(2 + (halvingSoon ? 1 : 0) + (hashrate7d ? 1 : 0) + (chainTxStats && chainTxStats[7 * targetBlocksPerDay] ? 1 : 0) + (difficultyAdjustmentData ? 1 : 0)) if (hashrate7d) +summaryItem("7d Hashrate", "The average hashrate of the global mining network over the last 7 days. A comparison to the 30-day average is included to help gauge the rate of change.") - var hashrate7dayData = utils.formatLargeNumber(hashrate7d, 1); @@ -110,7 +134,7 @@ if (exchangeRates) | ) - if (halvingShown) + if (halvingSoon) if (false) pre #{JSON.stringify(nextHalvingData)} diff --git a/views/includes/shared-mixins.pug b/views/includes/shared-mixins.pug index 3af95eb..3b6a360 100644 --- a/views/includes/shared-mixins.pug +++ b/views/includes/shared-mixins.pug @@ -618,7 +618,9 @@ mixin timestamp(timestamp, options={includeAgo:true, agoOptions:{}}) -mixin timeAgo(timeAgoTime, options={oneElement:false, agoText:true}) +mixin timeAgo(timeAgoTime, options={oneElement:false, agoText:true, showPlus:true}) + - let showPlus = "showPlus" in options ? options.showPlus : true; + - let oneElement = false; if (options && "oneElement" in options) - oneElement = options.oneElement; @@ -640,7 +642,7 @@ mixin timeAgo(timeAgoTime, options={oneElement:false, agoText:true}) - timeAgo = moment.duration(moment.utc(new Date(parseInt(timeAgoTime) * 1000)).diff(moment.utc(new Date()))); - if (future) + if (future && showPlus) span + diff --git a/views/index.pug b/views/index.pug index 0a7eca2..6aa3ef9 100644 --- a/views/index.pug +++ b/views/index.pug @@ -127,10 +127,44 @@ block content block endOfBody script. + var audio = new Audio('./audio/609335__kenneth_cooney__levelup.wav'); + var tipInfo = null; + + function refreshHalvingCountdownUI() { + $.get("./snippet/index-halving-countdown", function(data) { + $("#index-halving-countdown").html(data); + enableTooltipsAndPopovers(); + }); + } + + function checkHalvingCountdown() { + $.get("./api/blocks/tip", function(data) { + if (data.height > tipInfo.height) { + tipInfo = data; + + audio.play(); + + refreshHalvingCountdownUI(); + } + + console.log(tipInfo); + }); + } + $(document).ready(function() { $.get("./snippet/next-block", function(data) { $("#index-next-block").html(data); enableTooltipsAndPopovers(); }); + + $.get("./api/blocks/tip", function(data) { + tipInfo = data; + }); + + if ($("#index-halving-countdown-wrapper").length) { + refreshHalvingCountdownUI(); + + setInterval(checkHalvingCountdown, 3000); + } }); diff --git a/views/snippets/index-halving-countdown.pug b/views/snippets/index-halving-countdown.pug new file mode 100644 index 0000000..40ce1aa --- /dev/null +++ b/views/snippets/index-halving-countdown.pug @@ -0,0 +1,26 @@ +include ../includes/shared-mixins.pug + +if (nextHalvingData.nextHalvingIndex > -1) + +summaryRow(5) + +summaryItem("Blocks Remaining") + | #{nextHalvingData.blocksUntilNextHalving.toLocaleString()} + + +summaryItem("Time Remaining", "The estimated time until halving, based on the number of blocks remaining and the current mining rate (up until the next difficulty adjustment).") + +timeAgo(nextHalvingData.nextHalvingDate.getTime() / 1000, {showPlus:false}) + + +summaryItem("Estimated Date", "The date of halving, estimated based on the number of blocks remaining and the current mining rate (up until the next difficulty adjustment).") + +timestamp(nextHalvingData.nextHalvingDate.getTime() / 1000, {includeAgo:false}) + + +summaryItem("Block Height", "The block height at which the new block subsidy will begin.") + | #{nextHalvingData.nextHalvingBlock.toLocaleString()} + + +summaryItem("New Subsidy", "The new supply that will be created (and rewarded to the winning miner) for each new block during the next halving's era.") + - let subsidy = coinConfig.blockRewardFunction(nextHalvingData.nextHalvingBlock, global.activeBlockchain); + +valueDisplay(subsidy) + + - let progressPercent = new Decimal(nextHalvingData.halvingBlockInterval).minus(nextHalvingData.blocksUntilNextHalving).dividedBy(nextHalvingData.halvingBlockInterval).times(100); + .progress.mt-5(role="progressbar", aria-label="Next halving progress", aria-valuenow=progressPercent.toDP(0), aria-valuemin="0", aria-valuemax="100") + .progress-bar(style=`width: ${progressPercent.toDP(0)}%`) + | #{progressPercent.toDP(4)}% + +