From fc415372bf31e9680c0763eb2d1bd44f88812e96 Mon Sep 17 00:00:00 2001 From: natsoni Date: Mon, 5 Feb 2024 11:22:14 +0100 Subject: [PATCH 01/21] Fix P2SH / P2PKH detection on Liquid address page --- frontend/src/app/components/address/address.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 0e10b207f..060ec6772 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -105,7 +105,7 @@ export class AddressComponent implements OnInit, OnDestroy { .pipe( filter((address) => !!address), tap((address: Address) => { - if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { + if ((this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') && /^([a-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) { this.apiService.validateAddress$(address.address) .subscribe((addressInfo) => { this.addressInfo = addressInfo; From c58115a96c2cbcc89c8de4c9482d0d28e0e23645 Mon Sep 17 00:00:00 2001 From: natsoni Date: Mon, 5 Feb 2024 11:53:05 +0100 Subject: [PATCH 02/21] Add confidential addresses in liquid address regex --- frontend/src/app/shared/regex.utils.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/shared/regex.utils.ts b/frontend/src/app/shared/regex.utils.ts index 128f7566e..93f4b9275 100644 --- a/frontend/src/app/shared/regex.utils.ts +++ b/frontend/src/app/shared/regex.utils.ts @@ -77,11 +77,15 @@ const ADDRESS_CHARS: { + `)`, }, liquid: { - base58: `[GHPQ]` // G|H is P2PKH, P|Q is P2SH - + BASE58_CHARS - + `{33}`, // All min-max lengths are 34 + base58: `[GHPQ]` // PQ is P2PKH, GH is P2SH + + BASE58_CHARS + + `{33}` // All min-max lengths are 34 + + `|` + + `[V][TJ]` // Confidential P2PKH or P2SH starts with VT or VJ + + BASE58_CHARS + + `{78}`, bech32: `(?:` - + `(?:` // bech32 liquid starts with ex1 or lq1 + + `(?:` // bech32 liquid starts with ex1 (unconfidential) or lq1 (confidential) + `ex1` + `|` + `lq1` From b453898b1d565f997e285c3f20d01e9942ea827c Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 6 Feb 2024 13:42:23 +0100 Subject: [PATCH 03/21] Add glow effect to peg outs in progress --- .../recent-pegs-list.component.html | 4 ++-- .../recent-pegs-list.component.scss | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html index d562328a5..b8e172eb2 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html @@ -34,7 +34,7 @@ - + @@ -57,7 +57,7 @@ ‎{{ peg.blocktime * 1000 | date:'yyyy-MM-dd HH:mm' }}
()
- + diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.scss b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.scss index 92f5bc64f..4bb89d781 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.scss +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.scss @@ -105,3 +105,16 @@ tr, td, th { .debit { color: #D81B60; } + +.glow-effect { + animation: color-oscillation 1s ease-in-out infinite alternate; +} + +@keyframes color-oscillation { + 0% { + color: #777983; + } + 100% { + color: #D81B60; + } +} From 602c87bea07aeda3caf8a2e8f92280f3b9532f87 Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 6 Feb 2024 15:53:54 +0100 Subject: [PATCH 04/21] Update peg-in/out links to open new page on click --- .../transactions-list/transactions-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 24d34d6ec..625a7c70e 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -33,7 +33,7 @@ - + @@ -204,7 +204,7 @@ Peg-out to - + From b561648082c32d5652cb99b16d3c36cb923a209e Mon Sep 17 00:00:00 2001 From: natsoni Date: Tue, 6 Feb 2024 17:35:02 +0100 Subject: [PATCH 05/21] Add peg ins/out volume to stats components --- backend/src/api/liquid/elements-parser.ts | 10 ++++ backend/src/api/liquid/liquid.routes.ts | 13 +++++ .../recent-pegs-list.component.html | 2 +- .../recent-pegs-list.component.ts | 2 +- .../recent-pegs-stats.component.html | 50 +++++++++++++++++-- .../recent-pegs-stats.component.scss | 10 +++- .../recent-pegs-stats.component.ts | 8 ++- .../reserves-audit-dashboard.component.html | 2 +- .../reserves-audit-dashboard.component.ts | 10 +++- .../src/app/interfaces/node-api.interface.ts | 5 ++ frontend/src/app/services/api.service.ts | 6 ++- 11 files changed, 105 insertions(+), 13 deletions(-) diff --git a/backend/src/api/liquid/elements-parser.ts b/backend/src/api/liquid/elements-parser.ts index 05f35c085..8c3d519ab 100644 --- a/backend/src/api/liquid/elements-parser.ts +++ b/backend/src/api/liquid/elements-parser.ts @@ -433,6 +433,16 @@ class ElementsParser { const [rows] = await DB.query(query); return rows; } + + // Get all peg in / out from the last month + public async $getPegsVolumeDaily(): Promise { + const pegInQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount > 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`); + const pegOutQuery = await DB.query(`SELECT SUM(amount) AS volume, COUNT(*) AS number FROM elements_pegs WHERE amount < 0 and datetime > UNIX_TIMESTAMP(TIMESTAMPADD(DAY, -1, CURRENT_TIMESTAMP()));`); + return [ + pegInQuery[0][0], + pegOutQuery[0][0] + ]; + } } export default new ElementsParser(); diff --git a/backend/src/api/liquid/liquid.routes.ts b/backend/src/api/liquid/liquid.routes.ts index 64d631a05..b87348d1b 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -17,6 +17,7 @@ class LiquidRoutes { app .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs', this.$getElementsPegs) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/month', this.$getElementsPegsByMonth) + .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegs/volume', this.$getPegsVolumeDaily) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves', this.$getFederationReserves) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/reserves/month', this.$getFederationReservesByMonth) .get(config.MEMPOOL.API_URL_PREFIX + 'liquid/pegouts', this.$getPegOuts) @@ -189,6 +190,18 @@ class LiquidRoutes { } } + private async $getPegsVolumeDaily(req: Request, res: Response) { + try { + const pegsVolume = await elementsParser.$getPegsVolumeDaily(); + res.header('Pragma', 'public'); + res.header('Cache-control', 'public'); + res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); + res.json(pegsVolume); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + } export default new LiquidRoutes(); diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html index b8e172eb2..b0683389e 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.html @@ -18,7 +18,7 @@ - + diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts index e921e1250..d839d891f 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-list/recent-pegs-list.component.ts @@ -47,7 +47,7 @@ export class RecentPegsListComponent implements OnInit { ngOnInit(): void { this.isLoading = !this.widget; this.env = this.stateService.env; - this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; + this.skeletonLines = this.widget === true ? [...Array(5).keys()] : [...Array(15).keys()]; if (!this.widget) { this.seoService.setTitle($localize`:@@a8b0889ea1b41888f1e247f2731cc9322198ca04:Recent Peg-In / Out's`); diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html index fca78b881..2a853ff5d 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.html @@ -1,7 +1,47 @@ -
-
- -
Recent Peg-In / Out's 
-
+
+ +
+
+
+
+{{ (+pegsVolume[0].volume) / 100000000 | number: '1.2-2' }} BTC
+
{{ (+pegsVolume[0].number) }} Peg-Ins
+
+
+
+
+
{{ (+pegsVolume[1].volume) / 100000000 | number: '1.2-2' }} BTC
+
{{ (+pegsVolume[1].number) }} Peg-Outs
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.scss b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.scss index 0534c9b5d..658094609 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.scss +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.scss @@ -1,7 +1,6 @@ .fee-estimation-container { display: flex; justify-content: space-between; - padding-bottom: 1rem; @media (min-width: 376px) { flex-direction: row; } @@ -36,6 +35,7 @@ top: 0px; } .fee-text{ + border-bottom: 1px solid #ffffff1c; width: fit-content; margin: auto; line-height: 1.45; @@ -69,3 +69,11 @@ text-decoration: none; color: inherit; } + +.credit { + color: #7CB342; +} + +.debit { + color: #D81B60; +} \ No newline at end of file diff --git a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts index 3fbebf715..7bf8e6910 100644 --- a/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/recent-pegs-stats/recent-pegs-stats.component.ts @@ -1,4 +1,7 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { PegsVolume } from '../../../interfaces/node-api.interface'; + @Component({ selector: 'app-recent-pegs-stats', templateUrl: './recent-pegs-stats.component.html', @@ -6,10 +9,11 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class RecentPegsStatsComponent implements OnInit { + @Input() pegsVolume$: Observable; + constructor() { } ngOnInit(): void { - } } diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html index e9f6b4ccd..47d6e7aed 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.html @@ -25,7 +25,7 @@
- +
diff --git a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts index 9b23eb7cb..2133f0fe2 100644 --- a/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts +++ b/frontend/src/app/components/liquid-reserves-audit/reserves-audit-dashboard/reserves-audit-dashboard.component.ts @@ -4,7 +4,7 @@ import { WebsocketService } from '../../../services/websocket.service'; import { StateService } from '../../../services/state.service'; import { Observable, Subject, combineLatest, delayWhen, filter, interval, map, of, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime, timer } from 'rxjs'; import { ApiService } from '../../../services/api.service'; -import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo, RecentPeg } from '../../../interfaces/node-api.interface'; +import { AuditStatus, CurrentPegs, FederationAddress, FederationUtxo, PegsVolume, RecentPeg } from '../../../interfaces/node-api.interface'; @Component({ selector: 'app-reserves-audit-dashboard', @@ -20,6 +20,7 @@ export class ReservesAuditDashboardComponent implements OnInit { federationUtxos$: Observable; recentPegIns$: Observable; recentPegOuts$: Observable; + pegsVolume$: Observable; federationAddresses$: Observable; federationAddressesOneMonthAgo$: Observable; liquidPegsMonth$: Observable; @@ -127,6 +128,13 @@ export class ReservesAuditDashboardComponent implements OnInit { share() ); + this.pegsVolume$ = this.auditUpdated$.pipe( + filter(auditUpdated => auditUpdated === true), + throttleTime(40000), + switchMap(_ => this.apiService.pegsVolume$()), + share() + ); + this.federationAddresses$ = this.auditUpdated$.pipe( filter(auditUpdated => auditUpdated === true), throttleTime(40000), diff --git a/frontend/src/app/interfaces/node-api.interface.ts b/frontend/src/app/interfaces/node-api.interface.ts index cebb23f27..eaeeca94c 100644 --- a/frontend/src/app/interfaces/node-api.interface.ts +++ b/frontend/src/app/interfaces/node-api.interface.ts @@ -82,6 +82,11 @@ export interface CurrentPegs { hash: string; } +export interface PegsVolume { + volume: string; + number: number; +} + export interface FederationAddress { bitcoinaddress: string; balance: string; diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 38060d47d..bd20ec36e 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, - PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg } from '../interfaces/node-api.interface'; + PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume } from '../interfaces/node-api.interface'; import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; import { StateService } from './state.service'; import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface'; @@ -182,6 +182,10 @@ export class ApiService { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs'); } + pegsVolume$(): Observable { + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/volume'); + } + listLiquidPegsMonth$(): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month'); } From dfdd286f7547dc1fbc6f2c45f336adbca9138381 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 6 Feb 2024 21:39:29 +0000 Subject: [PATCH 06/21] Fix infinite address scroll --- .../components/address/address.component.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 0e10b207f..ed28fa1d2 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -31,8 +31,7 @@ export class AddressComponent implements OnInit, OnDestroy { addressLoadingStatus$: Observable; addressInfo: null | AddressInformation = null; - totalConfirmedTxCount = 0; - loadedConfirmedTxCount = 0; + fullyLoaded = false; txCount = 0; received = 0; sent = 0; @@ -66,7 +65,7 @@ export class AddressComponent implements OnInit, OnDestroy { switchMap((params: ParamMap) => { this.error = undefined; this.isLoadingAddress = true; - this.loadedConfirmedTxCount = 0; + this.fullyLoaded = false; this.address = null; this.isLoadingTransactions = true; this.transactions = null; @@ -128,7 +127,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.tempTransactions = transactions; if (transactions.length) { this.lastTransactionTxId = transactions[transactions.length - 1].txid; - this.loadedConfirmedTxCount += transactions.filter((tx) => tx.status.confirmed).length; } const fetchTxs: string[] = []; @@ -191,8 +189,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.audioService.playSound('magic'); } } - this.totalConfirmedTxCount++; - this.loadedConfirmedTxCount++; }); } @@ -252,16 +248,20 @@ export class AddressComponent implements OnInit, OnDestroy { } loadMore() { - if (this.isLoadingTransactions || !this.totalConfirmedTxCount || this.loadedConfirmedTxCount >= this.totalConfirmedTxCount) { + if (this.isLoadingTransactions || this.fullyLoaded) { return; } this.isLoadingTransactions = true; this.retryLoadMore = false; this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId) .subscribe((transactions: Transaction[]) => { - this.lastTransactionTxId = transactions[transactions.length - 1].txid; - this.loadedConfirmedTxCount += transactions.length; - this.transactions = this.transactions.concat(transactions); + if (transactions && transactions.length) { + this.lastTransactionTxId = transactions[transactions.length - 1].txid; + this.transactions = this.transactions.concat(transactions); + } + if (transactions?.length == null || transactions.length < 50) { + this.fullyLoaded = true; + } this.isLoadingTransactions = false; }, (error) => { @@ -278,7 +278,6 @@ export class AddressComponent implements OnInit, OnDestroy { this.received = this.address.chain_stats.funded_txo_sum + this.address.mempool_stats.funded_txo_sum; this.sent = this.address.chain_stats.spent_txo_sum + this.address.mempool_stats.spent_txo_sum; this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count; - this.totalConfirmedTxCount = this.address.chain_stats.tx_count; } ngOnDestroy() { From 3959f52d19d55d612616102e0d1d12cfa58f428f Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 6 Feb 2024 23:42:14 +0000 Subject: [PATCH 07/21] Staggered esplora fallback health checks --- backend/src/api/bitcoin/esplora-api.ts | 119 +++++++++++++++---------- 1 file changed, 70 insertions(+), 49 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.ts b/backend/src/api/bitcoin/esplora-api.ts index c0b548b9a..2f4bcee85 100644 --- a/backend/src/api/bitcoin/esplora-api.ts +++ b/backend/src/api/bitcoin/esplora-api.ts @@ -4,6 +4,7 @@ import http from 'http'; import { AbstractBitcoinApi } from './bitcoin-api-abstract-factory'; import { IEsploraApi } from './esplora-api.interface'; import logger from '../../logger'; +import { Common } from '../common'; interface FailoverHost { host: string, @@ -15,11 +16,13 @@ interface FailoverHost { outOfSync?: boolean, unreachable?: boolean, preferred?: boolean, + checked: boolean, } class FailoverRouter { activeHost: FailoverHost; fallbackHost: FailoverHost; + maxHeight: number = 0; hosts: FailoverHost[]; multihost: boolean; pollInterval: number = 60000; @@ -34,6 +37,7 @@ class FailoverRouter { this.hosts = (config.ESPLORA.FALLBACK || []).map(domain => { return { host: domain, + checked: false, rtts: [], rtt: Infinity, failures: 0, @@ -46,6 +50,7 @@ class FailoverRouter { failures: 0, socket: !!config.ESPLORA.UNIX_SOCKET_PATH, preferred: true, + checked: false, }; this.fallbackHost = this.activeHost; this.hosts.unshift(this.activeHost); @@ -74,66 +79,87 @@ class FailoverRouter { clearTimeout(this.pollTimer); } - const results = await Promise.allSettled(this.hosts.map(async (host) => { - if (host.socket) { - return this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT }); - } else { - return this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT }); - } - })); - const maxHeight = results.reduce((max, result) => Math.max(max, result.status === 'fulfilled' ? result.value?.data || 0 : 0), 0); + const start = Date.now(); // update rtts & sync status - for (let i = 0; i < results.length; i++) { - const host = this.hosts[i]; - const result = results[i].status === 'fulfilled' ? (results[i] as PromiseFulfilledResult>).value : null; - if (result) { - const height = result.data; - const rtt = result.config['meta'].rtt; - host.rtts.unshift(rtt); - host.rtts.slice(0, 5); - host.rtt = host.rtts.reduce((acc, l) => acc + l, 0) / host.rtts.length; - host.latestHeight = height; - if (height == null || isNaN(height) || (maxHeight - height > 2)) { - host.outOfSync = true; + for (const host of this.hosts) { + try { + const result = await (host.socket + ? this.pollConnection.get('/blocks/tip/height', { socketPath: host.host, timeout: config.ESPLORA.FALLBACK_TIMEOUT }) + : this.pollConnection.get(host.host + '/blocks/tip/height', { timeout: config.ESPLORA.FALLBACK_TIMEOUT }) + ); + if (result) { + const height = result.data; + this.maxHeight = Math.max(height, this.maxHeight); + const rtt = result.config['meta'].rtt; + host.rtts.unshift(rtt); + host.rtts.slice(0, 5); + host.rtt = host.rtts.reduce((acc, l) => acc + l, 0) / host.rtts.length; + host.latestHeight = height; + if (height == null || isNaN(height) || (this.maxHeight - height > 2)) { + host.outOfSync = true; + } else { + host.outOfSync = false; + } + host.unreachable = false; } else { - host.outOfSync = false; + host.outOfSync = true; + host.unreachable = true; + host.rtts = []; + host.rtt = Infinity; } - host.unreachable = false; - } else { + } catch (e) { host.outOfSync = true; host.unreachable = true; + host.rtts = []; + host.rtt = Infinity; } + host.checked = true; + + + // switch if the current host is out of sync or significantly slower than the next best alternative + const rankOrder = this.sortHosts(); + // switch if the current host is out of sync or significantly slower than the next best alternative + if (this.activeHost.outOfSync || this.activeHost.unreachable || (this.activeHost !== rankOrder[0] && rankOrder[0].preferred) || (!this.activeHost.preferred && this.activeHost.rtt > (rankOrder[0].rtt * 2) + 50)) { + if (this.activeHost.unreachable) { + logger.warn(`🚨🚨🚨 Unable to reach ${this.activeHost.host}, failing over to next best alternative 🚨🚨🚨`); + } else if (this.activeHost.outOfSync) { + logger.warn(`🚨🚨🚨 ${this.activeHost.host} has fallen behind, failing over to next best alternative 🚨🚨🚨`); + } else { + logger.debug(`🛠️ ${this.activeHost.host} is no longer the best esplora host 🛠️`); + } + this.electHost(); + } + await Common.sleep$(50); } - this.sortHosts(); + const rankOrder = this.updateFallback(); + logger.debug(`Tomahawk ranking:\n${rankOrder.map((host, index) => this.formatRanking(index, host, this.activeHost, this.maxHeight)).join('\n')}`); - logger.debug(`Tomahawk ranking:\n${this.hosts.map((host, index) => this.formatRanking(index, host, this.activeHost, maxHeight)).join('\n')}`); + const elapsed = Date.now() - start; - // switch if the current host is out of sync or significantly slower than the next best alternative - if (this.activeHost.outOfSync || this.activeHost.unreachable || (this.activeHost !== this.hosts[0] && this.hosts[0].preferred) || (!this.activeHost.preferred && this.activeHost.rtt > (this.hosts[0].rtt * 2) + 50)) { - if (this.activeHost.unreachable) { - logger.warn(`🚨🚨🚨 Unable to reach ${this.activeHost.host}, failing over to next best alternative 🚨🚨🚨`); - } else if (this.activeHost.outOfSync) { - logger.warn(`🚨🚨🚨 ${this.activeHost.host} has fallen behind, failing over to next best alternative 🚨🚨🚨`); - } else { - logger.debug(`🛠️ ${this.activeHost.host} is no longer the best esplora host 🛠️`); - } - this.electHost(); - } - - this.pollTimer = setTimeout(() => { this.pollHosts(); }, this.pollInterval); + this.pollTimer = setTimeout(() => { this.pollHosts(); }, Math.max(1, this.pollInterval - elapsed)); } private formatRanking(index: number, host: FailoverHost, active: FailoverHost, maxHeight: number): string { - const heightStatus = host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅'); - return `${host === active ? '⭐️' : ' '} ${host.rtt < Infinity ? Math.round(host.rtt).toString().padStart(5, ' ') + 'ms' : ' - '} ${host.unreachable ? '🔥' : '✅'} | block: ${host.latestHeight || '??????'} ${heightStatus} | ${host.host} ${host === active ? '⭐️' : ' '}`; + const heightStatus = !host.checked ? '⏳' : (host.outOfSync ? '🚫' : (host.latestHeight && host.latestHeight < maxHeight ? '🟧' : '✅')); + return `${host === active ? '⭐️' : ' '} ${host.rtt < Infinity ? Math.round(host.rtt).toString().padStart(5, ' ') + 'ms' : ' - '} ${!host.checked ? '⏳' : (host.unreachable ? '🔥' : '✅')} | block: ${host.latestHeight || '??????'} ${heightStatus} | ${host.host} ${host === active ? '⭐️' : ' '}`; + } + + private updateFallback(): FailoverHost[] { + const rankOrder = this.sortHosts(); + if (rankOrder.length > 1 && rankOrder[0] === this.activeHost) { + this.fallbackHost = rankOrder[1]; + } else { + this.fallbackHost = rankOrder[0]; + } + return rankOrder; } // sort hosts by connection quality, and update default fallback - private sortHosts(): void { + private sortHosts(): FailoverHost[] { // sort by connection quality - this.hosts.sort((a, b) => { + return this.hosts.slice().sort((a, b) => { if ((a.unreachable || a.outOfSync) === (b.unreachable || b.outOfSync)) { if (a.preferred === b.preferred) { // lower rtt is best @@ -145,19 +171,14 @@ class FailoverRouter { return (a.unreachable || a.outOfSync) ? 1 : -1; } }); - if (this.hosts.length > 1 && this.hosts[0] === this.activeHost) { - this.fallbackHost = this.hosts[1]; - } else { - this.fallbackHost = this.hosts[0]; - } } // depose the active host and choose the next best replacement private electHost(): void { this.activeHost.outOfSync = true; this.activeHost.failures = 0; - this.sortHosts(); - this.activeHost = this.hosts[0]; + const rankOrder = this.sortHosts(); + this.activeHost = rankOrder[0]; logger.warn(`Switching esplora host to ${this.activeHost.host}`); } From df7f1cc86b98dd24d1d67e674f7bd16346276424 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 7 Feb 2024 11:31:22 +0100 Subject: [PATCH 08/21] Blockchain component: display dash when feerate is undefined --- .../blockchain-blocks/blockchain-blocks.component.html | 4 ++-- .../shared/components/fee-rate/fee-rate.component.html | 10 ++++++++-- .../shared/components/fee-rate/fee-rate.component.ts | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html index 680beb006..443fc1946 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.html @@ -26,7 +26,7 @@
-   +
-   +
- {{ fee / (weight / 4) | feeRounding:rounding }} sat/vB - {{ fee / weight | feeRounding:rounding }} sat/WU + + {{ fee / (weight / 4) | feeRounding:rounding }} sat/vB + {{ fee / weight | feeRounding:rounding }} sat/WU + + + - sat/vB + - sat/WU + \ No newline at end of file diff --git a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts index 4d65ef0c2..b1d143e7f 100644 --- a/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts +++ b/frontend/src/app/shared/components/fee-rate/fee-rate.component.ts @@ -8,7 +8,7 @@ import { StateService } from '../../../services/state.service'; styleUrls: ['./fee-rate.component.scss'] }) export class FeeRateComponent implements OnInit { - @Input() fee: number; + @Input() fee: number | undefined; @Input() weight: number = 4; @Input() rounding: string = null; @Input() showUnit: boolean = true; From 20614213a7d07c1671b74e128157a38d8f82378e Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 7 Feb 2024 12:41:56 +0100 Subject: [PATCH 09/21] Add skeleton to recent blocks on Liquid dashboard --- .../app/dashboard/dashboard.component.html | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 92b64fe5e..1cb2fadd1 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -117,22 +117,14 @@ - - + - - + + - - + + + + + + + + + + + +
From 03d4375b17a63dd1e9eb950ad726fd0338848841 Mon Sep 17 00:00:00 2001 From: natsoni Date: Wed, 7 Feb 2024 14:51:23 +0100 Subject: [PATCH 10/21] Clean up blocks-list skeleton --- .../app/components/blocks-list/blocks-list.component.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/blocks-list/blocks-list.component.html b/frontend/src/app/components/blocks-list/blocks-list.component.html index 838c7cb4e..fd171720f 100644 --- a/frontend/src/app/components/blocks-list/blocks-list.component.html +++ b/frontend/src/app/components/blocks-list/blocks-list.component.html @@ -92,21 +92,18 @@ - -
HeightMinedPoolMined TXs Size
{{ block.height }} - - - {{ block.extras.pool.name }} - - {{ block.tx_count | number }}
@@ -221,6 +213,17 @@
- + - - + From 4f98a413d19d6c2914a8272c06e0c0e357c720e5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 7 Feb 2024 20:23:35 +0000 Subject: [PATCH 11/21] address scroll handle different page sizes --- frontend/src/app/components/address/address.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index ed28fa1d2..33cf3abf7 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -258,8 +258,7 @@ export class AddressComponent implements OnInit, OnDestroy { if (transactions && transactions.length) { this.lastTransactionTxId = transactions[transactions.length - 1].txid; this.transactions = this.transactions.concat(transactions); - } - if (transactions?.length == null || transactions.length < 50) { + } else { this.fullyLoaded = true; } this.isLoadingTransactions = false; From 29e4a581e7bbd7b856d00b297d2a3e649b916bf9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 7 Feb 2024 21:39:39 +0000 Subject: [PATCH 12/21] Add next block visualization to main dashboard, expand widgets --- .../app/dashboard/dashboard.component.html | 70 ++++++++---------- .../app/dashboard/dashboard.component.scss | 26 ++++++- .../src/app/dashboard/dashboard.component.ts | 72 +++++++++++-------- 3 files changed, 97 insertions(+), 71 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 92b64fe5e..08d4a130a 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -16,23 +16,22 @@
-
-
- -
-
+
- -
- -
-
+ +
Mempool Goggles
+   + +
+
+ +
+
+ +
+
@@ -41,9 +40,21 @@
- -
-
+ +
Incoming Transactions
+
+ +
+
+ +
+
@@ -60,15 +71,6 @@
- -
- -
-
@@ -284,19 +286,3 @@
- - -
Incoming Transactions
- - -  Backend is synchronizing ({{ mempoolLoadingStatus$ | async }}%) - - -
-
 
-
‎{{ mempoolInfoData.value.vBytesPerSecond | ceil | number }} vB/s
-
‎{{ mempoolInfoData.value.vBytesPerSecond * 4 | ceil | number }} WU/s
-
-
-
-
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index 2b319c425..d74d82054 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -44,8 +44,11 @@ .graph-card { height: 100%; + @media (min-width: 768px) { + height: 415px; + } @media (min-width: 992px) { - height: 385px; + height: 500px; } } @@ -258,6 +261,12 @@ .mempool-graph { height: 255px; + @media (min-width: 768px) { + height: 285px; + } + @media (min-width: 992px) { + height: 370px; + } } .loadingGraphs{ height: 250px; @@ -364,3 +373,18 @@ text-decoration: none; color: inherit; } + +.mempool-block-wrapper { + max-height: 430px; + max-width: 430px; + margin: auto; + + @media (min-width: 768px) { + max-height: 344px; + max-width: 344px; + } + @media (min-width: 992px) { + max-height: 430px; + max-width: 430px; + } +} \ No newline at end of file diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 6e65f2332..505fecc14 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnDestroy, OnInit } from '@angular/core'; import { combineLatest, EMPTY, merge, Observable, of, Subject, Subscription, timer } from 'rxjs'; import { catchError, delayWhen, filter, map, scan, share, shareReplay, startWith, switchMap, takeUntil, tap, throttleTime } from 'rxjs/operators'; import { AuditStatus, BlockExtended, CurrentPegs, OptimizedMempoolStats } from '../interfaces/node-api.interface'; @@ -54,8 +54,10 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { currentReserves$: Observable; fullHistory$: Observable; isLoad: boolean = true; + mempoolInfoSubscription: Subscription; currencySubscription: Subscription; currency: string; + incomingGraphHeight: number = 300; private lastPegBlockUpdate: number = 0; private lastPegAmount: string = ''; private lastReservesBlockUpdate: number = 0; @@ -74,6 +76,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { } ngOnDestroy(): void { + this.mempoolInfoSubscription.unsubscribe(); this.currencySubscription.unsubscribe(); this.websocketService.stopTrackRbfSummary(); this.destroy$.next(1); @@ -81,6 +84,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { } ngOnInit(): void { + this.onResize(); this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$; this.seoService.resetTitle(); this.seoService.resetDescription(); @@ -95,36 +99,37 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { this.mempoolInfoData$ = combineLatest([ this.stateService.mempoolInfo$, this.stateService.vbytesPerSecond$ - ]) - .pipe( - map(([mempoolInfo, vbytesPerSecond]) => { - const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100); + ]).pipe( + map(([mempoolInfo, vbytesPerSecond]) => { + const percent = Math.round((Math.min(vbytesPerSecond, this.vBytesPerSecondLimit) / this.vBytesPerSecondLimit) * 100); - let progressColor = 'bg-success'; - if (vbytesPerSecond > 1667) { - progressColor = 'bg-warning'; - } - if (vbytesPerSecond > 3000) { - progressColor = 'bg-danger'; - } + let progressColor = 'bg-success'; + if (vbytesPerSecond > 1667) { + progressColor = 'bg-warning'; + } + if (vbytesPerSecond > 3000) { + progressColor = 'bg-danger'; + } - const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100); - let mempoolSizeProgress = 'bg-danger'; - if (mempoolSizePercentage <= 50) { - mempoolSizeProgress = 'bg-success'; - } else if (mempoolSizePercentage <= 75) { - mempoolSizeProgress = 'bg-warning'; - } + const mempoolSizePercentage = (mempoolInfo.usage / mempoolInfo.maxmempool * 100); + let mempoolSizeProgress = 'bg-danger'; + if (mempoolSizePercentage <= 50) { + mempoolSizeProgress = 'bg-success'; + } else if (mempoolSizePercentage <= 75) { + mempoolSizeProgress = 'bg-warning'; + } - return { - memPoolInfo: mempoolInfo, - vBytesPerSecond: vbytesPerSecond, - progressWidth: percent + '%', - progressColor: progressColor, - mempoolSizeProgress: mempoolSizeProgress, - }; - }) - ); + return { + memPoolInfo: mempoolInfo, + vBytesPerSecond: vbytesPerSecond, + progressWidth: percent + '%', + progressColor: progressColor, + mempoolSizeProgress: mempoolSizeProgress, + }; + }) + ); + + this.mempoolInfoSubscription = this.mempoolInfoData$.subscribe(); this.mempoolBlocksData$ = this.stateService.mempoolBlocks$ .pipe( @@ -347,4 +352,15 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { trackByBlock(index: number, block: BlockExtended) { return block.height; } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.incomingGraphHeight = 300; + } else if (window.innerWidth >= 768) { + this.incomingGraphHeight = 215; + } else { + this.incomingGraphHeight = 180; + } + } } From ba0a3d004df4ce3b4db444f20219f6fbbb9e6f75 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 7 Feb 2024 21:40:22 +0000 Subject: [PATCH 13/21] Expand acceleration dashboard widgets & improve responsiveness --- .../acceleration-fees-graph.component.html | 6 ---- .../acceleration-fees-graph.component.scss | 5 ---- .../acceleration-fees-graph.component.ts | 5 +++- .../accelerator-dashboard.component.html | 15 +++++++++- .../accelerator-dashboard.component.scss | 30 +++++++++++++++++-- .../accelerator-dashboard.component.ts | 16 +++++++++- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html index 98095aa07..3698a3060 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html @@ -27,12 +27,6 @@
-
-
-
Total Bid Boost
-
-
-
diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss index c4b4335ee..e01beb350 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss @@ -53,11 +53,6 @@ padding-bottom: 55px; } } -.chart-widget { - width: 100%; - height: 100%; - max-height: 290px; -} h5 { margin-bottom: 10px; diff --git a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts index d75cbba2d..c47232501 100644 --- a/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core'; import { EChartsOption, graphic } from 'echarts'; -import { Observable, Subscription, combineLatest } from 'rxjs'; +import { Observable, Subscription, combineLatest, fromEvent } from 'rxjs'; import { map, max, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { SeoService } from '../../../services/seo.service'; @@ -28,6 +28,7 @@ import { Acceleration } from '../../../interfaces/node-api.interface'; }) export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { @Input() widget: boolean = false; + @Input() height: number | string = '200'; @Input() right: number | string = 45; @Input() left: number | string = 75; @Input() accelerations$: Observable; @@ -74,6 +75,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { this.statsObservable$ = combineLatest([ (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })), this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), + fromEvent(window, 'resize').pipe(startWith(null)), ]).pipe( tap(([accelerations, blockFeesResponse]) => { this.prepareChartOptions(accelerations, blockFeesResponse.body); @@ -173,6 +175,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { ], animation: false, grid: { + height: this.height, right: this.right, left: this.left, bottom: this.widget ? 30 : 80, diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html index 19d01e726..e7a11ac89 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html @@ -37,6 +37,11 @@
+ +
Next Block
+   + +
@@ -48,7 +53,15 @@
- +
Total Bid Boost
+
+ +
diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss index 145569342..77e287447 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss @@ -17,6 +17,16 @@ } } +.mempool-graph { + height: 295px; + @media (min-width: 768px) { + height: 325px; + } + @media (min-width: 992px) { + height: 400px; + } +} + .card-title { font-size: 1rem; color: #4a68b9; @@ -135,7 +145,12 @@ } .card { - height: 385px; + @media (min-width: 768px) { + height: 420px; + } + @media (min-width: 992px) { + height: 500px; + } } .list-card { height: 410px; @@ -145,7 +160,16 @@ } .mempool-block-wrapper { - max-height: 380px; - max-width: 380px; + max-height: 430px; + max-width: 430px; margin: auto; + + @media (min-width: 768px) { + max-height: 344px; + max-width: 344px; + } + @media (min-width: 992px) { + max-height: 430px; + max-width: 430px; + } } \ No newline at end of file diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index c39dbd253..07a513e7f 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { SeoService } from '../../../services/seo.service'; import { WebsocketService } from '../../../services/websocket.service'; import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; @@ -30,6 +30,8 @@ export class AcceleratorDashboardComponent implements OnInit { minedAccelerations$: Observable; loadingBlocks: boolean = true; + graphHeight: number = 300; + constructor( private seoService: SeoService, private websocketService: WebsocketService, @@ -40,6 +42,7 @@ export class AcceleratorDashboardComponent implements OnInit { } ngOnInit(): void { + this.onResize(); this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); this.pendingAccelerations$ = interval(30000).pipe( @@ -121,4 +124,15 @@ export class AcceleratorDashboardComponent implements OnInit { return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1]; } } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.graphHeight = 330; + } else if (window.innerWidth >= 768) { + this.graphHeight = 245; + } else { + this.graphHeight = 210; + } + } } From ce7a007b62517fe71260dacb5fcf1d9ffb734150 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 7 Feb 2024 23:26:06 +0000 Subject: [PATCH 14/21] Expand mining dashboard widgets & improve responsiveness --- .../hashrate-chart.component.scss | 1 - .../hashrate-chart.component.ts | 44 +++++++++++-------- .../mining-dashboard.component.html | 10 +++-- .../mining-dashboard.component.scss | 13 ++++++ .../mining-dashboard.component.ts | 16 ++++++- .../pool-ranking/pool-ranking.component.html | 2 +- .../pool-ranking/pool-ranking.component.scss | 4 +- .../pool-ranking/pool-ranking.component.ts | 1 + 8 files changed, 65 insertions(+), 26 deletions(-) diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index 886608573..a19cd47aa 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -58,7 +58,6 @@ .chart-widget { width: 100%; height: 100%; - height: 240px; } .pool-distribution { diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts index 9858807a6..2bda9a35a 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; import { echarts, EChartsOption } from '../../graphs/echarts'; -import { merge, Observable, of } from 'rxjs'; +import { combineLatest, fromEvent, merge, Observable, of } from 'rxjs'; import { map, mergeMap, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; @@ -31,6 +31,7 @@ import { seoDescriptionNetwork } from '../../shared/common.utils'; export class HashrateChartComponent implements OnInit { @Input() tableOnly = false; @Input() widget = false; + @Input() height: number = 300; @Input() right: number | string = 45; @Input() left: number | string = 75; @@ -86,28 +87,32 @@ export class HashrateChartComponent implements OnInit { } }); - this.hashrateObservable$ = merge( - this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.radioGroupForm.controls.dateSpan.value), - switchMap((timespan) => { - if (!this.widget && !firstRun) { - this.storageService.setValue('miningWindowPreference', timespan); - } - this.timespan = timespan; - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.apiService.getHistoricalHashrate$(this.timespan); - }) - ), - this.stateService.chainTip$ + this.hashrateObservable$ = combineLatest( + merge( + this.radioGroupForm.get('dateSpan').valueChanges .pipe( - switchMap(() => { + startWith(this.radioGroupForm.controls.dateSpan.value), + switchMap((timespan) => { + if (!this.widget && !firstRun) { + this.storageService.setValue('miningWindowPreference', timespan); + } + this.timespan = timespan; + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; return this.apiService.getHistoricalHashrate$(this.timespan); }) - ) + ), + this.stateService.chainTip$ + .pipe( + switchMap(() => { + return this.apiService.getHistoricalHashrate$(this.timespan); + }) + ) + ), + fromEvent(window, 'resize').pipe(startWith(null)), ).pipe( + map(([response, _]) => response), tap((response: any) => { const data = response.body; @@ -221,6 +226,7 @@ export class HashrateChartComponent implements OnInit { ]), ], grid: { + height: (this.widget && this.height) ? this.height - 30 : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 30 : 70, right: this.right, diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html index fd6e045dc..c4fca72eb 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.html @@ -26,9 +26,11 @@
-
+
- +
+ +
@@ -38,7 +40,9 @@
- +
+ +
diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index 4f01f7cad..c93a952f1 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -17,6 +17,19 @@ } } +.fixed-mempool-graph { + height: 330px; +} + +.mempool-graph, .fixed-mempool-graph { + @media (min-width: 768px) { + height: 345px; + } + @media (min-width: 992px) { + height: 432px; + } +} + .card-title { font-size: 1rem; color: #4a68b9; diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index b3b2093ce..dce9b9f13 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { SeoService } from '../../services/seo.service'; import { WebsocketService } from '../../services/websocket.service'; import { StateService } from '../../services/state.service'; @@ -11,6 +11,8 @@ import { EventType, NavigationStart, Router } from '@angular/router'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class MiningDashboardComponent implements OnInit, AfterViewInit { + graphHeight = 300; + constructor( private seoService: SeoService, private websocketService: WebsocketService, @@ -22,6 +24,7 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { } ngOnInit(): void { + this.onResize(); this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); } @@ -35,4 +38,15 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { } }); } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.graphHeight = 330; + } else if (window.innerWidth >= 768) { + this.graphHeight = 245; + } else { + this.graphHeight = 240; + } + } } diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.html b/frontend/src/app/components/pool-ranking/pool-ranking.component.html index 19ccaccdd..85dd16152 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.html +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.html @@ -76,7 +76,7 @@
-
diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss index 970b3a8ea..f5a49678b 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.scss +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.scss @@ -28,7 +28,9 @@ .chart-widget { width: 100%; height: 100%; - height: 240px; + @media (max-width: 767px) { + max-height: 240px; + } @media (max-width: 485px) { max-height: 200px; } diff --git a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts index 392cdf8c5..f7b91e151 100644 --- a/frontend/src/app/components/pool-ranking/pool-ranking.component.ts +++ b/frontend/src/app/components/pool-ranking/pool-ranking.component.ts @@ -20,6 +20,7 @@ import { isMobile } from '../../shared/common.utils'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PoolRankingComponent implements OnInit { + @Input() height: number = 300; @Input() widget = false; miningWindowPreference: string; From ca2c5d3628859a79e76c99e4103fe331b1d11ce3 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 00:00:06 +0000 Subject: [PATCH 15/21] Expand lightning dashboard widgets & improve responsiveness --- .../lightning-dashboard.component.html | 16 ++-- .../lightning-dashboard.component.scss | 13 +++ .../lightning-dashboard.component.ts | 16 +++- .../nodes-networks-chart.component.html | 2 +- .../nodes-networks-chart.component.scss | 1 - .../nodes-networks-chart.component.ts | 91 ++++++++++--------- .../nodes-per-isp-chart.component.html | 2 +- .../nodes-per-isp-chart.component.ts | 1 + .../lightning-statistics-chart.component.html | 2 +- .../lightning-statistics-chart.component.scss | 1 - .../lightning-statistics-chart.component.ts | 76 +++++++++------- 11 files changed, 134 insertions(+), 87 deletions(-) diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html index 89059185e..ec59b7432 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.html @@ -34,9 +34,11 @@
-
+
- +
+ +
@@ -44,11 +46,13 @@
-
+
-
Lightning Network History
- - +
+
Lightning Network History
+ + +
diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss index e91f7606a..3b95f4d3a 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss @@ -20,6 +20,19 @@ } } +.fixed-mempool-graph { + height: 330px; +} + +.mempool-graph, .fixed-mempool-graph { + @media (min-width: 768px) { + height: 345px; + } + @media (min-width: 992px) { + height: 432px; + } +} + .card-title { font-size: 1rem; color: #4a68b9; diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index dece98ddb..e49d55dd5 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, HostListener, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { share } from 'rxjs/operators'; import { INodesRanking, INodesStatistics } from '../../interfaces/node-api.interface'; @@ -16,6 +16,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { statistics$: Observable; nodesRanking$: Observable; officialMempoolSpace = this.stateService.env.OFFICIAL_MEMPOOL_SPACE; + graphHeight: number = 300; constructor( private lightningApiService: LightningApiService, @@ -24,6 +25,8 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { ) { } ngOnInit(): void { + this.onResize(); + this.seoService.setTitle($localize`:@@142e923d3b04186ac6ba23387265d22a2fa404e0:Lightning Explorer`); this.seoService.setDescription($localize`:@@meta.description.lightning.dashboard:Get stats on the Lightning network (aggregate capacity, connectivity, etc), Lightning nodes (channels, liquidity, etc) and Lightning channels (status, fees, etc).`); @@ -34,4 +37,15 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { ngAfterViewInit(): void { this.stateService.focusSearchInputDesktop(); } + + @HostListener('window:resize', ['$event']) + onResize(): void { + if (window.innerWidth >= 992) { + this.graphHeight = 330; + } else if (window.innerWidth >= 768) { + this.graphHeight = 245; + } else { + this.graphHeight = 210; + } + } } diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html index 908b98fda..915f2f7ec 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.html @@ -35,7 +35,7 @@
-
+
diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss index 0e6fb056d..aaea41265 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.scss @@ -56,7 +56,6 @@ } .chart-widget { width: 100%; - height: 145px; } .pool-distribution { diff --git a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts index 30f786b16..82c74395b 100644 --- a/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts +++ b/frontend/src/app/lightning/nodes-networks-chart/nodes-networks-chart.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; import { echarts, EChartsOption, LineSeriesOption } from '../../graphs/echarts'; import { Observable } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; @@ -26,7 +26,8 @@ import { isMobile } from '../../shared/common.utils'; `], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class NodesNetworksChartComponent implements OnInit { +export class NodesNetworksChartComponent implements OnInit, OnChanges { + @Input() height: number = 150; @Input() right: number | string = 45; @Input() left: number | string = 45; @Input() widget = false; @@ -47,6 +48,9 @@ export class NodesNetworksChartComponent implements OnInit { timespan = ''; chartInstance: any = undefined; + chartData: any; + maxYAxis: number; + constructor( @Inject(LOCALE_ID) public locale: string, private seoService: SeoService, @@ -71,44 +75,49 @@ export class NodesNetworksChartComponent implements OnInit { this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.miningWindowPreference), - switchMap((timespan) => { - this.timespan = timespan; - if (!this.widget && !firstRun) { - this.storageService.setValue('lightningWindowPreference', timespan); - } - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) - .pipe( - tap((response:any) => { - const data = response.body; - const chartData = { - tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]), - clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]), - unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]), - clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]), - }; - let maxYAxis = 0; - for (const day of data) { - maxYAxis = Math.max(maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes); - } - maxYAxis = Math.ceil(maxYAxis / 3000) * 3000; - this.prepareChartOptions(chartData, maxYAxis); - this.isLoading = false; - }), - map((response) => { - return { - days: parseInt(response.headers.get('x-total-count'), 10), - }; - }), - ); - }), - share() - ); + this.nodesNetworkObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.miningWindowPreference), + switchMap((timespan) => { + this.timespan = timespan; + if (!this.widget && !firstRun) { + this.storageService.setValue('lightningWindowPreference', timespan); + } + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; + return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) + .pipe( + tap((response:any) => { + const data = response.body; + this.chartData = { + tor_nodes: data.map(val => [val.added * 1000, val.tor_nodes]), + clearnet_nodes: data.map(val => [val.added * 1000, val.clearnet_nodes]), + unannounced_nodes: data.map(val => [val.added * 1000, val.unannounced_nodes]), + clearnet_tor_nodes: data.map(val => [val.added * 1000, val.clearnet_tor_nodes]), + }; + this.maxYAxis = 0; + for (const day of data) { + this.maxYAxis = Math.max(this.maxYAxis, day.tor_nodes + day.clearnet_nodes + day.unannounced_nodes + day.clearnet_tor_nodes); + } + this.maxYAxis = Math.ceil(this.maxYAxis / 3000) * 3000; + this.prepareChartOptions(this.chartData, this.maxYAxis); + this.isLoading = false; + }), + map((response) => { + return { + days: parseInt(response.headers.get('x-total-count'), 10), + }; + }), + ); + }), + share() + ); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.height && this.chartData && this.maxYAxis != null) { + this.prepareChartOptions(this.chartData, this.maxYAxis); + } } prepareChartOptions(data, maxYAxis): void { @@ -228,7 +237,7 @@ export class NodesNetworksChartComponent implements OnInit { title: title, animation: false, grid: { - height: this.widget ? 90 : undefined, + height: this.widget ? ((this.height || 120) - 60) : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 0 : 70, right: (isMobile() && this.widget) ? 35 : this.right, diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html index 259c3503a..794ced684 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.html @@ -39,7 +39,7 @@
-
diff --git a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts index 5342f616b..3d8a4b861 100644 --- a/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp-chart/nodes-per-isp-chart.component.ts @@ -18,6 +18,7 @@ import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pi changeDetection: ChangeDetectionStrategy.OnPush, }) export class NodesPerISPChartComponent implements OnInit { + @Input() height: number = 300; @Input() widget: boolean = false; isLoading = true; diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html index 68fe3ee64..9bb88ac59 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.html @@ -42,7 +42,7 @@
-
diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss index c885e4839..923a8a3e4 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.scss @@ -56,7 +56,6 @@ } .chart-widget { width: 100%; - height: 145px; } .pool-distribution { diff --git a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts index 0e4f66ca0..8bcf01015 100644 --- a/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts +++ b/frontend/src/app/lightning/statistics-chart/lightning-statistics-chart.component.ts @@ -1,6 +1,6 @@ -import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding } from '@angular/core'; +import { Component, Inject, Input, LOCALE_ID, OnInit, HostBinding, OnChanges, SimpleChanges } from '@angular/core'; import { echarts, EChartsOption } from '../../graphs/echarts'; -import { Observable } from 'rxjs'; +import { Observable, combineLatest, fromEvent } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { SeoService } from '../../services/seo.service'; import { formatNumber } from '@angular/common'; @@ -25,7 +25,8 @@ import { isMobile } from '../../shared/common.utils'; } `], }) -export class LightningStatisticsChartComponent implements OnInit { +export class LightningStatisticsChartComponent implements OnInit, OnChanges { + @Input() height: number = 150; @Input() right: number | string = 45; @Input() left: number | string = 45; @Input() widget = false; @@ -37,6 +38,7 @@ export class LightningStatisticsChartComponent implements OnInit { chartInitOptions = { renderer: 'svg', }; + chartData: any; @HostBinding('attr.dir') dir = 'ltr'; @@ -70,36 +72,42 @@ export class LightningStatisticsChartComponent implements OnInit { this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges - .pipe( - startWith(this.miningWindowPreference), - switchMap((timespan) => { - this.timespan = timespan; - if (!this.widget && !firstRun) { - this.storageService.setValue('lightningWindowPreference', timespan); - } - firstRun = false; - this.miningWindowPreference = timespan; - this.isLoading = true; - return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) - .pipe( - tap((response:any) => { - const data = response.body; - this.prepareChartOptions({ - channel_count: data.map(val => [val.added * 1000, val.channel_count]), - capacity: data.map(val => [val.added * 1000, val.total_capacity]), - }); - this.isLoading = false; - }), - map((response) => { - return { - days: parseInt(response.headers.get('x-total-count'), 10), - }; - }), - ); - }), - share(), - ); + this.capacityObservable$ = this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.miningWindowPreference), + switchMap((timespan) => { + this.timespan = timespan; + if (!this.widget && !firstRun) { + this.storageService.setValue('lightningWindowPreference', timespan); + } + firstRun = false; + this.miningWindowPreference = timespan; + this.isLoading = true; + return this.lightningApiService.cachedRequest(this.lightningApiService.listStatistics$, 250, timespan) + .pipe( + tap((response:any) => { + const data = response.body; + this.chartData = { + channel_count: data.map(val => [val.added * 1000, val.channel_count]), + capacity: data.map(val => [val.added * 1000, val.total_capacity]), + }; + this.prepareChartOptions(this.chartData); + this.isLoading = false; + }), + map((response) => { + return { + days: parseInt(response.headers.get('x-total-count'), 10), + }; + }), + ); + }), + share(), + ); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.height && this.chartData) { + this.prepareChartOptions(this.chartData); + } } prepareChartOptions(data): void { @@ -138,7 +146,7 @@ export class LightningStatisticsChartComponent implements OnInit { ]), ], grid: { - height: this.widget ? 90 : undefined, + height: this.widget ? ((this.height || 120) - 60) : undefined, top: this.widget ? 20 : 40, bottom: this.widget ? 0 : 70, right: (isMobile() && this.widget) ? 35 : this.right, From e268a6a033ce8ed8f6b3fa98612d67c81297f8f5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 00:03:34 +0000 Subject: [PATCH 16/21] Fix mining dashboard hashrate graph height --- .../app/components/hashrate-chart/hashrate-chart.component.html | 2 +- .../app/components/hashrate-chart/hashrate-chart.component.scss | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html index 83f8a3a4c..f3d340472 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.html @@ -54,7 +54,7 @@
-
diff --git a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss index a19cd47aa..32885d5de 100644 --- a/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss +++ b/frontend/src/app/components/hashrate-chart/hashrate-chart.component.scss @@ -57,7 +57,6 @@ } .chart-widget { width: 100%; - height: 100%; } .pool-distribution { From f14cd5ee2bdf3ccefbdfdf4b31f766eeea5414d9 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 00:37:16 +0000 Subject: [PATCH 17/21] Swap incoming vb chart and mempool stats --- frontend/src/app/dashboard/dashboard.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 08d4a130a..ae3fff673 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -40,8 +40,9 @@
+ -
Incoming Transactions
+
Incoming Transactions
-

From 3039481686d1a713abfeee4d089820b66c63f4ce Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 02:47:46 +0000 Subject: [PATCH 18/21] Change titles, adjust padding --- .../accelerator-dashboard/accelerator-dashboard.component.html | 2 +- .../accelerator-dashboard/accelerator-dashboard.component.scss | 2 +- .../components/mining-dashboard/mining-dashboard.component.scss | 2 +- .../components/mining-dashboard/mining-dashboard.component.ts | 2 +- frontend/src/app/dashboard/dashboard.component.html | 2 +- frontend/src/app/dashboard/dashboard.component.scss | 2 +- .../lightning-dashboard/lightning-dashboard.component.scss | 2 +- .../lightning-dashboard/lightning-dashboard.component.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html index e7a11ac89..6d9e49265 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html @@ -38,7 +38,7 @@
-
Next Block
+
Mempool Goggles: Accelerations
 
diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss index 77e287447..d6fb57953 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss @@ -149,7 +149,7 @@ height: 420px; } @media (min-width: 992px) { - height: 500px; + height: 510px; } } .list-card { diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index c93a952f1..c07042605 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -26,7 +26,7 @@ height: 345px; } @media (min-width: 992px) { - height: 432px; + height: 442px; } } diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts index dce9b9f13..1a446d673 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.ts @@ -42,7 +42,7 @@ export class MiningDashboardComponent implements OnInit, AfterViewInit { @HostListener('window:resize', ['$event']) onResize(): void { if (window.innerWidth >= 992) { - this.graphHeight = 330; + this.graphHeight = 340; } else if (window.innerWidth >= 768) { this.graphHeight = 245; } else { diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index ae3fff673..403107bf2 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -19,7 +19,7 @@
-
Mempool Goggles
+
Mempool Goggles: Next Block
 
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index d74d82054..f8549e166 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -48,7 +48,7 @@ height: 415px; } @media (min-width: 992px) { - height: 500px; + height: 510px; } } diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss index 3b95f4d3a..052ea6227 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.scss @@ -29,7 +29,7 @@ height: 345px; } @media (min-width: 992px) { - height: 432px; + height: 442px; } } diff --git a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts index e49d55dd5..63cdab42d 100644 --- a/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts +++ b/frontend/src/app/lightning/lightning-dashboard/lightning-dashboard.component.ts @@ -41,7 +41,7 @@ export class LightningDashboardComponent implements OnInit, AfterViewInit { @HostListener('window:resize', ['$event']) onResize(): void { if (window.innerWidth >= 992) { - this.graphHeight = 330; + this.graphHeight = 340; } else if (window.innerWidth >= 768) { this.graphHeight = 245; } else { From d7be5a47371e019aebe7595fc02b949812f3993d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 03:57:11 +0000 Subject: [PATCH 19/21] Simple Goggle Toggles --- .../block-overview-graph.component.ts | 9 +++++---- .../mempool-block-overview.component.html | 4 +++- .../mempool-block-overview.component.ts | 3 +++ .../mining-dashboard.component.scss | 2 +- .../app/dashboard/dashboard.component.html | 20 ++++++++++++++++--- .../app/dashboard/dashboard.component.scss | 19 ++++++++++++++---- .../src/app/dashboard/dashboard.component.ts | 12 +++++++++++ 7 files changed, 56 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index d6000e27b..ac1df2bf5 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -42,6 +42,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() showFilters: boolean = false; @Input() excludeFilters: string[] = []; @Input() filterFlags: bigint | null = null; + @Input() filterMode: 'and' | 'or' = 'and'; @Input() blockConversion: Price; @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); @@ -113,7 +114,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (changes.overrideColor && this.scene) { this.scene.setColorFunction(this.overrideColors); } - if ((changes.filterFlags || changes.showFilters)) { + if ((changes.filterFlags || changes.showFilters || changes.filterMode)) { this.setFilterFlags(); } } @@ -121,8 +122,8 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On setFilterFlags(flags?: bigint | null): void { this.activeFilterFlags = this.filterFlags || flags || null; if (this.scene) { - if (flags != null) { - this.scene.setColorFunction(this.getFilterColorFunction(flags)); + if (this.activeFilterFlags != null) { + this.scene.setColorFunction(this.getFilterColorFunction(this.activeFilterFlags)); } else { this.scene.setColorFunction(this.overrideColors); } @@ -523,7 +524,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On getFilterColorFunction(flags: bigint): ((tx: TxView) => Color) { return (tx: TxView) => { - if ((tx.bigintFlags & flags) === flags) { + if ((this.filterMode === 'and' && (tx.bigintFlags & flags) === flags) || (this.filterMode === 'or' && (tx.bigintFlags & flags) > 0n)) { return defaultColorFunction(tx); } else { return defaultColorFunction( diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html index 85e7eebb1..7cc458e60 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html @@ -1,11 +1,13 @@ diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 8dad6a9c1..7fb036718 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -18,8 +18,11 @@ import TxView from '../block-overview-graph/tx-view'; }) export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { @Input() index: number; + @Input() resolution = 86; @Input() showFilters: boolean = false; @Input() overrideColors: ((tx: TxView) => Color) | null = null; + @Input() filterFlags: bigint | undefined = undefined; + @Input() filterMode: 'and' | 'or' = 'and'; @Output() txPreviewEvent = new EventEmitter(); @ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent; diff --git a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss index c07042605..310b9e9de 100644 --- a/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss +++ b/frontend/src/app/components/mining-dashboard/mining-dashboard.component.scss @@ -26,7 +26,7 @@ height: 345px; } @media (min-width: 992px) { - height: 442px; + height: 472px; } } diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index 403107bf2..bf254a9a8 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -18,13 +18,27 @@
- -
Mempool Goggles: Next Block
+
+
Mempool Goggles: {{ goggleCycle[goggleIndex].name }}
 
+
+ +
{{ filter.name }}
+
+
- +
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index f8549e166..f6362bc21 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -375,8 +375,8 @@ } .mempool-block-wrapper { - max-height: 430px; - max-width: 430px; + max-height: 410px; + max-width: 410px; margin: auto; @media (min-width: 768px) { @@ -384,7 +384,18 @@ max-width: 344px; } @media (min-width: 992px) { - max-height: 430px; - max-width: 430px; + max-height: 410px; + max-width: 410px; + } +} + +.badge { + margin: 6px 5px 8px; + background: none; + border: solid 2px #105fb0; + cursor: pointer; + + &.active { + background: #105fb0; } } \ No newline at end of file diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index 505fecc14..cd6097a43 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -62,6 +62,15 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { private lastPegAmount: string = ''; private lastReservesBlockUpdate: number = 0; + goggleResolution = 82; + goggleCycle = [ + { index: 0, name: 'All' }, + { index: 1, name: 'CPFP', flag: 0b00000010_00000000_00000000n }, + { index: 2, name: 'RBF', flag: 0b00000100_00000000_00000000n }, + { index: 3, name: '💩', flag: 0b00000100_00000000_00000000_00000000n | 0b00000010_00000000_00000000_00000000n | 0b00000001_00000000_00000000_00000000n }, + ]; + goggleIndex = 0; // Math.floor(Math.random() * this.goggleCycle.length); + private destroy$ = new Subject(); constructor( @@ -357,10 +366,13 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { onResize(): void { if (window.innerWidth >= 992) { this.incomingGraphHeight = 300; + this.goggleResolution = 82; } else if (window.innerWidth >= 768) { this.incomingGraphHeight = 215; + this.goggleResolution = 80; } else { this.incomingGraphHeight = 180; + this.goggleResolution = 86; } } } From 3ceb583127655eb83482fb9dc084de9ff857027b Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 8 Feb 2024 04:03:20 +0000 Subject: [PATCH 20/21] Fix Goggles title alignment --- frontend/src/app/dashboard/dashboard.component.html | 6 +++--- frontend/src/app/dashboard/dashboard.component.scss | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index bf254a9a8..c2076d114 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -18,15 +18,15 @@
- -
Mempool Goggles: {{ goggleCycle[goggleIndex].name }}
+
+
Mempool Goggles: {{ goggleCycle[goggleIndex].name }}
 
{{ filter.name }}
diff --git a/frontend/src/app/dashboard/dashboard.component.scss b/frontend/src/app/dashboard/dashboard.component.scss index f6362bc21..704d02813 100644 --- a/frontend/src/app/dashboard/dashboard.component.scss +++ b/frontend/src/app/dashboard/dashboard.component.scss @@ -389,7 +389,7 @@ } } -.badge { +.goggle-badge { margin: 6px 5px 8px; background: none; border: solid 2px #105fb0; From 510dd9132860925bd76f360479074c482e37df09 Mon Sep 17 00:00:00 2001 From: softsimon Date: Thu, 8 Feb 2024 14:52:31 +0800 Subject: [PATCH 21/21] Changed to toggle buttons. Changed filters. --- frontend/src/app/dashboard/dashboard.component.html | 12 +++++------- frontend/src/app/dashboard/dashboard.component.scss | 12 +++++++++++- frontend/src/app/dashboard/dashboard.component.ts | 4 ++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/dashboard/dashboard.component.html b/frontend/src/app/dashboard/dashboard.component.html index c2076d114..676dfa0ee 100644 --- a/frontend/src/app/dashboard/dashboard.component.html +++ b/frontend/src/app/dashboard/dashboard.component.html @@ -24,13 +24,11 @@
- -
{{ filter.name }}
-
+
+ +