From dc063873473085f4891038fb492034004e49c11b Mon Sep 17 00:00:00 2001 From: TechMiX Date: Wed, 6 Dec 2023 19:50:33 +0100 Subject: [PATCH 01/23] fix: rtl issues related to sidenav and global footer --- .../global-footer/global-footer.component.html | 2 +- .../global-footer/global-footer.component.scss | 1 - frontend/src/styles.scss | 12 ++++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.html b/frontend/src/app/shared/components/global-footer/global-footer.component.html index 676db35e5..fba04e605 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.html +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.html @@ -11,7 +11,7 @@ Explore the full Bitcoin ecosystem

-
+
diff --git a/frontend/src/app/shared/components/global-footer/global-footer.component.scss b/frontend/src/app/shared/components/global-footer/global-footer.component.scss index 148383cb4..79c7dcfce 100644 --- a/frontend/src/app/shared/components/global-footer/global-footer.component.scss +++ b/frontend/src/app/shared/components/global-footer/global-footer.component.scss @@ -63,7 +63,6 @@ footer .row.main .links .category:not(:first-child) { } footer .site-options { - float: right; margin-top: -20px; } diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 0e26c4eb3..48d5b5966 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -996,6 +996,18 @@ th { .btn-audit { margin-left: .5em; } + + .sidenav { + @extend .sidenav; + margin-left: 0px !important; + margin-right: -250px; + + .open { + @extend .open; + margin-right: 0px; + right: 0px; + } + } } .scriptmessage { From 9406103ed1d895a15895d0e1d5eada60731b7160 Mon Sep 17 00:00:00 2001 From: TechMiX Date: Thu, 7 Dec 2023 22:49:51 +0100 Subject: [PATCH 02/23] fix: adjust the blockchain container with menu in rtl mode --- frontend/src/styles.scss | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 48d5b5966..be8cec328 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -1001,12 +1001,21 @@ th { @extend .sidenav; margin-left: 0px !important; margin-right: -250px; + } - .open { - @extend .open; - margin-right: 0px; - right: 0px; - } + .sidenav.open { + margin-right: 0px; + } + + .profile_image_container { + @extend .profile_image_container; + margin-right: 0px !important; + margin-left: 15px; + } + + #blockchain-container.with-menu { + width: calc(100% + 120px); + left: 120px; } } From bbcc683a3c2c9db98b67878d4b09105709c66792 Mon Sep 17 00:00:00 2001 From: TechMiX Date: Fri, 8 Dec 2023 14:06:34 +0100 Subject: [PATCH 03/23] fix: use empty sidenav when service is disabled --- .../src/app/components/master-page/master-page.component.html | 2 +- .../src/app/components/master-page/master-page.component.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index dc1cc2c30..f31e262ce 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -78,7 +78,7 @@
-
+
diff --git a/frontend/src/app/components/master-page/master-page.component.scss b/frontend/src/app/components/master-page/master-page.component.scss index 79f5e8a12..a40b4ec61 100644 --- a/frontend/src/app/components/master-page/master-page.component.scss +++ b/frontend/src/app/components/master-page/master-page.component.scss @@ -241,7 +241,7 @@ main { } // empty sidenav -.sidenav { +.empty-sidenav { z-index: 1; background-color: transparent; width: 0px; From 42a3a380d5e56ed33a13d5db99757c8ff8e105ec Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 20 Jul 2023 16:25:09 +0900 Subject: [PATCH 04/23] widgetify block fee rates chart --- .../block-fee-rates-graph.component.html | 46 +++++++- .../block-fee-rates-graph.component.scss | 56 ++++++++- .../block-fee-rates-graph.component.ts | 107 +++++++++++++----- frontend/src/app/services/api.service.ts | 4 + 4 files changed, 176 insertions(+), 37 deletions(-) diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html index 77c35cea8..f5eb5d1cf 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html @@ -1,13 +1,13 @@ - + -
-
+
+
Block Fee Rates -
+
@@ -45,11 +45,45 @@
-
+
+
+
Avg Block Fee (1m)
+

+ +

+
+
+
???
+

+ ??? +

+
+
+
+ +
-
\ No newline at end of file +
+ + +
+
+
Avg Block Fee
+

+ +

+
+
+
???
+

+ +

+
+
+
\ No newline at end of file diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss index f4f4dcc77..21dd458b5 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.scss @@ -57,7 +57,54 @@ .chart-widget { width: 100%; height: 100%; - max-height: 270px; + max-height: 238px; +} + +.block-fee-rates { + min-height: 56px; + display: block; + @media (min-width: 485px) { + display: flex; + flex-direction: row; + } + h5 { + margin-bottom: 10px; + } + .item { + width: 50%; + display: inline-block; + margin: 0px auto 20px; + &:nth-child(2) { + order: 2; + @media (min-width: 485px) { + order: 3; + } + } + &:nth-child(3) { + order: 3; + @media (min-width: 485px) { + order: 2; + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + .card-title { + font-size: 1rem; + color: #4a68b9; + } + .card-text { + font-size: 18px; + span { + color: #ffffff66; + font-size: 12px; + } + } + } } .formRadioGroup { @@ -85,6 +132,13 @@ } } +.skeleton-loader { + width: 100%; + display: block; + max-width: 80px; + margin: 15px auto 3px; +} + .disabled { pointer-events: none; opacity: 0.5; diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index c4d061927..19c51527f 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -1,6 +1,6 @@ -import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; -import { EChartsOption } from '../../graphs/echarts'; -import { Observable, Subscription, combineLatest } from 'rxjs'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; +import { EChartsOption, graphic } from 'echarts'; +import { Observable, Subscription, combineLatest, of } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; @@ -29,6 +29,7 @@ import { ActivatedRoute, Router } from '@angular/router'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class BlockFeeRatesGraphComponent implements OnInit { + @Input() widget = false; @Input() right: number | string = 45; @Input() left: number | string = 75; @@ -57,39 +58,48 @@ export class BlockFeeRatesGraphComponent implements OnInit { private router: Router, private zone: NgZone, private route: ActivatedRoute, + private cd: ChangeDetectorRef, ) { this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); this.radioGroupForm.controls.dateSpan.setValue('1y'); } ngOnInit(): void { - this.seoService.setTitle($localize`:@@ed8e33059967f554ff06b4f5b6049c465b92d9b3:Block Fee Rates`); - this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-fee-rates:See Bitcoin feerates visualized over time, including minimum and maximum feerates per block along with feerates at various percentiles.`); - this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); + if (this.widget) { + this.miningWindowPreference = '1m'; + } else { + this.seoService.setTitle($localize`:@@ed8e33059967f554ff06b4f5b6049c465b92d9b3:Block Fee Rates`); + this.seoService.setDescription($localize`:@@meta.description.bitcoin.graphs.block-fee-rates:See Bitcoin feerates visualized over time, including minimum and maximum feerates per block along with feerates at various percentiles.`); + this.miningWindowPreference = this.miningService.getDefaultTimespan('24h'); + } this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); - this.route - .fragment - .subscribe((fragment) => { - if (['24h', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) { - this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false }); - } - }); + if (!this.widget) { + this.route + .fragment + .subscribe((fragment) => { + if (['24h', '3d', '1w', '1m', '3m', '6m', '1y', '2y', '3y', 'all'].indexOf(fragment) > -1) { + this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false }); + } + }); + } this.statsObservable$ = combineLatest([ - this.radioGroupForm.get('dateSpan').valueChanges.pipe(startWith(this.radioGroupForm.controls.dateSpan.value)), + this.widget ? of(this.miningWindowPreference) : this.radioGroupForm.get('dateSpan').valueChanges.pipe(startWith(this.radioGroupForm.controls.dateSpan.value)), this.stateService.rateUnits$ ]).pipe( switchMap(([timespan, rateUnits]) => { - this.storageService.setValue('miningWindowPreference', timespan); + if (!this.widget) { + this.storageService.setValue('miningWindowPreference', timespan); + } this.timespan = timespan; this.isLoading = true; return this.apiService.getHistoricalBlockFeeRates$(timespan) .pipe( tap((response) => { // Group by percentile - const seriesData = { + const seriesData = this.widget ? { 'Median': [] } : { 'Min': [], '10th': [], '25th': [], @@ -100,13 +110,17 @@ export class BlockFeeRatesGraphComponent implements OnInit { }; for (const rate of response.body) { const timestamp = rate.timestamp * 1000; - seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]); - seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]); - seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]); - seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]); - seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]); - seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]); - seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]); + if (this.widget) { + seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]); + } else { + seriesData['Min'].push([timestamp, rate.avgFee_0, rate.avgHeight]); + seriesData['10th'].push([timestamp, rate.avgFee_10, rate.avgHeight]); + seriesData['25th'].push([timestamp, rate.avgFee_25, rate.avgHeight]); + seriesData['Median'].push([timestamp, rate.avgFee_50, rate.avgHeight]); + seriesData['75th'].push([timestamp, rate.avgFee_75, rate.avgHeight]); + seriesData['90th'].push([timestamp, rate.avgFee_90, rate.avgHeight]); + seriesData['Max'].push([timestamp, rate.avgFee_100, rate.avgHeight]); + } } // Prepare chart @@ -135,15 +149,42 @@ export class BlockFeeRatesGraphComponent implements OnInit { }); } + if (this.widget) { + let maResolution = 30; + const medianMa = []; + for (let i = maResolution - 1; i < seriesData['Median'].length; ++i) { + let avg = 0; + for (let y = maResolution - 1; y >= 0; --y) { + avg += seriesData['Median'][i - y][1]; + } + avg /= maResolution; + medianMa.push([seriesData['Median'][i][0], avg]); + } + series.push({ + zlevel: 1, + name: 'MA', + data: medianMa, + type: 'line', + showSymbol: false, + symbol: 'none', + lineStyle: { + width: 3, + } + }); + } + this.prepareChartOptions({ legends: legends, series: series }, rateUnits === 'wu'); + this.isLoading = false; + this.cd.markForCheck(); }), map((response) => { return { blockCount: parseInt(response.headers.get('x-total-count'), 10), + avgMedianRate: response.body.length ? response.body.reduce((acc, rate) => acc + rate.avgFee_50, 0) / response.body.length : 0, }; }), ); @@ -154,16 +195,22 @@ export class BlockFeeRatesGraphComponent implements OnInit { prepareChartOptions(data, weightMode) { this.chartOptions = { - color: ['#D81B60', '#8E24AA', '#1E88E5', '#7CB342', '#FDD835', '#6D4C41', '#546E7A'], + color: this.widget ? ['#6b6b6b', new graphic.LinearGradient(0, 0, 0, 0.65, [ + { offset: 0, color: '#F4511E' }, + { offset: 0.25, color: '#FB8C00' }, + { offset: 0.5, color: '#FFB300' }, + { offset: 0.75, color: '#FDD835' }, + { offset: 1, color: '#7CB342' } + ])] : ['#D81B60', '#8E24AA', '#1E88E5', '#7CB342', '#FDD835', '#6D4C41', '#546E7A'], animation: false, grid: { right: this.right, left: this.left, - bottom: 80, - top: this.isMobile() ? 10 : 50, + bottom: this.widget ? 30 : 80, + top: this.widget ? 20 : (this.isMobile() ? 10 : 50), }, tooltip: { - show: !this.isMobile(), + show: !this.isMobile() && !this.widget, trigger: 'axis', axisPointer: { type: 'line' @@ -201,7 +248,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { }, xAxis: data.series.length === 0 ? undefined : { - name: formatterXAxisLabel(this.locale, this.timespan), + name: this.widget ? undefined : formatterXAxisLabel(this.locale, this.timespan), nameLocation: 'middle', nameTextStyle: { padding: [10, 0, 0, 0], @@ -218,7 +265,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { padding: [0, 5], }, }, - legend: (data.series.length === 0) ? undefined : { + legend: (this.widget || data.series.length === 0) ? undefined : { padding: [10, 75], data: data.legends, selected: JSON.parse(this.storageService.getValue('fee_rates_legend')) ?? { @@ -256,7 +303,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { max: (val) => this.timespan === 'all' ? Math.min(val.max, 5000) : undefined, }, series: data.series, - dataZoom: [{ + dataZoom: this.widget ? null : [{ type: 'inside', realtime: true, zoomLock: true, diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index 854d15c2a..e379a8626 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,7 +1,11 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, +<<<<<<< HEAD PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface'; +======= + PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration } from '../interfaces/node-api.interface'; +>>>>>>> 9b9adcd43 (widgetify block fee rates chart) import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; import { StateService } from './state.service'; import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface'; From 6f97a2ef741bc140c8aeb4ec9440e879324f3d49 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 20 Jul 2023 16:26:42 +0900 Subject: [PATCH 05/23] Add accelerator dashboard --- .../acceleration-stats.component.html | 51 +++++++++ .../acceleration-stats.component.scss | 88 ++++++++++++++ .../acceleration-stats.component.ts | 43 +++++++ .../accelerations-list.component.html | 62 ++++++++++ .../accelerations-list.component.scss | 107 ++++++++++++++++++ .../accelerations-list.component.ts | 53 +++++++++ .../accelerator-dashboard.component.html | 76 +++++++++++++ .../accelerator-dashboard.component.scss | 102 +++++++++++++++++ .../accelerator-dashboard.component.ts | 22 ++++ frontend/src/app/graphs/graphs.module.ts | 2 + .../src/app/graphs/graphs.routing.module.ts | 12 ++ frontend/src/app/shared/shared.module.ts | 6 + 12 files changed, 624 insertions(+) create mode 100644 frontend/src/app/components/acceleration-stats/acceleration-stats.component.html create mode 100644 frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss create mode 100644 frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts create mode 100644 frontend/src/app/components/accelerations-list/accelerations-list.component.html create mode 100644 frontend/src/app/components/accelerations-list/accelerations-list.component.scss create mode 100644 frontend/src/app/components/accelerations-list/accelerations-list.component.ts create mode 100644 frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html create mode 100644 frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss create mode 100644 frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html new file mode 100644 index 000000000..e8ded44fc --- /dev/null +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html @@ -0,0 +1,51 @@ +
+
+
+
Transactions
+
+
{{ stats.count }}
+
accelerated
+
+
+
+
Fee delta
+
+
{{ stats.totalFeeDelta | number }} sat
+
paid out of band
+
+
+
+
Success rate
+
+
{{ stats.successRate.toFixed(2) }} %
+
mined in the next block
+
+
+
+
+ + +
+
+
Miners Reward
+
+
+
+
+
+
+
Avg Block Fees
+
+
+
+
+
+
+
Avg Tx Fee
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss new file mode 100644 index 000000000..fcc5564a8 --- /dev/null +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss @@ -0,0 +1,88 @@ +.card-title { + color: #4a68b9; + font-size: 10px; + margin-bottom: 4px; + font-size: 1rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-text { + font-size: 22px; + span { + font-size: 11px; + position: relative; + top: -2px; + display: inline-flex; + } + .green-color { + display: block; + } +} + +.stats-container { + display: flex; + justify-content: space-between; + @media (min-width: 376px) { + flex-direction: row; + } + .item { + max-width: 150px; + margin: 0; + width: -webkit-fill-available; + @media (min-width: 376px) { + margin: 0 auto 0px; + } + &:first-child{ + display: none; + @media (min-width: 485px) { + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + &:last-child { + margin-bottom: 0; + } + .card-text span { + color: #ffffff66; + font-size: 12px; + top: 0px; + } + .fee-text{ + border-bottom: 1px solid #ffffff1c; + width: fit-content; + margin: auto; + line-height: 1.45; + padding: 0px 2px; + } + .fiat { + display: block; + font-size: 14px !important; + } + } +} + +.loading-container{ + min-height: 76px; +} + +.card-text { + .skeleton-loader { + width: 100%; + display: block; + &:first-child { + max-width: 90px; + margin: 15px auto 3px; + } + &:last-child { + margin: 10px auto 3px; + max-width: 55px; + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts new file mode 100644 index 000000000..fa69b1a9d --- /dev/null +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts @@ -0,0 +1,43 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiService } from '../../services/api.service'; +import { StateService } from '../../services/state.service'; + +@Component({ + selector: 'app-acceleration-stats', + templateUrl: './acceleration-stats.component.html', + styleUrls: ['./acceleration-stats.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AccelerationStatsComponent implements OnInit { + public accelerationStats$: Observable; + private lastBlockHeight: number; + + constructor(private apiService: ApiService, private stateService: StateService) { } + + ngOnInit(): void { + this.accelerationStats$ = this.apiService.getAccelerations$().pipe( + switchMap(accelerations => { + let totalFeeDelta = 0; + let totalMined = 0; + let totalCanceled = 0; + for (const acceleration of accelerations) { + if (acceleration.mined) { + totalMined++; + totalFeeDelta += acceleration.feeDelta; + } else if (acceleration.canceled) { + totalCanceled++; + } + } + return of({ + count: totalMined, + totalFeeDelta, + successRate: (totalMined + totalCanceled > 0) ? ((totalMined / (totalMined + totalCanceled)) * 100) : 0.0, + }); + }) + ); + } + + +} diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/accelerations-list/accelerations-list.component.html new file mode 100644 index 000000000..367b6f33d --- /dev/null +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.html @@ -0,0 +1,62 @@ + + +
+

Accelerations

+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
TXIDFeeFee deltaStatus
+ + + + + {{ acceleration.fee | number }} sat + + {{ acceleration.feeDelta | number }} sat + + Mined + Canceled +
+ + + + + + + +
+ + +
+
+
+
+ +
diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/accelerations-list/accelerations-list.component.scss new file mode 100644 index 000000000..9fd978341 --- /dev/null +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.scss @@ -0,0 +1,107 @@ +.spinner-border { + height: 25px; + width: 25px; + margin-top: 13px; +} + +.container-xl { + max-width: 1400px; +} +.container-xl.widget { + padding-left: 0px; + padding-bottom: 0px; +} +.container-xl.legacy { + max-width: 1140px; +} + +.container { + max-width: 100%; +} + +tr, td, th { + border: 0px; + padding-top: 0.65rem !important; + padding-bottom: 0.7rem !important; + + .difference { + margin-left: 0.5em; + + &.positive { + color: rgb(66, 183, 71); + } + &.negative { + color: rgb(183, 66, 66); + } + } +} + +.clear-link { + color: white; +} + +.disabled { + pointer-events: none; + opacity: 0.5; +} + +.progress { + background-color: #2d3348; +} + +.txid { + width: 30%; + @media (max-width: 1100px) { + padding-right: 10px; + } + @media (max-width: 875px) { + display: none; + } + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 30%; +} + +.fee { + width: 25%; +} + +.fee-delta { + width: 25%; +} + +.status { + width: 20% +} + +/* Tooltip text */ +.tooltip-custom { + position: relative; +} + +.tooltip-custom .tooltiptext { + visibility: hidden; + color: #fff; + text-align: center; + padding: 5px 0; + border-radius: 6px; + position: absolute; + z-index: 1; + top: -40px; + left: 0; +} + +/* Show the tooltip text when you mouse over the tooltip container */ +.tooltip-custom:hover .tooltiptext { + visibility: visible; +} + +.scriptmessage { + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; + vertical-align: middle; + max-width: 50vw; + text-align: left; +} diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts new file mode 100644 index 000000000..10c14a2ad --- /dev/null +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; +import { Observable, catchError, of } from 'rxjs'; +import { Acceleration, BlockExtended } from '../../interfaces/node-api.interface'; +import { ApiService } from '../../services/api.service'; +import { StateService } from '../../services/state.service'; +import { WebsocketService } from '../../services/websocket.service'; + +@Component({ + selector: 'app-accelerations-list', + templateUrl: './accelerations-list.component.html', + styleUrls: ['./accelerations-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AccelerationsListComponent implements OnInit { + @Input() widget: boolean = false; + + accelerations$: Observable = undefined; + + isLoading = true; + paginationMaxSize: number; + page = 1; + lastPage = 1; + maxSize = window.innerWidth <= 767.98 ? 3 : 5; + skeletonLines: number[] = []; + + constructor( + private apiService: ApiService, + private websocketService: WebsocketService, + public stateService: StateService, + private cd: ChangeDetectorRef, + ) { + } + + ngOnInit(): void { + if (!this.widget) { + this.websocketService.want(['blocks']); + } + + this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; + this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; + + this.accelerations$ = this.apiService.getAccelerations$().pipe( + catchError((err) => { + this.isLoading = false; + return of([]); + }) + ); + } + + trackByBlock(index: number, block: BlockExtended): number { + return block.height; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html new file mode 100644 index 000000000..13954fb95 --- /dev/null +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -0,0 +1,76 @@ + + +
+ +
+ +
+
+ Acceleration stats  + (144 blocks) +
+
+
+
+ +
+
+
+
+ + +
+
Difficulty Adjustment
+ +
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ + + + + + + +
+
diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss new file mode 100644 index 000000000..4f01f7cad --- /dev/null +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss @@ -0,0 +1,102 @@ +.dashboard-container { + text-align: center; + margin-top: 0.5rem; + .col { + margin-bottom: 1.5rem; + } +} + +.card { + background-color: #1d1f31; +} + +.graph-card { + height: 100%; + @media (min-width: 992px) { + height: 385px; + } +} + +.card-title { + font-size: 1rem; + color: #4a68b9; +} +.card-title > a { + color: #4a68b9; +} + +.card-body.pool-ranking { + padding: 1.25rem 0.25rem 0.75rem 0.25rem; +} +.card-text { + font-size: 22px; +} + +#blockchain-container { + position: relative; + overflow-x: scroll; + overflow-y: hidden; + scrollbar-width: none; + -ms-overflow-style: none; +} + +#blockchain-container::-webkit-scrollbar { + display: none; +} + +.fade-border { + -webkit-mask-image: linear-gradient(to right, transparent 0%, black 10%, black 80%, transparent 100%) +} + +.main-title { + position: relative; + color: #ffffff91; + margin-top: -13px; + font-size: 10px; + text-transform: uppercase; + font-weight: 500; + text-align: center; + padding-bottom: 3px; +} + +.more-padding { + padding: 24px 20px !important; +} + +.card-wrapper { + .card { + height: auto !important; + } + .card-body { + display: flex; + flex: inherit; + text-align: center; + flex-direction: column; + justify-content: space-around; + padding: 22px 20px; + } +} + +.skeleton-loader { + width: 100%; + display: block; + &:first-child { + max-width: 90px; + margin: 15px auto 3px; + } + &:last-child { + margin: 10px auto 3px; + max-width: 55px; + } +} + +.card-text { + font-size: 22px; +} + +.title-link, .title-link:hover, .title-link:focus, .title-link:active { + display: block; + margin-bottom: 10px; + text-decoration: none; + color: inherit; +} diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts new file mode 100644 index 000000000..edae81aa1 --- /dev/null +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { SeoService } from '../../services/seo.service'; +import { WebsocketService } from '../../services/websocket.service'; + +@Component({ + selector: 'app-accelerator-dashboard', + templateUrl: './accelerator-dashboard.component.html', + styleUrls: ['./accelerator-dashboard.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AcceleratorDashboardComponent implements OnInit { + constructor( + private seoService: SeoService, + private websocketService: WebsocketService, + ) { + this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`); + } + + ngOnInit(): void { + this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); + } +} diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index a2160977c..ce5775175 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -19,6 +19,7 @@ import { PoolComponent } from '../components/pool/pool.component'; import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; +import { AcceleratorDashboardComponent } from '../components/accelerator-dashboard/accelerator-dashboard.component'; import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { BlockHealthGraphComponent } from '../components/block-health-graph/block-health-graph.component'; @@ -30,6 +31,7 @@ import { CommonModule } from '@angular/common'; MempoolBlockComponent, MiningDashboardComponent, + AcceleratorDashboardComponent, PoolComponent, PoolRankingComponent, TelevisionComponent, diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 346bcf7f1..e3d9897b2 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -10,6 +10,7 @@ import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-ch import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; +import { AcceleratorDashboardComponent } from '../components/accelerator-dashboard/accelerator-dashboard.component'; import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; import { PoolComponent } from '../components/pool/pool.component'; import { StartComponent } from '../components/start/start.component'; @@ -37,6 +38,17 @@ const routes: Routes = [ }, ] }, + { + path: 'acceleration', + data: { networks: ['bitcoin'] }, + component: StartComponent, + children: [ + { + path: '', + component: AcceleratorDashboardComponent, + } + ] + }, { path: 'mempool-block/:id', data: { networks: ['bitcoin', 'liquid'] }, diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 82327c561..bd897ba99 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -85,6 +85,8 @@ import { GlobalFooterComponent } from './components/global-footer/global-footer. import { AcceleratePreviewComponent } from '../components/accelerate-preview/accelerate-preview.component'; import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component'; import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component'; +import { AccelerationsListComponent } from '../components/accelerations-list/accelerations-list.component'; +import { AccelerationStatsComponent } from '../components/acceleration-stats/acceleration-stats.component'; import { BlockViewComponent } from '../components/block-view/block-view.component'; import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component'; @@ -190,6 +192,8 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir OnlyVsizeDirective, OnlyWeightDirective, MempoolErrorComponent, + AccelerationsListComponent, + AccelerationStatsComponent, ], imports: [ CommonModule, @@ -300,6 +304,8 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir AcceleratePreviewComponent, AccelerateFeeGraphComponent, MempoolErrorComponent, + AccelerationsListComponent, + AccelerationStatsComponent, MempoolBlockOverviewComponent, ClockchainComponent, From f23bcec10d70bff5663175d689934ecb0901bac6 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 21 Jul 2023 14:11:46 +0900 Subject: [PATCH 06/23] Add acceleration fees graph --- .../accelerator-dashboard.component.html | 6 +++--- frontend/src/app/graphs/graphs.module.ts | 2 ++ frontend/src/app/graphs/graphs.routing.module.ts | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 13954fb95..7a6060500 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -24,12 +24,12 @@
- + diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index ce5775175..81a1f0c06 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -3,6 +3,7 @@ import { NgxEchartsModule } from 'ngx-echarts'; import { GraphsRoutingModule } from './graphs.routing.module'; import { SharedModule } from '../shared/shared.module'; +import { AccelerationFeesGraphComponent } from '../components/acceleration-fees-graph/acceleration-fees-graph.component'; import { BlockFeesGraphComponent } from '../components/block-fees-graph/block-fees-graph.component'; import { BlockRewardsGraphComponent } from '../components/block-rewards-graph/block-rewards-graph.component'; import { BlockFeeRatesGraphComponent } from '../components/block-fee-rates-graph/block-fee-rates-graph.component'; @@ -38,6 +39,7 @@ import { CommonModule } from '@angular/common'; StatisticsComponent, GraphsComponent, + AccelerationFeesGraphComponent, BlockFeesGraphComponent, BlockRewardsGraphComponent, BlockFeeRatesGraphComponent, diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index e3d9897b2..a3e84db69 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -17,6 +17,7 @@ import { StartComponent } from '../components/start/start.component'; import { StatisticsComponent } from '../components/statistics/statistics.component'; import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; +import { AccelerationFeesGraphComponent } from '../components/acceleration-fees-graph/acceleration-fees-graph.component'; const routes: Routes = [ { @@ -105,6 +106,11 @@ const routes: Routes = [ data: { networks: ['bitcoin'] }, component: BlockSizesWeightsGraphComponent, }, + { + path: 'acceleration/fees', + data: { networks: ['bitcoin'] }, + component: AccelerationFeesGraphComponent, + }, { path: 'lightning', data: { preload: true, networks: ['bitcoin'] }, From f86645b50cdf1de369f1aec78bdfe50ec1c06834 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 21 Jul 2023 14:13:18 +0900 Subject: [PATCH 07/23] Add latest block accelerations list --- .../accelerations-list.component.scss | 2 +- .../accelerations-list.component.ts | 11 +++- .../accelerator-dashboard.component.html | 29 +++++++++-- .../accelerator-dashboard.component.scss | 29 +++++++++++ .../accelerator-dashboard.component.ts | 51 +++++++++++++++++++ 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/accelerations-list/accelerations-list.component.scss index 9fd978341..d49ac2609 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.scss +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.scss @@ -22,7 +22,7 @@ tr, td, th { border: 0px; padding-top: 0.65rem !important; - padding-bottom: 0.7rem !important; + padding-bottom: 0.8rem !important; .difference { margin-left: 0.5em; diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts index 10c14a2ad..0b1cdd3ba 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { Observable, catchError, of } from 'rxjs'; +import { Observable, catchError, of, switchMap } from 'rxjs'; import { Acceleration, BlockExtended } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -39,7 +39,14 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - this.accelerations$ = this.apiService.getAccelerations$().pipe( + this.accelerations$ = this.apiService.getAccelerations$('24h').pipe( + switchMap(accelerations => { + if (this.widget) { + return of(accelerations.slice(0, 6)); + } else { + return of(accelerations); + } + }), catchError((err) => { this.isLoading = false; return of([]); diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 7a6060500..71baeceb9 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -58,16 +58,37 @@
- +
- -
Adjustments
+
+
Latest blocks
 
- + + + + + + + + + + + + + + + +
HeightPoolMedian feeAccelerations
{{ block.height }} + + + {{ block.extras.pool.name }} + + {{ block.accelerationCount | number }}
diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss index 4f01f7cad..1171846fe 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss @@ -100,3 +100,32 @@ text-decoration: none; color: inherit; } + +.lastest-blocks-table { + width: 100%; + text-align: left; + tr, td, th { + border: 0px; + padding-top: 0.65rem !important; + padding-bottom: 0.8rem !important; + } + .table-cell-height { + width: 25%; + } + .table-cell-fee { + width: 25%; + text-align: right; + } + .table-cell-pool { + text-align: left; + width: 30%; + + .pool-name { + margin-left: 1em; + } + } + .table-cell-acceleration-count { + text-align: right; + width: 20%; + } +} \ No newline at end of file diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts index edae81aa1..499984d2e 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,6 +1,14 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { SeoService } from '../../services/seo.service'; import { WebsocketService } from '../../services/websocket.service'; +import { Acceleration, BlockExtended } from '../../interfaces/node-api.interface'; +import { StateService } from '../../services/state.service'; +import { Observable, catchError, combineLatest, of, switchMap } from 'rxjs'; +import { ApiService } from '../../services/api.service'; + +interface AccelerationBlock extends BlockExtended { + accelerationCount: number, +} @Component({ selector: 'app-accelerator-dashboard', @@ -9,14 +17,57 @@ import { WebsocketService } from '../../services/websocket.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AcceleratorDashboardComponent implements OnInit { + blocks$: Observable; + + loadingBlocks: boolean = true; + constructor( private seoService: SeoService, private websocketService: WebsocketService, + private apiService: ApiService, + private stateService: StateService, ) { this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`); } ngOnInit(): void { this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); + + this.blocks$ = combineLatest([ + this.stateService.blocks$.pipe( + switchMap((blocks) => { + if (this.stateService.env.MINING_DASHBOARD === true) { + for (const block of blocks) { + // @ts-ignore: Need to add an extra field for the template + block.extras.pool.logo = `/resources/mining-pools/` + + block.extras.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'; + } + } + return of(blocks as AccelerationBlock[]); + }) + ), + this.apiService.getAccelerations$('24h').pipe( + catchError((err) => { + this.loadingBlocks = false; + return of([]); + }) + ) + ]).pipe( + switchMap(([blocks, accelerations]) => { + const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {}; + for (const acceleration of accelerations) { + if (acceleration.mined && !accelerationsByBlock[acceleration.block_hash]) { + accelerationsByBlock[acceleration.block_hash] = []; + } + if (acceleration.mined) { + accelerationsByBlock[acceleration.block_hash].push(acceleration); + } + } + return of(blocks.slice(0, 6).map(block => { + block.accelerationCount = (accelerationsByBlock[block.id] || []).length; + return block; + })); + }) + ); } } From 14ad0c8ecb7d20a329c8f50b1a88f56ee8fc0a85 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 21 Jul 2023 14:13:36 +0900 Subject: [PATCH 08/23] Add more acceleration stats --- .../acceleration-stats.component.html | 6 +++--- .../acceleration-stats.component.ts | 5 +++-- .../accelerator-dashboard.component.html | 18 ++++++++++++++---- .../block-fee-rates-graph.component.ts | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html index e8ded44fc..a2f201eb4 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html @@ -27,21 +27,21 @@
-
Miners Reward
+
Transactions
-
Avg Block Fees
+
Fee delta
-
Avg Tx Fee
+
Success rate
diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts index fa69b1a9d..2d6355940 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; @@ -11,13 +11,14 @@ import { StateService } from '../../services/state.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccelerationStatsComponent implements OnInit { + @Input() timespan: '24h' | '1w' = '24h'; public accelerationStats$: Observable; private lastBlockHeight: number; constructor(private apiService: ApiService, private stateService: StateService) { } ngOnInit(): void { - this.accelerationStats$ = this.apiService.getAccelerations$().pipe( + this.accelerationStats$ = this.apiService.getAccelerations$(this.timespan).pipe( switchMap(accelerations => { let totalFeeDelta = 0; let totalMined = 0; diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 71baeceb9..530cbbb10 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -4,6 +4,7 @@
+
Acceleration stats  @@ -12,16 +13,25 @@
- +
- +
-
Difficulty Adjustment
- +
+ Acceleration stats  + (1008 blocks) +
+
+
+
+ +
+
+
diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 19c51527f..0de1e8474 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -210,7 +210,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { top: this.widget ? 20 : (this.isMobile() ? 10 : 50), }, tooltip: { - show: !this.isMobile() && !this.widget, + show: !this.isMobile(), trigger: 'axis', axisPointer: { type: 'line' From 6cf40489e50a3973b4df3a07a38b21c4b280b335 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 21 Jul 2023 14:24:36 +0900 Subject: [PATCH 09/23] tidy up acceleration dashboard --- .../acceleration-stats/acceleration-stats.component.ts | 6 +++--- .../block-fee-rates-graph.component.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts index 2d6355940..8c83c9d9c 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; @Component({ selector: 'app-acceleration-stats', @@ -13,9 +12,10 @@ import { StateService } from '../../services/state.service'; export class AccelerationStatsComponent implements OnInit { @Input() timespan: '24h' | '1w' = '24h'; public accelerationStats$: Observable; - private lastBlockHeight: number; - constructor(private apiService: ApiService, private stateService: StateService) { } + constructor( + private apiService: ApiService, + ) { } ngOnInit(): void { this.accelerationStats$ = this.apiService.getAccelerations$(this.timespan).pipe( diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 0de1e8474..7f73a197a 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnInit } from '@angular/core'; import { EChartsOption, graphic } from 'echarts'; -import { Observable, Subscription, combineLatest, of } from 'rxjs'; +import { Observable, combineLatest, of } from 'rxjs'; import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../services/api.service'; import { SeoService } from '../../services/seo.service'; @@ -158,11 +158,11 @@ export class BlockFeeRatesGraphComponent implements OnInit { avg += seriesData['Median'][i - y][1]; } avg /= maResolution; - medianMa.push([seriesData['Median'][i][0], avg]); + medianMa.push([seriesData['Median'][i][0], avg, seriesData['Median'][i][2]]); } series.push({ zlevel: 1, - name: 'MA', + name: 'Moving average', data: medianMa, type: 'line', showSymbol: false, @@ -231,9 +231,9 @@ export class BlockFeeRatesGraphComponent implements OnInit { for (const rate of data.reverse()) { if (weightMode) { - tooltip += `${rate.marker} ${rate.seriesName}: ${rate.data[1] / 4} sats/WU
`; + tooltip += `${rate.marker} ${rate.seriesName}: ${(rate.data[1] / 4).toFixed(2)} sats/WU
`; } else { - tooltip += `${rate.marker} ${rate.seriesName}: ${rate.data[1]} sats/vByte
`; + tooltip += `${rate.marker} ${rate.seriesName}: ${rate.data[1].toFixed(2)} sats/vByte
`; } } From f409c6712531831f257e156aba0ed18447c042fe Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 22 Jul 2023 15:30:48 +0900 Subject: [PATCH 10/23] update acceleration dashboard to match actual API format --- .../acceleration-stats/acceleration-stats.component.ts | 6 +++--- .../accelerations-list/accelerations-list.component.html | 6 +++--- .../accelerations-list/accelerations-list.component.ts | 2 +- .../accelerator-dashboard.component.ts | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts index 8c83c9d9c..ab423a216 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts @@ -18,16 +18,16 @@ export class AccelerationStatsComponent implements OnInit { ) { } ngOnInit(): void { - this.accelerationStats$ = this.apiService.getAccelerations$(this.timespan).pipe( + this.accelerationStats$ = this.apiService.getAccelerationHistory$(this.timespan).pipe( switchMap(accelerations => { let totalFeeDelta = 0; let totalMined = 0; let totalCanceled = 0; for (const acceleration of accelerations) { - if (acceleration.mined) { + if (acceleration.status === 'completed' || acceleration.status === 'mined') { totalMined++; totalFeeDelta += acceleration.feeDelta; - } else if (acceleration.canceled) { + } else if (acceleration.status === 'failed') { totalCanceled++; } } diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/accelerations-list/accelerations-list.component.html index 367b6f33d..4b37f28c0 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.html @@ -22,14 +22,14 @@ - {{ acceleration.fee | number }} sat + {{ acceleration.feePaid | number }} sat {{ acceleration.feeDelta | number }} sat - Mined - Canceled + Mined + Canceled diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts index 0b1cdd3ba..70dbd03f0 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts @@ -39,7 +39,7 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - this.accelerations$ = this.apiService.getAccelerations$('24h').pipe( + this.accelerations$ = this.apiService.getAccelerations$().pipe( switchMap(accelerations => { if (this.widget) { return of(accelerations.slice(0, 6)); diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts index 499984d2e..aaf7be825 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts @@ -46,7 +46,7 @@ export class AcceleratorDashboardComponent implements OnInit { return of(blocks as AccelerationBlock[]); }) ), - this.apiService.getAccelerations$('24h').pipe( + this.apiService.getAccelerationHistory$('24h').pipe( catchError((err) => { this.loadingBlocks = false; return of([]); @@ -56,11 +56,11 @@ export class AcceleratorDashboardComponent implements OnInit { switchMap(([blocks, accelerations]) => { const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {}; for (const acceleration of accelerations) { - if (acceleration.mined && !accelerationsByBlock[acceleration.block_hash]) { - accelerationsByBlock[acceleration.block_hash] = []; + if (acceleration.mined && !accelerationsByBlock[acceleration.blockHash]) { + accelerationsByBlock[acceleration.blockHash] = []; } if (acceleration.mined) { - accelerationsByBlock[acceleration.block_hash].push(acceleration); + accelerationsByBlock[acceleration.blockHash].push(acceleration); } } return of(blocks.slice(0, 6).map(block => { From 7019e6ec0376b877a0b9480b0ea134b67b8d7440 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 22 Jul 2023 15:43:52 +0900 Subject: [PATCH 11/23] Add acceleration fees graph --- .../acceleration-fees-graph.component.html | 71 ++++ .../acceleration-fees-graph.component.scss | 119 ++++++ .../acceleration-fees-graph.component.ts | 354 ++++++++++++++++++ 3 files changed, 544 insertions(+) create mode 100644 frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html create mode 100644 frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss create mode 100644 frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html new file mode 100644 index 000000000..2cf3fbc0b --- /dev/null +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html @@ -0,0 +1,71 @@ + + +
+
+
+ Acceleration Fees + +
+ +
+
+ + + + +
+
+
+ +
+
+
+
???
+

+ ??? +

+
+
+
???
+

+ ??? +

+
+
+
+ +
+
+
+
+
+ +
+ + +
+
+
???
+

+ +

+
+
+
???
+

+ +

+
+
+
diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss new file mode 100644 index 000000000..2ffcc6374 --- /dev/null +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss @@ -0,0 +1,119 @@ +.card-header { + border-bottom: 0; + font-size: 18px; + @media (min-width: 465px) { + font-size: 20px; + } + @media (min-width: 992px) { + height: 40px; + } +} + +.main-title { + position: relative; + color: #ffffff91; + margin-top: -13px; + font-size: 10px; + text-transform: uppercase; + font-weight: 500; + text-align: center; + padding-bottom: 3px; +} + +.full-container { + display: flex; + flex-direction: column; + padding: 0px 15px; + width: 100%; + height: calc(100vh - 250px); + @media (min-width: 992px) { + height: calc(100vh - 150px); + } +} + +.chart { + display: flex; + flex: 1; + width: 100%; + padding-bottom: 20px; + padding-right: 10px; + @media (max-width: 992px) { + padding-bottom: 25px; + } + @media (max-width: 829px) { + padding-bottom: 50px; + } + @media (max-width: 767px) { + padding-bottom: 25px; + } + @media (max-width: 629px) { + padding-bottom: 55px; + } + @media (max-width: 567px) { + padding-bottom: 55px; + } +} +.chart-widget { + width: 100%; + height: 100%; + max-height: 238px; +} + +.acceleration-fees { + min-height: 56px; + display: block; + @media (min-width: 485px) { + display: flex; + flex-direction: row; + } + h5 { + margin-bottom: 10px; + } + .item { + width: 50%; + display: inline-block; + margin: 0px auto 20px; + &:nth-child(2) { + order: 2; + @media (min-width: 485px) { + order: 3; + } + } + &:nth-child(3) { + order: 3; + @media (min-width: 485px) { + order: 2; + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + .card-title { + font-size: 1rem; + color: #4a68b9; + } + .card-text { + font-size: 18px; + span { + color: #ffffff66; + font-size: 12px; + } + } + } +} + +.skeleton-loader { + width: 100%; + display: block; + max-width: 80px; + margin: 15px auto 3px; +} + +.disabled { + pointer-events: none; + opacity: 0.5; +} \ No newline at end of file diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts new file mode 100644 index 000000000..f666c8f31 --- /dev/null +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -0,0 +1,354 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; +import { EChartsOption, graphic } from 'echarts'; +import { Observable, combineLatest } from 'rxjs'; +import { map, startWith, switchMap, tap } from 'rxjs/operators'; +import { ApiService } from '../../services/api.service'; +import { SeoService } from '../../services/seo.service'; +import { formatNumber } from '@angular/common'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { download, formatterXAxis } from '../../shared/graphs.utils'; +import { StorageService } from '../../services/storage.service'; +import { MiningService } from '../../services/mining.service'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'app-acceleration-fees-graph', + templateUrl: './acceleration-fees-graph.component.html', + styleUrls: ['./acceleration-fees-graph.component.scss'], + styles: [` + .loadingGraphs { + position: absolute; + top: 50%; + left: calc(50% - 15px); + z-index: 100; + } + `], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AccelerationFeesGraphComponent implements OnInit { + @Input() widget: boolean = false; + @Input() right: number | string = 45; + @Input() left: number | string = 75; + + miningWindowPreference: string; + radioGroupForm: UntypedFormGroup; + + chartOptions: EChartsOption = {}; + chartInitOptions = { + renderer: 'svg', + }; + + statsObservable$: Observable; + isLoading = true; + formatNumber = formatNumber; + timespan = ''; + chartInstance: any = undefined; + + currency: string; + + constructor( + @Inject(LOCALE_ID) public locale: string, + private seoService: SeoService, + private apiService: ApiService, + private formBuilder: UntypedFormBuilder, + private storageService: StorageService, + private miningService: MiningService, + private route: ActivatedRoute, + private cd: ChangeDetectorRef, + ) { + this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); + this.radioGroupForm.controls.dateSpan.setValue('1y'); + this.currency = 'USD'; + } + + ngOnInit(): void { + this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Acceleration Fees`); + if (this.widget) { + this.miningWindowPreference = '1w'; + this.isLoading = true; + this.timespan = this.miningWindowPreference; + this.statsObservable$ = combineLatest([ + this.apiService.getAccelerationHistory$(this.miningWindowPreference), + this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), + ]).pipe( + tap(([accelerations, blockFeesResponse]) => { + console.log(accelerations, blockFeesResponse.body); + this.prepareChartOptions(accelerations, blockFeesResponse.body); + this.isLoading = false; + }), + map(([accelerations, blockFeesResponse]) => { + return { + + }; + }), + ); + } else { + this.miningWindowPreference = this.miningService.getDefaultTimespan('1w'); + this.radioGroupForm = this.formBuilder.group({ dateSpan: this.miningWindowPreference }); + this.radioGroupForm.controls.dateSpan.setValue(this.miningWindowPreference); + this.route.fragment.subscribe((fragment) => { + if (['24h', '3d', '1w', '1m'].indexOf(fragment) > -1) { + this.radioGroupForm.controls.dateSpan.setValue(fragment, { emitEvent: false }); + } + }); + this.statsObservable$ = combineLatest([ + this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.radioGroupForm.controls.dateSpan.value), + switchMap((timespan) => { + this.isLoading = true; + this.storageService.setValue('miningWindowPreference', timespan); + this.timespan = timespan; + return this.apiService.getAccelerations$(); + }) + ), + this.radioGroupForm.get('dateSpan').valueChanges.pipe( + startWith(this.radioGroupForm.controls.dateSpan.value), + switchMap((timespan) => { + return this.apiService.getHistoricalBlockFees$(timespan); + }) + ) + ]).pipe( + tap(([accelerations, blockFeesResponse]) => { + this.prepareChartOptions(accelerations, blockFeesResponse.body); + this.isLoading = false; + this.cd.markForCheck(); + }), + map(([accelerations, blockFeesResponse]) => { + return { + + }; + }), + ); + } + } + + prepareChartOptions(accelerations, blockFees) { + let title: object; + + const blockAccelerations = {}; + + for (const acceleration of accelerations) { + if (acceleration.mined) { + if (!blockAccelerations[acceleration.block_height]) { + blockAccelerations[acceleration.block_height] = []; + } + blockAccelerations[acceleration.block_height].push(acceleration); + } + } + + let last = null; + const data = []; + for (const val of blockFees) { + if (last == null) { + last = val.avgHeight; + } + let totalFeeDelta = 0; + let totalCount = 0; + while (last <= val.avgHeight) { + totalFeeDelta += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feeDelta, 0); + totalCount += (blockAccelerations[last] || []).length; + last++; + } + data.push({ + ...val, + feeDelta: totalFeeDelta, + accelerations: totalCount, + }); + } + + this.chartOptions = { + title: title, + color: [ + new graphic.LinearGradient(0, 0, 0, 0.65, [ + { offset: 0, color: '#F4511E' }, + { offset: 0.25, color: '#FB8C00' }, + { offset: 0.5, color: '#FFB300' }, + { offset: 0.75, color: '#FDD835' }, + { offset: 1, color: '#7CB342' } + ]), + '#1E88E5', + ], + animation: false, + grid: { + right: this.right, + left: this.left, + bottom: this.widget ? 30 : 80, + top: this.widget ? 20 : (this.isMobile() ? 10 : 50), + }, + tooltip: { + show: !this.isMobile(), + trigger: 'axis', + axisPointer: { + type: 'line' + }, + backgroundColor: 'rgba(17, 19, 31, 1)', + borderRadius: 4, + shadowColor: 'rgba(0, 0, 0, 0.5)', + textStyle: { + color: '#b1b1b1', + align: 'left', + }, + borderColor: '#000', + formatter: function (data) { + if (data.length <= 0) { + return ''; + } + let tooltip = ` + ${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}
`; + + for (const tick of data) { + if (tick.seriesIndex === 0) { + tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')} sats
`; + } else if (tick.seriesIndex === 1) { + tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')}
`; + } + } + + if (['24h', '3d'].includes(this.timespan)) { + tooltip += `` + $localize`At block: ${data[0].data[2]}` + ``; + } else { + tooltip += `` + $localize`Around block: ${data[0].data[2]}` + ``; + } + + return tooltip; + }.bind(this) + }, + xAxis: data.length === 0 ? undefined : + { + type: 'time', + splitNumber: this.isMobile() ? 5 : 10, + axisLabel: { + hideOverlap: true, + } + }, + legend: (this.widget || data.length === 0) ? undefined : { + data: [ + { + name: 'Total fee delta', + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + }, + { + name: 'Total accelerations', + inactiveColor: 'rgb(110, 112, 121)', + textStyle: { + color: 'white', + }, + icon: 'roundRect', + }, + ], + }, + yAxis: data.length === 0 ? undefined : [ + { + type: 'value', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: (val) => { + return `${val / 100_000_000} BTC`; + } + }, + splitLine: { + lineStyle: { + type: 'dotted', + color: '#ffffff66', + opacity: 0.25, + } + }, + }, + { + type: 'value', + position: 'right', + axisLabel: { + color: 'rgb(110, 112, 121)', + formatter: function(val) { + return `${val}`; + }.bind(this) + }, + splitLine: { + show: false, + }, + }, + ], + series: data.length === 0 ? undefined : [ + { + legendHoverLink: false, + zlevel: 1, + yAxisIndex: 0, + name: 'Total fee delta', + data: data.map(block => [block.timestamp * 1000, block.feeDelta, block.avgHeight]), + type: 'line', + smooth: 0.25, + symbol: 'none', + lineStyle: { + width: 1, + opacity: 1, + } + }, + { + legendHoverLink: false, + zlevel: 0, + yAxisIndex: 1, + name: 'Total accelerations', + data: data.map(block => [block.timestamp * 1000, block.accelerations, block.avgHeight]), + type: 'bar', + barWidth: '100%', + large: true, + }, + ], + dataZoom: (this.widget || data.length === 0 )? undefined : [{ + type: 'inside', + realtime: true, + zoomLock: true, + maxSpan: 100, + minSpan: 5, + moveOnMouseMove: false, + }, { + showDetail: false, + show: true, + type: 'slider', + brushSelect: false, + realtime: true, + left: 20, + right: 15, + selectedDataBackground: { + lineStyle: { + color: '#fff', + opacity: 0.45, + }, + areaStyle: { + opacity: 0, + } + }, + }], + }; + } + + onChartInit(ec) { + this.chartInstance = ec; + } + + isMobile() { + return (window.innerWidth <= 767.98); + } + + onSaveChart() { + // @ts-ignore + const prevBottom = this.chartOptions.grid.bottom; + const now = new Date(); + // @ts-ignore + this.chartOptions.grid.bottom = 40; + this.chartOptions.backgroundColor = '#11131f'; + this.chartInstance.setOption(this.chartOptions); + download(this.chartInstance.getDataURL({ + pixelRatio: 2, + excludeComponents: ['dataZoom'], + }), `acceleration-fees-${this.timespan}-${Math.round(now.getTime() / 1000)}.svg`); + // @ts-ignore + this.chartOptions.grid.bottom = prevBottom; + this.chartOptions.backgroundColor = 'none'; + this.chartInstance.setOption(this.chartOptions); + } +} From b0e73466fa57bd79a5058ac7baa2c9abb0cf56da Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 26 Jul 2023 15:22:24 +0900 Subject: [PATCH 12/23] Show mining blockchain blocks on accelerator dashboard --- .../components/blockchain-blocks/blockchain-blocks.component.ts | 2 +- .../app/components/mempool-blocks/mempool-blocks.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts index ba066d10a..0da8ca7b5 100644 --- a/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts +++ b/frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts @@ -79,7 +79,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy { } enabledMiningInfoIfNeeded(url) { - this.showMiningInfo = url.indexOf('/mining') !== -1; + this.showMiningInfo = url.includes('/mining') || url.includes('/acceleration'); this.cd.markForCheck(); // Need to update the view asap } diff --git a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts index 0ddbbd4b7..61e62f642 100644 --- a/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts +++ b/frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts @@ -90,7 +90,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy { ) { } enabledMiningInfoIfNeeded(url) { - this.showMiningInfo = url.indexOf('/mining') !== -1; + this.showMiningInfo = url.includes('/mining') || url.includes('/acceleration'); this.cd.markForCheck(); // Need to update the view asap } From f26b630aabd062ec426df16635190db8624753b0 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 30 Aug 2023 22:24:27 +0900 Subject: [PATCH 13/23] Fix acceleration dashboard merge issues --- .../acceleration-fees-graph.component.ts | 10 +++++----- .../accelerations-list.component.html | 1 + .../accelerations-list/accelerations-list.component.ts | 6 +++--- .../accelerator-dashboard.component.ts | 4 ++-- frontend/src/app/graphs/graphs.routing.module.ts | 6 ++++++ 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts index f666c8f31..bba0cbc3e 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -98,7 +98,7 @@ export class AccelerationFeesGraphComponent implements OnInit { this.isLoading = true; this.storageService.setValue('miningWindowPreference', timespan); this.timespan = timespan; - return this.apiService.getAccelerations$(); + return this.apiService.getAccelerationHistory$(); }) ), this.radioGroupForm.get('dateSpan').valueChanges.pipe( @@ -128,11 +128,11 @@ export class AccelerationFeesGraphComponent implements OnInit { const blockAccelerations = {}; for (const acceleration of accelerations) { - if (acceleration.mined) { - if (!blockAccelerations[acceleration.block_height]) { - blockAccelerations[acceleration.block_height] = []; + if (acceleration.status === 'mined' || acceleration.status === 'completed') { + if (!blockAccelerations[acceleration.blockHeight]) { + blockAccelerations[acceleration.blockHeight] = []; } - blockAccelerations[acceleration.block_height].push(acceleration); + blockAccelerations[acceleration.blockHeight].push(acceleration); } } diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/accelerations-list/accelerations-list.component.html index 4b37f28c0..80f1c1dbf 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.html @@ -28,6 +28,7 @@ {{ acceleration.feeDelta | number }} sat + Pending Mined Canceled diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts index 70dbd03f0..efc900bd5 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/accelerations-list/accelerations-list.component.ts @@ -39,12 +39,12 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - this.accelerations$ = this.apiService.getAccelerations$().pipe( + this.accelerations$ = this.apiService.getAccelerationHistory$().pipe( switchMap(accelerations => { if (this.widget) { - return of(accelerations.slice(0, 6)); + return of(accelerations.slice(-6).reverse()); } else { - return of(accelerations); + return of(accelerations.reverse()); } }), catchError((err) => { diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts index aaf7be825..8257c3306 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts @@ -56,10 +56,10 @@ export class AcceleratorDashboardComponent implements OnInit { switchMap(([blocks, accelerations]) => { const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {}; for (const acceleration of accelerations) { - if (acceleration.mined && !accelerationsByBlock[acceleration.blockHash]) { + if (['mined', 'completed'].includes(acceleration.status) && !accelerationsByBlock[acceleration.blockHash]) { accelerationsByBlock[acceleration.blockHash] = []; } - if (acceleration.mined) { + if (['mined', 'completed'].includes(acceleration.status)) { accelerationsByBlock[acceleration.blockHash].push(acceleration); } } diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index a3e84db69..12eeb6e4c 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -18,6 +18,7 @@ import { StatisticsComponent } from '../components/statistics/statistics.compone import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; import { AccelerationFeesGraphComponent } from '../components/acceleration-fees-graph/acceleration-fees-graph.component'; +import { AccelerationsListComponent } from '../components/accelerations-list/accelerations-list.component'; const routes: Routes = [ { @@ -50,6 +51,11 @@ const routes: Routes = [ } ] }, + { + path: 'acceleration-list', + data: { networks: ['bitcoin'] }, + component: AccelerationsListComponent, + }, { path: 'mempool-block/:id', data: { networks: ['bitcoin', 'liquid'] }, From 472a76560f8edf6874b692064c8b3d8114a9e615 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Wed, 30 Aug 2023 22:25:33 +0900 Subject: [PATCH 14/23] Tidy acceleration dashboard, add missing fields --- .../acceleration-fees-graph.component.html | 42 +++++------ .../acceleration-fees-graph.component.ts | 70 ++++++++++++------- .../block-fee-rates-graph.component.html | 38 +++++----- .../block-fee-rates-graph.component.ts | 14 ++++ 4 files changed, 97 insertions(+), 67 deletions(-) diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html index 2cf3fbc0b..6a56ac6ca 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html @@ -28,17 +28,17 @@
-
-
-
???
+
+
+
Avg Fee Delta (24h)

- ??? + {{ stats.avgFeeDelta | number }} sat

-
-
???
+
+
Avg Fee Delta (1w)

- ??? + {{ stats.avgFeeDelta | number }} sat

@@ -53,19 +53,19 @@
- -
-
-
???
-

- -

-
-
-
???
-

- -

-
+ +
+
Avg Fee Delta (24h)
+

+ +

+
+
+ +
+
Avg Fee Delta (1w)
+

+ +

diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts index bba0cbc3e..ca3dde69c 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -38,6 +38,7 @@ export class AccelerationFeesGraphComponent implements OnInit { renderer: 'svg', }; + hrStatsObservable$: Observable; statsObservable$: Observable; isLoading = true; formatNumber = formatNumber; @@ -67,18 +68,26 @@ export class AccelerationFeesGraphComponent implements OnInit { this.miningWindowPreference = '1w'; this.isLoading = true; this.timespan = this.miningWindowPreference; + + this.hrStatsObservable$ = this.apiService.getAccelerationHistory$('24h').pipe( + map((accelerations) => { + return { + avgFeeDelta: accelerations.filter(acc => acc.status === 'mined' || acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length + }; + }) + ); + this.statsObservable$ = combineLatest([ this.apiService.getAccelerationHistory$(this.miningWindowPreference), this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), ]).pipe( tap(([accelerations, blockFeesResponse]) => { - console.log(accelerations, blockFeesResponse.body); this.prepareChartOptions(accelerations, blockFeesResponse.body); this.isLoading = false; }), map(([accelerations, blockFeesResponse]) => { return { - + avgFeeDelta: accelerations.filter(acc => acc.status === 'mined' || acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length }; }), ); @@ -112,12 +121,7 @@ export class AccelerationFeesGraphComponent implements OnInit { this.prepareChartOptions(accelerations, blockFeesResponse.body); this.isLoading = false; this.cd.markForCheck(); - }), - map(([accelerations, blockFeesResponse]) => { - return { - - }; - }), + }) ); } } @@ -159,6 +163,7 @@ export class AccelerationFeesGraphComponent implements OnInit { this.chartOptions = { title: title, color: [ + '#1E88E5', new graphic.LinearGradient(0, 0, 0, 0.65, [ { offset: 0, color: '#F4511E' }, { offset: 0.25, color: '#FB8C00' }, @@ -166,7 +171,6 @@ export class AccelerationFeesGraphComponent implements OnInit { { offset: 0.75, color: '#FDD835' }, { offset: 1, color: '#7CB342' } ]), - '#1E88E5', ], animation: false, grid: { @@ -197,9 +201,9 @@ export class AccelerationFeesGraphComponent implements OnInit { ${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}
`; for (const tick of data) { - if (tick.seriesIndex === 0) { + if (tick.seriesIndex === 1) { tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')} sats
`; - } else if (tick.seriesIndex === 1) { + } else if (tick.seriesIndex === 0) { tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')}
`; } } @@ -224,17 +228,17 @@ export class AccelerationFeesGraphComponent implements OnInit { legend: (this.widget || data.length === 0) ? undefined : { data: [ { - name: 'Total fee delta', + name: 'Total accelerations', inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', }, { - name: 'Total accelerations', + name: 'Total fee delta', inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', @@ -247,7 +251,11 @@ export class AccelerationFeesGraphComponent implements OnInit { axisLabel: { color: 'rgb(110, 112, 121)', formatter: (val) => { - return `${val / 100_000_000} BTC`; + if (val >= 1_000_000) { + return `${(val / 100_000_000).toPrecision(5)} BTC`; + } else { + return `${val} sats`; + } } }, splitLine: { @@ -273,6 +281,24 @@ export class AccelerationFeesGraphComponent implements OnInit { }, ], series: data.length === 0 ? undefined : [ + { + legendHoverLink: false, + zlevel: 0, + yAxisIndex: 1, + name: 'Total accelerations', + data: data.map(block => [block.timestamp * 1000, block.accelerations, block.avgHeight]), + type: 'line', + symbol: 'none', + areaStyle: { + color: '#1E88E5', + opacity: 0.5 + }, + lineStyle: { + width: 1, + opacity: 1, + }, + step: 'middle', + }, { legendHoverLink: false, zlevel: 1, @@ -283,20 +309,10 @@ export class AccelerationFeesGraphComponent implements OnInit { smooth: 0.25, symbol: 'none', lineStyle: { - width: 1, + width: 2, opacity: 1, } }, - { - legendHoverLink: false, - zlevel: 0, - yAxisIndex: 1, - name: 'Total accelerations', - data: data.map(block => [block.timestamp * 1000, block.accelerations, block.avgHeight]), - type: 'bar', - barWidth: '100%', - large: true, - }, ], dataZoom: (this.widget || data.length === 0 )? undefined : [{ type: 'inside', diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html index f5eb5d1cf..8676b8a44 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.html @@ -46,17 +46,17 @@
-
-
-
Avg Block Fee (1m)
+
+
+
Avg Block Fee (24h)

-
-
???
+
+
Avg Block Fee (1m)

- ??? +

@@ -71,19 +71,19 @@
+ +
+
Avg Block Fee (24h)
+

+ +

+
+
-
-
-
Avg Block Fee
-

- -

-
-
-
???
-

- -

-
+
+
Avg Block Fee (1m)
+

+ +

\ No newline at end of file diff --git a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts index 7f73a197a..0283b2d00 100644 --- a/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts +++ b/frontend/src/app/components/block-fee-rates-graph/block-fee-rates-graph.component.ts @@ -41,6 +41,7 @@ export class BlockFeeRatesGraphComponent implements OnInit { renderer: 'svg', }; + hrStatsObservable$: Observable; statsObservable$: Observable; isLoading = true; formatNumber = formatNumber; @@ -85,6 +86,19 @@ export class BlockFeeRatesGraphComponent implements OnInit { }); } + this.hrStatsObservable$ = combineLatest([ + this.apiService.getHistoricalBlockFeeRates$('24h'), + this.stateService.rateUnits$ + ]).pipe( + map(([response, rateUnits]) => { + return { + blockCount: parseInt(response.headers.get('x-total-count'), 10), + avgMedianRate: response.body.length ? response.body.reduce((acc, rate) => acc + rate.avgFee_50, 0) / response.body.length : 0, + }; + }), + share(), + ); + this.statsObservable$ = combineLatest([ this.widget ? of(this.miningWindowPreference) : this.radioGroupForm.get('dateSpan').valueChanges.pipe(startWith(this.radioGroupForm.controls.dateSpan.value)), this.stateService.rateUnits$ From 2efd4743576f396aa2ade6f72f7a24f656c247ee Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 3 Dec 2023 08:16:20 +0000 Subject: [PATCH 15/23] Fix accelerator dashboard widget page link css --- .../accelerator-dashboard.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 530cbbb10..039252e3a 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -61,7 +61,7 @@
Latest accelerations
  - +
@@ -75,7 +75,7 @@
Latest blocks
  - +
From 5a55ba2d28585e6c4345738f77b3cbd1f7c2d7f6 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 3 Dec 2023 08:17:29 +0000 Subject: [PATCH 16/23] Accelerator dashboard pending widget, fee delta fiat values --- .../acceleration-stats.component.html | 24 ++++++-- .../acceleration-stats.component.ts | 60 ++++++++++++------- .../accelerator-dashboard.component.html | 5 +- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html index a2f201eb4..2180a1ad6 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html @@ -10,17 +10,26 @@
Fee delta
-
{{ stats.totalFeeDelta | number }} sat
-
paid out of band
+
{{ stats.totalFeeDelta / 100_000_000 | amountShortener: 4 }} BTC
+ + +
-
+
Success rate
{{ stats.successRate.toFixed(2) }} %
mined in the next block
+
+
Total vsize
+
+
+
{{ (stats.totalVsize / 1_000_000).toFixed(2) }}% of next block
+
+
@@ -40,12 +49,19 @@
-
+
Success rate
+
+
Total vsize
+
+
+
+
+
diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts index ab423a216..4d06a31e2 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts @@ -10,7 +10,7 @@ import { ApiService } from '../../services/api.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccelerationStatsComponent implements OnInit { - @Input() timespan: '24h' | '1w' = '24h'; + @Input() timespan: 'now' | '24h' | '1w' = '24h'; public accelerationStats$: Observable; constructor( @@ -18,26 +18,46 @@ export class AccelerationStatsComponent implements OnInit { ) { } ngOnInit(): void { - this.accelerationStats$ = this.apiService.getAccelerationHistory$(this.timespan).pipe( - switchMap(accelerations => { - let totalFeeDelta = 0; - let totalMined = 0; - let totalCanceled = 0; - for (const acceleration of accelerations) { - if (acceleration.status === 'completed' || acceleration.status === 'mined') { - totalMined++; - totalFeeDelta += acceleration.feeDelta; - } else if (acceleration.status === 'failed') { - totalCanceled++; + if (this.timespan === 'now') { + this.accelerationStats$ = this.apiService.getAccelerations$().pipe( + switchMap(accelerations => { + let totalAccelerations = 0; + let totalFeeDelta = 0; + let totalVsize = 0; + for (const acceleration of accelerations) { + totalAccelerations++; + totalFeeDelta += acceleration.feeDelta || 0; + totalVsize += acceleration.effectiveVsize || 0; } - } - return of({ - count: totalMined, - totalFeeDelta, - successRate: (totalMined + totalCanceled > 0) ? ((totalMined / (totalMined + totalCanceled)) * 100) : 0.0, - }); - }) - ); + return of({ + count: totalAccelerations, + totalFeeDelta, + totalVsize, + }); + }) + ); + } else { + this.accelerationStats$ = this.apiService.getAccelerationHistory$(this.timespan).pipe( + switchMap(accelerations => { + let totalFeeDelta = 0; + let totalMined = 0; + let totalCanceled = 0; + for (const acceleration of accelerations) { + if (acceleration.status === 'completed') { + totalMined++; + totalFeeDelta += acceleration.feeDelta || 0; + } else if (acceleration.status === 'failed') { + totalCanceled++; + } + } + return of({ + count: totalMined, + totalFeeDelta, + successRate: (totalMined + totalCanceled > 0) ? ((totalMined / (totalMined + totalCanceled)) * 100) : 0.0, + }); + }) + ); + } } diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 039252e3a..155f3c4c6 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -7,13 +7,12 @@
- Acceleration stats  - (144 blocks) + Pending accelerations
- +
From f071d5e76ac771330c21e56df041eb480347e3d5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 3 Dec 2023 08:17:59 +0000 Subject: [PATCH 17/23] Accelerator dashboard fix success criteria --- .../acceleration-fees-graph.component.ts | 6 +++--- .../accelerator-dashboard.component.ts | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts index ca3dde69c..cb14db9e8 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -72,7 +72,7 @@ export class AccelerationFeesGraphComponent implements OnInit { this.hrStatsObservable$ = this.apiService.getAccelerationHistory$('24h').pipe( map((accelerations) => { return { - avgFeeDelta: accelerations.filter(acc => acc.status === 'mined' || acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length + avgFeeDelta: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length }; }) ); @@ -87,7 +87,7 @@ export class AccelerationFeesGraphComponent implements OnInit { }), map(([accelerations, blockFeesResponse]) => { return { - avgFeeDelta: accelerations.filter(acc => acc.status === 'mined' || acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length + avgFeeDelta: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length }; }), ); @@ -132,7 +132,7 @@ export class AccelerationFeesGraphComponent implements OnInit { const blockAccelerations = {}; for (const acceleration of accelerations) { - if (acceleration.status === 'mined' || acceleration.status === 'completed') { + if (acceleration.status === 'completed') { if (!blockAccelerations[acceleration.blockHeight]) { blockAccelerations[acceleration.blockHeight] = []; } diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts index 8257c3306..3a3b3f384 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts @@ -54,12 +54,16 @@ export class AcceleratorDashboardComponent implements OnInit { ) ]).pipe( switchMap(([blocks, accelerations]) => { + const blockMap = {}; + for (const block of blocks) { + blockMap[block.id] = block; + } const accelerationsByBlock: { [ hash: string ]: Acceleration[] } = {}; for (const acceleration of accelerations) { - if (['mined', 'completed'].includes(acceleration.status) && !accelerationsByBlock[acceleration.blockHash]) { - accelerationsByBlock[acceleration.blockHash] = []; - } - if (['mined', 'completed'].includes(acceleration.status)) { + if (['mined', 'completed'].includes(acceleration.status) && acceleration.pools.includes(blockMap[acceleration.blockHash]?.extras.pool.id)) { + if (!accelerationsByBlock[acceleration.blockHash]) { + accelerationsByBlock[acceleration.blockHash] = []; + } accelerationsByBlock[acceleration.blockHash].push(acceleration); } } From 47de84f03a3dcf77e1105c66871efaf7a5ebb999 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sun, 3 Dec 2023 08:25:47 +0000 Subject: [PATCH 18/23] Accelerator dashboard widget titles: latest -> recent --- .../accelerator-dashboard.component.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html index 155f3c4c6..def01fc00 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html @@ -43,7 +43,7 @@
- +
@@ -53,26 +53,26 @@
- + - +
-
Latest blocks
+
Recent Blocks
 
From e34ee3a34d652b7102b3727c9f5a379858175a22 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 7 Dec 2023 06:02:08 +0000 Subject: [PATCH 19/23] Fix rebase issues --- frontend/src/app/services/api.service.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index e379a8626..854d15c2a 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -1,11 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, -<<<<<<< HEAD PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface'; -======= - PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration } from '../interfaces/node-api.interface'; ->>>>>>> 9b9adcd43 (widgetify block fee rates chart) import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs'; import { StateService } from './state.service'; import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface'; From 8599876b26ee2109c379d4bfd8a5ab1d720360d0 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 7 Dec 2023 08:26:32 +0000 Subject: [PATCH 20/23] Refactor accelerator dashboard --- .../acceleration-stats.component.ts | 64 -------------- .../acceleration-fees-graph.component.html | 12 +-- .../acceleration-fees-graph.component.scss | 0 .../acceleration-fees-graph.component.ts | 35 ++++---- .../acceleration-stats.component.html | 53 +++++++++++ .../acceleration-stats.component.scss | 0 .../acceleration-stats.component.ts | 46 ++++++++++ .../accelerations-list.component.html | 13 +-- .../accelerations-list.component.scss | 0 .../accelerations-list.component.ts | 18 ++-- .../accelerator-dashboard.component.html | 10 +-- .../accelerator-dashboard.component.scss | 4 + .../accelerator-dashboard.component.ts | 35 +++++--- .../pending-stats.component.html} | 26 ++---- .../pending-stats.component.scss | 88 +++++++++++++++++++ .../pending-stats/pending-stats.component.ts | 39 ++++++++ frontend/src/app/graphs/graphs.module.ts | 4 +- .../src/app/graphs/graphs.routing.module.ts | 6 +- .../shared/components/btc/btc.component.html | 8 ++ .../shared/components/btc/btc.component.scss | 0 .../shared/components/btc/btc.component.ts | 44 ++++++++++ frontend/src/app/shared/shared.module.ts | 10 ++- 22 files changed, 373 insertions(+), 142 deletions(-) delete mode 100644 frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts rename frontend/src/app/components/{ => acceleration}/acceleration-fees-graph/acceleration-fees-graph.component.html (84%) rename frontend/src/app/components/{ => acceleration}/acceleration-fees-graph/acceleration-fees-graph.component.scss (100%) rename frontend/src/app/components/{ => acceleration}/acceleration-fees-graph/acceleration-fees-graph.component.ts (87%) create mode 100644 frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html rename frontend/src/app/components/{ => acceleration}/acceleration-stats/acceleration-stats.component.scss (100%) create mode 100644 frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts rename frontend/src/app/components/{ => acceleration}/accelerations-list/accelerations-list.component.html (82%) rename frontend/src/app/components/{ => acceleration}/accelerations-list/accelerations-list.component.scss (100%) rename frontend/src/app/components/{ => acceleration}/accelerations-list/accelerations-list.component.ts (68%) rename frontend/src/app/components/{ => acceleration}/accelerator-dashboard/accelerator-dashboard.component.html (92%) rename frontend/src/app/components/{ => acceleration}/accelerator-dashboard/accelerator-dashboard.component.scss (97%) rename frontend/src/app/components/{ => acceleration}/accelerator-dashboard/accelerator-dashboard.component.ts (72%) rename frontend/src/app/components/{acceleration-stats/acceleration-stats.component.html => acceleration/pending-stats/pending-stats.component.html} (60%) create mode 100644 frontend/src/app/components/acceleration/pending-stats/pending-stats.component.scss create mode 100644 frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts create mode 100644 frontend/src/app/shared/components/btc/btc.component.html create mode 100644 frontend/src/app/shared/components/btc/btc.component.scss create mode 100644 frontend/src/app/shared/components/btc/btc.component.ts diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts deleted file mode 100644 index 4d06a31e2..000000000 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { Observable, of } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; - -@Component({ - selector: 'app-acceleration-stats', - templateUrl: './acceleration-stats.component.html', - styleUrls: ['./acceleration-stats.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AccelerationStatsComponent implements OnInit { - @Input() timespan: 'now' | '24h' | '1w' = '24h'; - public accelerationStats$: Observable; - - constructor( - private apiService: ApiService, - ) { } - - ngOnInit(): void { - if (this.timespan === 'now') { - this.accelerationStats$ = this.apiService.getAccelerations$().pipe( - switchMap(accelerations => { - let totalAccelerations = 0; - let totalFeeDelta = 0; - let totalVsize = 0; - for (const acceleration of accelerations) { - totalAccelerations++; - totalFeeDelta += acceleration.feeDelta || 0; - totalVsize += acceleration.effectiveVsize || 0; - } - return of({ - count: totalAccelerations, - totalFeeDelta, - totalVsize, - }); - }) - ); - } else { - this.accelerationStats$ = this.apiService.getAccelerationHistory$(this.timespan).pipe( - switchMap(accelerations => { - let totalFeeDelta = 0; - let totalMined = 0; - let totalCanceled = 0; - for (const acceleration of accelerations) { - if (acceleration.status === 'completed') { - totalMined++; - totalFeeDelta += acceleration.feeDelta || 0; - } else if (acceleration.status === 'failed') { - totalCanceled++; - } - } - return of({ - count: totalMined, - totalFeeDelta, - successRate: (totalMined + totalCanceled > 0) ? ((totalMined / (totalMined + totalCanceled)) * 100) : 0.0, - }); - }) - ); - } - } - - -} diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html similarity index 84% rename from frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html rename to frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html index 6a56ac6ca..0c15f9f44 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.html +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.html @@ -30,15 +30,15 @@
-
Avg Fee Delta (24h)
+
Avg Out-of-band Fees (24h)

- {{ stats.avgFeeDelta | number }} sat +

-
Avg Fee Delta (1w)
+
Avg Out-of-band Fees (1w)

- {{ stats.avgFeeDelta | number }} sat +

@@ -55,7 +55,7 @@
-
Avg Fee Delta (24h)
+
Avg Out-of-band Fees (24h)

@@ -63,7 +63,7 @@
-
Avg Fee Delta (1w)
+
Avg Out-of-band Fees (1w)

diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss similarity index 100% rename from frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.scss rename to frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.scss diff --git a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts similarity index 87% rename from frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts rename to frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts index cb14db9e8..b5dc21eb6 100644 --- a/frontend/src/app/components/acceleration-fees-graph/acceleration-fees-graph.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component.ts @@ -2,14 +2,15 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, L import { EChartsOption, graphic } from 'echarts'; import { Observable, combineLatest } from 'rxjs'; import { map, startWith, switchMap, tap } from 'rxjs/operators'; -import { ApiService } from '../../services/api.service'; -import { SeoService } from '../../services/seo.service'; +import { ApiService } from '../../../services/api.service'; +import { SeoService } from '../../../services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis } from '../../shared/graphs.utils'; -import { StorageService } from '../../services/storage.service'; -import { MiningService } from '../../services/mining.service'; +import { download, formatterXAxis } from '../../../shared/graphs.utils'; +import { StorageService } from '../../../services/storage.service'; +import { MiningService } from '../../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; +import { Acceleration } from '../../../interfaces/node-api.interface'; @Component({ selector: 'app-acceleration-fees-graph', @@ -29,6 +30,7 @@ export class AccelerationFeesGraphComponent implements OnInit { @Input() widget: boolean = false; @Input() right: number | string = 45; @Input() left: number | string = 75; + @Input() accelerations$: Observable; miningWindowPreference: string; radioGroupForm: UntypedFormGroup; @@ -69,16 +71,16 @@ export class AccelerationFeesGraphComponent implements OnInit { this.isLoading = true; this.timespan = this.miningWindowPreference; - this.hrStatsObservable$ = this.apiService.getAccelerationHistory$('24h').pipe( + this.hrStatsObservable$ = (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: '24h' })).pipe( map((accelerations) => { return { - avgFeeDelta: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length + avgFeesPaid: accelerations.filter(acc => acc.status === 'completed' && acc.lastUpdated < (Date.now() - (24 * 60 * 60 * 1000))).reduce((total, acc) => total + acc.feePaid, 0) / accelerations.length }; }) ); this.statsObservable$ = combineLatest([ - this.apiService.getAccelerationHistory$(this.miningWindowPreference), + (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })), this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), ]).pipe( tap(([accelerations, blockFeesResponse]) => { @@ -87,7 +89,7 @@ export class AccelerationFeesGraphComponent implements OnInit { }), map(([accelerations, blockFeesResponse]) => { return { - avgFeeDelta: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + acc.feeDelta, 0) / accelerations.length + avgFeesPaid: accelerations.filter(acc => acc.status === 'completed').reduce((total, acc) => total + acc.feePaid, 0) / accelerations.length }; }), ); @@ -107,7 +109,7 @@ export class AccelerationFeesGraphComponent implements OnInit { this.isLoading = true; this.storageService.setValue('miningWindowPreference', timespan); this.timespan = timespan; - return this.apiService.getAccelerationHistory$(); + return this.apiService.getAccelerationHistory$({}); }) ), this.radioGroupForm.get('dateSpan').valueChanges.pipe( @@ -147,15 +149,18 @@ export class AccelerationFeesGraphComponent implements OnInit { last = val.avgHeight; } let totalFeeDelta = 0; + let totalFeePaid = 0; let totalCount = 0; while (last <= val.avgHeight) { totalFeeDelta += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feeDelta, 0); + totalFeePaid += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feePaid, 0); totalCount += (blockAccelerations[last] || []).length; last++; } data.push({ ...val, feeDelta: totalFeeDelta, + feePaid: totalFeePaid, accelerations: totalCount, }); } @@ -236,7 +241,7 @@ export class AccelerationFeesGraphComponent implements OnInit { icon: 'roundRect', }, { - name: 'Total fee delta', + name: 'Out-of-band fees paid', inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -251,8 +256,8 @@ export class AccelerationFeesGraphComponent implements OnInit { axisLabel: { color: 'rgb(110, 112, 121)', formatter: (val) => { - if (val >= 1_000_000) { - return `${(val / 100_000_000).toPrecision(5)} BTC`; + if (val >= 100_000) { + return `${(val / 100_000_000).toFixed(3)} BTC`; } else { return `${val} sats`; } @@ -303,8 +308,8 @@ export class AccelerationFeesGraphComponent implements OnInit { legendHoverLink: false, zlevel: 1, yAxisIndex: 0, - name: 'Total fee delta', - data: data.map(block => [block.timestamp * 1000, block.feeDelta, block.avgHeight]), + name: 'Out-of-band fees paid', + data: data.map(block => [block.timestamp * 1000, block.feePaid, block.avgHeight]), type: 'line', smooth: 0.25, symbol: 'none', diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html new file mode 100644 index 000000000..21cd57ae0 --- /dev/null +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.html @@ -0,0 +1,53 @@ +
+
+
+
Transactions
+
+
{{ stats.count }}
+
accelerated
+
+
+
+
Out-of-band Fees
+
+
{{ stats.totalFeesPaid / 100_000_000 | amountShortener: 4 }} BTC
+ + + +
+
+
+
Success rate
+
+
{{ stats.successRate.toFixed(2) }} %
+
mined
+
+
+
+
+ + +
+
+
Transactions
+
+
+
+
+
+
+
Out-of-band Fees
+
+
+
+
+
+
+
Success rate
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.scss similarity index 100% rename from frontend/src/app/components/acceleration-stats/acceleration-stats.component.scss rename to frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.scss diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts new file mode 100644 index 000000000..51f7360e1 --- /dev/null +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts @@ -0,0 +1,46 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiService } from '../../../services/api.service'; +import { StateService } from '../../../services/state.service'; +import { Acceleration } from '../../../interfaces/node-api.interface'; + +@Component({ + selector: 'app-acceleration-stats', + templateUrl: './acceleration-stats.component.html', + styleUrls: ['./acceleration-stats.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AccelerationStatsComponent implements OnInit { + @Input() timespan: '24h' | '1w' = '24h'; + @Input() accelerations$: Observable; + public accelerationStats$: Observable; + + constructor( + private apiService: ApiService, + private stateService: StateService, + ) { } + + ngOnInit(): void { + this.accelerationStats$ = this.accelerations$.pipe( + switchMap(accelerations => { + let totalFeesPaid = 0; + let totalSucceeded = 0; + let totalCanceled = 0; + for (const acceleration of accelerations) { + if (acceleration.status === 'completed') { + totalSucceeded++; + totalFeesPaid += acceleration.feePaid || 0; + } else if (acceleration.status === 'failed') { + totalCanceled++; + } + } + return of({ + count: totalSucceeded, + totalFeesPaid, + successRate: (totalSucceeded + totalCanceled > 0) ? ((totalSucceeded / (totalSucceeded + totalCanceled)) * 100) : 0.0, + }); + }) + ); + } +} diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html similarity index 82% rename from frontend/src/app/components/accelerations-list/accelerations-list.component.html rename to frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index 80f1c1dbf..0ac963446 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -10,19 +10,22 @@
- - + + - + - +
TXIDFeeFee deltaFinal FeeMax Bid Status
- {{ acceleration.feePaid | number }} sat + + {{ (acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee) | number }} sat + + ~ {{ acceleration.feeDelta | number }} sat diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss similarity index 100% rename from frontend/src/app/components/accelerations-list/accelerations-list.component.scss rename to frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss diff --git a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts similarity index 68% rename from frontend/src/app/components/accelerations-list/accelerations-list.component.ts rename to frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts index efc900bd5..0c0002782 100644 --- a/frontend/src/app/components/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core'; -import { Observable, catchError, of, switchMap } from 'rxjs'; -import { Acceleration, BlockExtended } from '../../interfaces/node-api.interface'; -import { ApiService } from '../../services/api.service'; -import { StateService } from '../../services/state.service'; -import { WebsocketService } from '../../services/websocket.service'; +import { Observable, catchError, of, switchMap, tap } from 'rxjs'; +import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; +import { ApiService } from '../../../services/api.service'; +import { StateService } from '../../../services/state.service'; +import { WebsocketService } from '../../../services/websocket.service'; @Component({ selector: 'app-accelerations-list', @@ -13,8 +13,9 @@ import { WebsocketService } from '../../services/websocket.service'; }) export class AccelerationsListComponent implements OnInit { @Input() widget: boolean = false; + @Input() accelerations$: Observable; - accelerations$: Observable = undefined; + accelerationList$: Observable = undefined; isLoading = true; paginationMaxSize: number; @@ -39,7 +40,7 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - this.accelerations$ = this.apiService.getAccelerationHistory$().pipe( + this.accelerationList$ = (this.apiService.getAccelerationHistory$({ timeframe: '1m' }) || this.accelerations$).pipe( switchMap(accelerations => { if (this.widget) { return of(accelerations.slice(-6).reverse()); @@ -50,6 +51,9 @@ export class AccelerationsListComponent implements OnInit { catchError((err) => { this.isLoading = false; return of([]); + }), + tap(() => { + this.isLoading = false; }) ); } diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html similarity index 92% rename from frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html rename to frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html index def01fc00..b43d27f05 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.html +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.html @@ -4,7 +4,7 @@
- +
Pending accelerations @@ -12,7 +12,7 @@
- +
@@ -27,7 +27,7 @@
- +
@@ -37,7 +37,7 @@
@@ -62,7 +62,7 @@   - +
diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss similarity index 97% rename from frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss rename to frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss index 1171846fe..6d863fb60 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.scss +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.scss @@ -120,6 +120,10 @@ text-align: left; width: 30%; + @media (max-width: 875px) { + display: none; + } + .pool-name { margin-left: 1em; } diff --git a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts similarity index 72% rename from frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts rename to frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index 3a3b3f384..00b3d303b 100644 --- a/frontend/src/app/components/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { SeoService } from '../../services/seo.service'; -import { WebsocketService } from '../../services/websocket.service'; -import { Acceleration, BlockExtended } from '../../interfaces/node-api.interface'; -import { StateService } from '../../services/state.service'; -import { Observable, catchError, combineLatest, of, switchMap } from 'rxjs'; -import { ApiService } from '../../services/api.service'; +import { SeoService } from '../../../services/seo.service'; +import { WebsocketService } from '../../../services/websocket.service'; +import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; +import { StateService } from '../../../services/state.service'; +import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, of, share, switchMap, tap } from 'rxjs'; +import { ApiService } from '../../../services/api.service'; interface AccelerationBlock extends BlockExtended { accelerationCount: number, @@ -18,7 +18,7 @@ interface AccelerationBlock extends BlockExtended { }) export class AcceleratorDashboardComponent implements OnInit { blocks$: Observable; - + accelerations$: Observable; loadingBlocks: boolean = true; constructor( @@ -33,7 +33,19 @@ export class AcceleratorDashboardComponent implements OnInit { ngOnInit(): void { this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); + this.accelerations$ = this.stateService.chainTip$.pipe( + distinctUntilChanged(), + switchMap((chainTip) => { + return this.apiService.getAccelerationHistory$({ timeframe: '1w' }); + }), + catchError((e) => { + return of([]); + }), + share(), + ); + this.blocks$ = combineLatest([ + this.accelerations$, this.stateService.blocks$.pipe( switchMap((blocks) => { if (this.stateService.env.MINING_DASHBOARD === true) { @@ -44,16 +56,13 @@ export class AcceleratorDashboardComponent implements OnInit { } } return of(blocks as AccelerationBlock[]); - }) - ), - this.apiService.getAccelerationHistory$('24h').pipe( - catchError((err) => { + }), + tap(() => { this.loadingBlocks = false; - return of([]); }) ) ]).pipe( - switchMap(([blocks, accelerations]) => { + switchMap(([accelerations, blocks]) => { const blockMap = {}; for (const block of blocks) { blockMap[block.id] = block; diff --git a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html similarity index 60% rename from frontend/src/app/components/acceleration-stats/acceleration-stats.component.html rename to frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html index 2180a1ad6..5b7bc356a 100644 --- a/frontend/src/app/components/acceleration-stats/acceleration-stats.component.html +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html @@ -8,22 +8,15 @@
-
Fee delta
+
Avg Max Bid
-
{{ stats.totalFeeDelta / 100_000_000 | amountShortener: 4 }} BTC
+
{{ stats.avgFeeDelta / 100_000_000 | amountShortener: 4 }} BTC
- +
-
-
Success rate
-
-
{{ stats.successRate.toFixed(2) }} %
-
mined in the next block
-
-
-
+
Total vsize
@@ -43,20 +36,13 @@
-
Fee delta
+
Avg Max Bid
-
-
Success rate
-
-
-
-
-
-
+
Total vsize
diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.scss b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.scss new file mode 100644 index 000000000..fcc5564a8 --- /dev/null +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.scss @@ -0,0 +1,88 @@ +.card-title { + color: #4a68b9; + font-size: 10px; + margin-bottom: 4px; + font-size: 1rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-text { + font-size: 22px; + span { + font-size: 11px; + position: relative; + top: -2px; + display: inline-flex; + } + .green-color { + display: block; + } +} + +.stats-container { + display: flex; + justify-content: space-between; + @media (min-width: 376px) { + flex-direction: row; + } + .item { + max-width: 150px; + margin: 0; + width: -webkit-fill-available; + @media (min-width: 376px) { + margin: 0 auto 0px; + } + &:first-child{ + display: none; + @media (min-width: 485px) { + display: block; + } + @media (min-width: 768px) { + display: none; + } + @media (min-width: 992px) { + display: block; + } + } + &:last-child { + margin-bottom: 0; + } + .card-text span { + color: #ffffff66; + font-size: 12px; + top: 0px; + } + .fee-text{ + border-bottom: 1px solid #ffffff1c; + width: fit-content; + margin: auto; + line-height: 1.45; + padding: 0px 2px; + } + .fiat { + display: block; + font-size: 14px !important; + } + } +} + +.loading-container{ + min-height: 76px; +} + +.card-text { + .skeleton-loader { + width: 100%; + display: block; + &:first-child { + max-width: 90px; + margin: 15px auto 3px; + } + &:last-child { + margin: 10px auto 3px; + max-width: 55px; + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts new file mode 100644 index 000000000..dd3adacf8 --- /dev/null +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts @@ -0,0 +1,39 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiService } from '../../../services/api.service'; + +@Component({ + selector: 'app-pending-stats', + templateUrl: './pending-stats.component.html', + styleUrls: ['./pending-stats.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PendingStatsComponent implements OnInit { + public accelerationStats$: Observable; + + constructor( + private apiService: ApiService, + ) { } + + ngOnInit(): void { + this.accelerationStats$ = this.apiService.getAccelerations$().pipe( + switchMap(accelerations => { + let totalAccelerations = 0; + let totalFeeDelta = 0; + let totalVsize = 0; + for (const acceleration of accelerations) { + totalAccelerations++; + totalFeeDelta += acceleration.feeDelta || 0; + totalVsize += acceleration.effectiveVsize || 0; + } + return of({ + count: totalAccelerations, + totalFeeDelta, + avgFeeDelta: totalAccelerations ? totalFeeDelta / totalAccelerations : 0, + totalVsize, + }); + }) + ); + } +} diff --git a/frontend/src/app/graphs/graphs.module.ts b/frontend/src/app/graphs/graphs.module.ts index 81a1f0c06..85905d1f1 100644 --- a/frontend/src/app/graphs/graphs.module.ts +++ b/frontend/src/app/graphs/graphs.module.ts @@ -3,7 +3,7 @@ import { NgxEchartsModule } from 'ngx-echarts'; import { GraphsRoutingModule } from './graphs.routing.module'; import { SharedModule } from '../shared/shared.module'; -import { AccelerationFeesGraphComponent } from '../components/acceleration-fees-graph/acceleration-fees-graph.component'; +import { AccelerationFeesGraphComponent } from '../components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; import { BlockFeesGraphComponent } from '../components/block-fees-graph/block-fees-graph.component'; import { BlockRewardsGraphComponent } from '../components/block-rewards-graph/block-rewards-graph.component'; import { BlockFeeRatesGraphComponent } from '../components/block-fee-rates-graph/block-fee-rates-graph.component'; @@ -20,7 +20,7 @@ import { PoolComponent } from '../components/pool/pool.component'; import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; -import { AcceleratorDashboardComponent } from '../components/accelerator-dashboard/accelerator-dashboard.component'; +import { AcceleratorDashboardComponent } from '../components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-chart.component'; import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { BlockHealthGraphComponent } from '../components/block-health-graph/block-health-graph.component'; diff --git a/frontend/src/app/graphs/graphs.routing.module.ts b/frontend/src/app/graphs/graphs.routing.module.ts index 12eeb6e4c..0f217eb6e 100644 --- a/frontend/src/app/graphs/graphs.routing.module.ts +++ b/frontend/src/app/graphs/graphs.routing.module.ts @@ -10,15 +10,15 @@ import { HashrateChartComponent } from '../components/hashrate-chart/hashrate-ch import { HashrateChartPoolsComponent } from '../components/hashrates-chart-pools/hashrate-chart-pools.component'; import { MempoolBlockComponent } from '../components/mempool-block/mempool-block.component'; import { MiningDashboardComponent } from '../components/mining-dashboard/mining-dashboard.component'; -import { AcceleratorDashboardComponent } from '../components/accelerator-dashboard/accelerator-dashboard.component'; +import { AcceleratorDashboardComponent } from '../components/acceleration/accelerator-dashboard/accelerator-dashboard.component'; import { PoolRankingComponent } from '../components/pool-ranking/pool-ranking.component'; import { PoolComponent } from '../components/pool/pool.component'; import { StartComponent } from '../components/start/start.component'; import { StatisticsComponent } from '../components/statistics/statistics.component'; import { TelevisionComponent } from '../components/television/television.component'; import { DashboardComponent } from '../dashboard/dashboard.component'; -import { AccelerationFeesGraphComponent } from '../components/acceleration-fees-graph/acceleration-fees-graph.component'; -import { AccelerationsListComponent } from '../components/accelerations-list/accelerations-list.component'; +import { AccelerationFeesGraphComponent } from '../components/acceleration/acceleration-fees-graph/acceleration-fees-graph.component'; +import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; const routes: Routes = [ { diff --git a/frontend/src/app/shared/components/btc/btc.component.html b/frontend/src/app/shared/components/btc/btc.component.html new file mode 100644 index 000000000..c13a8ff31 --- /dev/null +++ b/frontend/src/app/shared/components/btc/btc.component.html @@ -0,0 +1,8 @@ +{{ valueOverride }} +‎{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ value | number }} + + L- + tL- + t- + s-{{ unit }} + \ No newline at end of file diff --git a/frontend/src/app/shared/components/btc/btc.component.scss b/frontend/src/app/shared/components/btc/btc.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/app/shared/components/btc/btc.component.ts b/frontend/src/app/shared/components/btc/btc.component.ts new file mode 100644 index 000000000..4e62b07ff --- /dev/null +++ b/frontend/src/app/shared/components/btc/btc.component.ts @@ -0,0 +1,44 @@ +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { StateService } from '../../../services/state.service'; + +@Component({ + selector: 'app-btc', + templateUrl: './btc.component.html', + styleUrls: ['./btc.component.scss'] +}) +export class BtcComponent implements OnInit, OnChanges { + @Input() satoshis: number; + @Input() addPlus = false; + @Input() valueOverride: string | undefined = undefined; + + value: number; + unit: string; + + network = ''; + stateSubscription: Subscription; + + constructor( + private stateService: StateService, + ) { } + + ngOnInit() { + this.stateSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network); + } + + ngOnDestroy() { + if (this.stateSubscription) { + this.stateSubscription.unsubscribe(); + } + } + + ngOnChanges(changes: SimpleChanges): void { + if (this.satoshis >= 1_000_000) { + this.value = (this.satoshis / 100_000_000); + this.unit = 'BTC' + } else { + this.value = Math.round(this.satoshis); + this.unit = 'sats' + } + } +} diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index bd897ba99..52123f995 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -73,6 +73,7 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index import { SvgImagesComponent } from '../components/svg-images/svg-images.component'; import { ChangeComponent } from '../components/change/change.component'; import { SatsComponent } from './components/sats/sats.component'; +import { BtcComponent } from './components/btc/btc.component'; import { FeeRateComponent } from './components/fee-rate/fee-rate.component'; import { TruncateComponent } from './components/truncate/truncate.component'; import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component'; @@ -85,8 +86,9 @@ import { GlobalFooterComponent } from './components/global-footer/global-footer. import { AcceleratePreviewComponent } from '../components/accelerate-preview/accelerate-preview.component'; import { AccelerateFeeGraphComponent } from '../components/accelerate-preview/accelerate-fee-graph.component'; import { MempoolErrorComponent } from './components/mempool-error/mempool-error.component'; -import { AccelerationsListComponent } from '../components/accelerations-list/accelerations-list.component'; -import { AccelerationStatsComponent } from '../components/acceleration-stats/acceleration-stats.component'; +import { AccelerationsListComponent } from '../components/acceleration/accelerations-list/accelerations-list.component'; +import { PendingStatsComponent } from '../components/acceleration/pending-stats/pending-stats.component'; +import { AccelerationStatsComponent } from '../components/acceleration/acceleration-stats/acceleration-stats.component'; import { BlockViewComponent } from '../components/block-view/block-view.component'; import { EightBlocksComponent } from '../components/eight-blocks/eight-blocks.component'; @@ -169,6 +171,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir SvgImagesComponent, ChangeComponent, SatsComponent, + BtcComponent, FeeRateComponent, TruncateComponent, SearchResultsComponent, @@ -194,6 +197,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir MempoolErrorComponent, AccelerationsListComponent, AccelerationStatsComponent, + PendingStatsComponent, ], imports: [ CommonModule, @@ -291,6 +295,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir SvgImagesComponent, ChangeComponent, SatsComponent, + BtcComponent, FeeRateComponent, TruncateComponent, SearchResultsComponent, @@ -306,6 +311,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir MempoolErrorComponent, AccelerationsListComponent, AccelerationStatsComponent, + PendingStatsComponent, MempoolBlockOverviewComponent, ClockchainComponent, From 42f6f0c1224fc0bf56f566c6a1025c1d9ef4b8d5 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 7 Dec 2023 11:12:20 +0000 Subject: [PATCH 21/23] Redesign accelerator dashboard --- .../accelerations-list.component.html | 14 ++- .../accelerations-list.component.scss | 10 ++ .../accelerations-list.component.ts | 9 +- .../accelerator-dashboard.component.html | 69 ++++------- .../accelerator-dashboard.component.scss | 13 +++ .../accelerator-dashboard.component.ts | 38 ++++++- .../pending-stats.component.html | 2 +- .../pending-stats/pending-stats.component.ts | 4 +- .../block-overview-graph.component.ts | 9 +- .../block-overview-graph/block-scene.ts | 107 ++++++++++++++++-- .../block-overview-graph/tx-view.ts | 86 +------------- .../components/block-overview-graph/utils.ts | 29 +++++ .../mempool-block-overview.component.html | 1 + .../mempool-block-overview.component.ts | 3 + 14 files changed, 243 insertions(+), 151 deletions(-) create mode 100644 frontend/src/app/components/block-overview-graph/utils.ts diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index 0ac963446..a64c4724b 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -1,20 +1,18 @@ - -

Accelerations

-
- +
+
- +
TXID Final Fee Max Bid Status
@@ -62,5 +60,11 @@
+ + +
+ There are no accelerations show here yet! +
+
diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss index d49ac2609..55cecdcbd 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss @@ -105,3 +105,13 @@ tr, td, th { max-width: 50vw; text-align: left; } + +.no-data { + color: rgba(255, 255, 255, 0.4); + display: flex; + height: 280px; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: center; +} diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts index 0c0002782..ddd89d31c 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.ts @@ -13,6 +13,7 @@ import { WebsocketService } from '../../../services/websocket.service'; }) export class AccelerationsListComponent implements OnInit { @Input() widget: boolean = false; + @Input() pending: boolean = false; @Input() accelerations$: Observable; accelerationList$: Observable = undefined; @@ -40,8 +41,14 @@ export class AccelerationsListComponent implements OnInit { this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()]; this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5; - this.accelerationList$ = (this.apiService.getAccelerationHistory$({ timeframe: '1m' }) || this.accelerations$).pipe( + const accelerationObservable$ = this.accelerations$ || (this.pending ? this.apiService.getAccelerations$() : this.apiService.getAccelerationHistory$({ timeframe: '1m' })); + this.accelerationList$ = accelerationObservable$.pipe( switchMap(accelerations => { + if (this.pending) { + for (const acceleration of accelerations) { + acceleration.status = acceleration.status || 'accelerating'; + } + } if (this.widget) { return of(accelerations.slice(-6).reverse()); } else { 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 b43d27f05..0dbba3219 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 @@ -12,7 +12,7 @@
- +
@@ -27,7 +27,18 @@
- + +
+
+
+ + + +
+
+
+
+
@@ -43,64 +54,28 @@
- -
-
-
- - + +
+
+
+
Pending Accelerations
+
- +
- - - -
-
-
- -
Recent Blocks
-   - -
- - - - - - - - - - - - - - - -
HeightPoolMedian feeAccelerations
{{ block.height }} - - - {{ block.extras.pool.name }} - - {{ block.accelerationCount | number }}
-
-
-
-
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 6d863fb60..0d1c3b1c0 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 @@ -132,4 +132,17 @@ text-align: right; width: 20%; } +} + +.card { + height: 385px; +} +.list-card { + height: 410px; +} + +.mempool-block-wrapper { + max-height: 380px; + max-width: 380px; + margin: auto; } \ 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 00b3d303b..c3a40730b 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 @@ -3,8 +3,15 @@ import { SeoService } from '../../../services/seo.service'; import { WebsocketService } from '../../../services/websocket.service'; import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface'; import { StateService } from '../../../services/state.service'; -import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, of, share, switchMap, tap } from 'rxjs'; +import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs'; import { ApiService } from '../../../services/api.service'; +import { Color } from '../../block-overview-graph/sprite-types'; +import { hexToColor } from '../../block-overview-graph/utils'; +import TxView from '../../block-overview-graph/tx-view'; +import { feeLevels, mempoolFeeColors } from '../../../app.constants'; + +const acceleratedColor: Color = hexToColor('8F5FF6'); +const normalColors = mempoolFeeColors.map(hex => hexToColor(hex + '5F')); interface AccelerationBlock extends BlockExtended { accelerationCount: number, @@ -19,6 +26,8 @@ interface AccelerationBlock extends BlockExtended { export class AcceleratorDashboardComponent implements OnInit { blocks$: Observable; accelerations$: Observable; + pendingAccelerations$: Observable; + minedAccelerations$: Observable; loadingBlocks: boolean = true; constructor( @@ -33,6 +42,17 @@ export class AcceleratorDashboardComponent implements OnInit { ngOnInit(): void { this.websocketService.want(['blocks', 'mempool-blocks', 'stats']); + this.pendingAccelerations$ = interval(30000).pipe( + startWith(true), + switchMap(() => { + return this.apiService.getAccelerations$(); + }), + catchError((e) => { + return of([]); + }), + share(), + ); + this.accelerations$ = this.stateService.chainTip$.pipe( distinctUntilChanged(), switchMap((chainTip) => { @@ -44,6 +64,12 @@ export class AcceleratorDashboardComponent implements OnInit { share(), ); + this.minedAccelerations$ = this.accelerations$.pipe( + map(accelerations => { + return accelerations.filter(acc => ['mined', 'completed'].includes(acc.status)) + }) + ); + this.blocks$ = combineLatest([ this.accelerations$, this.stateService.blocks$.pipe( @@ -83,4 +109,14 @@ export class AcceleratorDashboardComponent implements OnInit { }) ); } + + getAcceleratorColor(tx: TxView): Color { + if (tx.status === 'accelerated' || tx.acc) { + return acceleratedColor; + } else { + const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate + const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1; + return normalColors[feeLevelIndex] || normalColors[mempoolFeeColors.length - 1]; + } + } } diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html index 5b7bc356a..c94bbf43a 100644 --- a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.html @@ -20,7 +20,7 @@
Total vsize
-
{{ (stats.totalVsize / 1_000_000).toFixed(2) }}% of next block
+
{{ (stats.totalVsize / 1_000_000 * 100).toFixed(2) }}% of next block
diff --git a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts index dd3adacf8..f344c37a0 100644 --- a/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts +++ b/frontend/src/app/components/acceleration/pending-stats/pending-stats.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core import { Observable, of } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; +import { Acceleration } from '../../../interfaces/node-api.interface'; @Component({ selector: 'app-pending-stats', @@ -10,6 +11,7 @@ import { ApiService } from '../../../services/api.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PendingStatsComponent implements OnInit { + @Input() accelerations$: Observable; public accelerationStats$: Observable; constructor( @@ -17,7 +19,7 @@ export class PendingStatsComponent implements OnInit { ) { } ngOnInit(): void { - this.accelerationStats$ = this.apiService.getAccelerations$().pipe( + this.accelerationStats$ = (this.accelerations$ || this.apiService.getAccelerations$()).pipe( switchMap(accelerations => { let totalAccelerations = 0; let totalFeeDelta = 0; 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 68d2a1bf3..1fc173a2d 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 @@ -4,7 +4,7 @@ import { FastVertexArray } from './fast-vertex-array'; import BlockScene from './block-scene'; import TxSprite from './tx-sprite'; import TxView from './tx-view'; -import { Position } from './sprite-types'; +import { Color, Position } from './sprite-types'; import { Price } from '../../services/price.service'; import { StateService } from '../../services/state.service'; import { Subscription } from 'rxjs'; @@ -27,6 +27,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On @Input() unavailable: boolean = false; @Input() auditHighlighting: boolean = false; @Input() blockConversion: Price; + @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txClickEvent = new EventEmitter<{ tx: TransactionStripped, keyModifier: boolean}>(); @Output() txHoverEvent = new EventEmitter(); @Output() readyEvent = new EventEmitter(); @@ -91,6 +92,9 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On if (changes.auditHighlighting) { this.setHighlightingEnabled(this.auditHighlighting); } + if (changes.overrideColor) { + this.scene.setColorFunction(this.overrideColors); + } } ngOnDestroy(): void { @@ -228,7 +232,8 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On } else { this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: this.resolution, blockLimit: this.blockLimit, orientation: this.orientation, flip: this.flip, vertexArray: this.vertexArray, - highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset }); + highlighting: this.auditHighlighting, animationDuration: this.animationDuration, animationOffset: this.animationOffset, + colorFunction: this.overrideColors }); this.start(); } } diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index 2569a3bb2..77b7c2e05 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -1,12 +1,26 @@ import { FastVertexArray } from './fast-vertex-array'; import TxView from './tx-view'; import { TransactionStripped } from '../../interfaces/websocket.interface'; -import { Position, Square, ViewUpdateParams } from './sprite-types'; +import { Color, Position, Square, ViewUpdateParams } from './sprite-types'; +import { feeLevels, mempoolFeeColors } from '../../app.constants'; +import { darken, desaturate, hexToColor } from './utils'; + +const feeColors = mempoolFeeColors.map(hexToColor); +const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9)); +const marginalFeeColors = feeColors.map((color) => darken(desaturate(color, 0.8), 1.1)); +const auditColors = { + censored: hexToColor('f344df'), + missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), + added: hexToColor('0099ff'), + selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), + accelerated: hexToColor('8F5FF6'), +}; export default class BlockScene { scene: { count: number, offset: { x: number, y: number}}; vertexArray: FastVertexArray; txs: { [key: string]: TxView }; + getColor: ((tx: TxView) => Color) = defaultColorFunction; orientation: string; flip: boolean; animationDuration: number = 1000; @@ -26,11 +40,11 @@ export default class BlockScene { animateUntil = 0; dirty: boolean; - constructor({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting }: + constructor({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction }: { width: number, height: number, resolution: number, blockLimit: number, animationDuration: number, animationOffset: number, - orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } + orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null } ) { - this.init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting }); + this.init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction }); } resize({ width = this.width, height = this.height, animate = true }: { width?: number, height?: number, animate: boolean }): void { @@ -63,6 +77,14 @@ export default class BlockScene { } } + setColorFunction(colorFunction: ((tx: TxView) => Color) | null): void { + this.getColor = colorFunction; + this.dirty = true; + if (this.initialised && this.scene) { + this.updateColors(performance.now(), 50); + } + } + // Destroy the current layout and clean up graphics sprites without any exit animation destroy(): void { Object.values(this.txs).forEach(tx => tx.destroy()); @@ -86,7 +108,7 @@ export default class BlockScene { this.applyTxUpdate(txView, { display: { position: txView.screenPosition, - color: txView.getColor() + color: this.getColor(txView) }, duration: 0 }); @@ -217,9 +239,9 @@ export default class BlockScene { this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value)); } - private init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting }: + private init({ width, height, resolution, blockLimit, animationDuration, animationOffset, orientation, flip, vertexArray, highlighting, colorFunction }: { width: number, height: number, resolution: number, blockLimit: number, animationDuration: number, animationOffset: number, - orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean } + orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, colorFunction: ((tx: TxView) => Color) | null } ): void { this.animationDuration = animationDuration || 1000; this.configAnimationOffset = animationOffset; @@ -228,6 +250,7 @@ export default class BlockScene { this.flip = flip; this.vertexArray = vertexArray; this.highlightingEnabled = highlighting; + this.getColor = colorFunction || defaultColorFunction; this.scene = { count: 0, @@ -261,9 +284,23 @@ export default class BlockScene { } } + private updateColor(tx: TxView, startTime: number, delay: number, animate: boolean = true, duration: number = 500): void { + if (tx.dirty || this.dirty) { + const txColor = this.getColor(tx); + this.applyTxUpdate(tx, { + display: { + color: txColor, + }, + start: startTime, + delay, + duration: animate ? duration : 0, + }); + } + } + private setTxOnScreen(tx: TxView, startTime: number, delay: number = 50, direction: string = 'left', animate: boolean = true): void { if (!tx.initialised) { - const txColor = tx.getColor(); + const txColor = this.getColor(tx); this.applyTxUpdate(tx, { display: { position: { @@ -321,6 +358,15 @@ export default class BlockScene { this.dirty = false; } + private updateColors(startTime: number, delay: number = 50, animate: boolean = true, duration: number = 500): void { + const ids = this.getTxList(); + startTime = startTime || performance.now(); + for (const id of ids) { + this.updateColor(this.txs[id], startTime, delay, animate, duration); + } + this.dirty = false; + } + private remove(id: string, startTime: number, direction: string = 'left'): TxView | void { const tx = this.txs[id]; if (tx) { @@ -858,3 +904,48 @@ class BlockLayout { function feeRateDescending(a: TxView, b: TxView) { return b.feerate - a.feerate; } + +function defaultColorFunction(tx: TxView): Color { + const rate = tx.fee / tx.vsize; // color by simple single-tx fee rate + const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1; + const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1]; + // Normal mode + if (!tx.scene?.highlightingEnabled) { + if (tx.acc) { + return auditColors.accelerated; + } else { + return feeLevelColor; + } + return feeLevelColor; + } + // Block audit + switch(tx.status) { + case 'censored': + return auditColors.censored; + case 'missing': + case 'sigop': + case 'rbf': + return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; + case 'fresh': + case 'freshcpfp': + return auditColors.missing; + case 'added': + return auditColors.added; + case 'selected': + return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; + case 'accelerated': + return auditColors.accelerated; + case 'found': + if (tx.context === 'projected') { + return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1]; + } else { + return feeLevelColor; + } + default: + if (tx.acc) { + return auditColors.accelerated; + } else { + return feeLevelColor; + } + } +} \ No newline at end of file diff --git a/frontend/src/app/components/block-overview-graph/tx-view.ts b/frontend/src/app/components/block-overview-graph/tx-view.ts index db2c4f6ae..4e2d855e6 100644 --- a/frontend/src/app/components/block-overview-graph/tx-view.ts +++ b/frontend/src/app/components/block-overview-graph/tx-view.ts @@ -2,24 +2,13 @@ import TxSprite from './tx-sprite'; import { FastVertexArray } from './fast-vertex-array'; import { TransactionStripped } from '../../interfaces/websocket.interface'; import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from './sprite-types'; -import { feeLevels, mempoolFeeColors } from '../../app.constants'; +import { hexToColor } from './utils'; import BlockScene from './block-scene'; const hoverTransitionTime = 300; const defaultHoverColor = hexToColor('1bd8f4'); const defaultHighlightColor = hexToColor('800080'); -const feeColors = mempoolFeeColors.map(hexToColor); -const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9)); -const marginalFeeColors = feeColors.map((color) => darken(desaturate(color, 0.8), 1.1)); -const auditColors = { - censored: hexToColor('f344df'), - missing: darken(desaturate(hexToColor('f344df'), 0.3), 0.7), - added: hexToColor('0099ff'), - selected: darken(desaturate(hexToColor('0099ff'), 0.3), 0.7), - accelerated: hexToColor('8F5FF6'), -}; - // convert from this class's update format to TxSprite's update format function toSpriteUpdate(params: ViewUpdateParams): SpriteUpdateParams { return { @@ -195,77 +184,4 @@ export default class TxView implements TransactionStripped { this.dirty = false; return performance.now() + hoverTransitionTime; } - - getColor(): Color { - const rate = this.fee / this.vsize; // color by simple single-tx fee rate - const feeLevelIndex = feeLevels.findIndex((feeLvl) => Math.max(1, rate) < feeLvl) - 1; - const feeLevelColor = feeColors[feeLevelIndex] || feeColors[mempoolFeeColors.length - 1]; - // Normal mode - if (!this.scene?.highlightingEnabled) { - if (this.acc) { - return auditColors.accelerated; - } else { - return feeLevelColor; - } - return feeLevelColor; - } - // Block audit - switch(this.status) { - case 'censored': - return auditColors.censored; - case 'missing': - case 'sigop': - case 'rbf': - return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; - case 'fresh': - case 'freshcpfp': - return auditColors.missing; - case 'added': - return auditColors.added; - case 'selected': - return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1]; - case 'accelerated': - return auditColors.accelerated; - case 'found': - if (this.context === 'projected') { - return auditFeeColors[feeLevelIndex] || auditFeeColors[mempoolFeeColors.length - 1]; - } else { - return feeLevelColor; - } - default: - if (this.acc) { - return auditColors.accelerated; - } else { - return feeLevelColor; - } - } - } -} - -function hexToColor(hex: string): Color { - return { - r: parseInt(hex.slice(0, 2), 16) / 255, - g: parseInt(hex.slice(2, 4), 16) / 255, - b: parseInt(hex.slice(4, 6), 16) / 255, - a: 1 - }; -} - -function desaturate(color: Color, amount: number): Color { - const gray = (color.r + color.g + color.b) / 6; - return { - r: color.r + ((gray - color.r) * amount), - g: color.g + ((gray - color.g) * amount), - b: color.b + ((gray - color.b) * amount), - a: color.a, - }; -} - -function darken(color: Color, amount: number): Color { - return { - r: color.r * amount, - g: color.g * amount, - b: color.b * amount, - a: color.a, - } } diff --git a/frontend/src/app/components/block-overview-graph/utils.ts b/frontend/src/app/components/block-overview-graph/utils.ts new file mode 100644 index 000000000..a0bb8e868 --- /dev/null +++ b/frontend/src/app/components/block-overview-graph/utils.ts @@ -0,0 +1,29 @@ +import { Color } from './sprite-types'; + +export function hexToColor(hex: string): Color { + return { + r: parseInt(hex.slice(0, 2), 16) / 255, + g: parseInt(hex.slice(2, 4), 16) / 255, + b: parseInt(hex.slice(4, 6), 16) / 255, + a: hex.length > 6 ? parseInt(hex.slice(6, 8), 16) / 255 : 1 + }; +} + +export function desaturate(color: Color, amount: number): Color { + const gray = (color.r + color.g + color.b) / 6; + return { + r: color.r + ((gray - color.r) * amount), + g: color.g + ((gray - color.g) * amount), + b: color.b + ((gray - color.b) * amount), + a: color.a, + }; +} + +export function darken(color: Color, amount: number): Color { + return { + r: color.r * amount, + g: color.g * amount, + b: color.b * amount, + a: color.a, + } +} \ No newline at end of file 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 503f2e38d..1e0cba48c 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 @@ -5,5 +5,6 @@ [blockLimit]="stateService.blockVSize" [orientation]="timeLtr ? 'right' : 'left'" [flip]="true" + [overrideColors]="overrideColors" (txClickEvent)="onTxClick($event)" > 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 226be5210..09eac989e 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 @@ -8,6 +8,8 @@ import { switchMap, filter } from 'rxjs/operators'; import { WebsocketService } from '../../services/websocket.service'; import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe'; import { Router } from '@angular/router'; +import { Color } from '../block-overview-graph/sprite-types'; +import TxView from '../block-overview-graph/tx-view'; @Component({ selector: 'app-mempool-block-overview', @@ -16,6 +18,7 @@ import { Router } from '@angular/router'; }) export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { @Input() index: number; + @Input() overrideColors: ((tx: TxView) => Color) | null = null; @Output() txPreviewEvent = new EventEmitter(); @ViewChild('blockGraph') blockGraph: BlockOverviewGraphComponent; From aff44d90d5ebc678f86e9caeadd5e82376c33e09 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 8 Dec 2023 13:03:08 +0000 Subject: [PATCH 22/23] Rerefactor acceleration dashboard --- .../acceleration-fees-graph.component.html | 33 +--- .../acceleration-fees-graph.component.scss | 57 +------ .../acceleration-fees-graph.component.ts | 145 ++++++++++-------- .../acceleration-stats.component.ts | 2 +- .../accelerations-list.component.html | 57 ++++--- .../accelerations-list.component.scss | 14 +- .../accelerator-dashboard.component.html | 24 ++- .../accelerator-dashboard.component.ts | 2 +- 8 files changed, 158 insertions(+), 176 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 0c15f9f44..9ae0ddade 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 @@ -28,19 +28,8 @@
-
-
-
Avg Out-of-band Fees (24h)
-

- -

-
-
-
Avg Out-of-band Fees (1w)
-

- -

-
+
+
Out-of-band Fees Per Block
@@ -50,22 +39,4 @@
-
- - -
-
Avg Out-of-band Fees (24h)
-

- -

-
-
- -
-
Avg Out-of-band Fees (1w)
-

- -

-
-
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 2ffcc6374..c4b4335ee 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 @@ -56,61 +56,16 @@ .chart-widget { width: 100%; height: 100%; - max-height: 238px; + max-height: 290px; } -.acceleration-fees { - min-height: 56px; - display: block; - @media (min-width: 485px) { - display: flex; - flex-direction: row; - } - h5 { - margin-bottom: 10px; - } - .item { - width: 50%; - display: inline-block; - margin: 0px auto 20px; - &:nth-child(2) { - order: 2; - @media (min-width: 485px) { - order: 3; - } - } - &:nth-child(3) { - order: 3; - @media (min-width: 485px) { - order: 2; - display: block; - } - @media (min-width: 768px) { - display: none; - } - @media (min-width: 992px) { - display: block; - } - } - .card-title { - font-size: 1rem; - color: #4a68b9; - } - .card-text { - font-size: 18px; - span { - color: #ffffff66; - font-size: 12px; - } - } - } +h5 { + margin-bottom: 10px; } -.skeleton-loader { - width: 100%; - display: block; - max-width: 80px; - margin: 15px auto 3px; +.card-title { + font-size: 1rem; + color: #4a68b9; } .disabled { 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 b5dc21eb6..d27b10690 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,12 +1,12 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core'; import { EChartsOption, graphic } from 'echarts'; -import { Observable, combineLatest } from 'rxjs'; -import { map, startWith, switchMap, tap } from 'rxjs/operators'; +import { Observable, Subscription, combineLatest } from 'rxjs'; +import { map, max, startWith, switchMap, tap } from 'rxjs/operators'; import { ApiService } from '../../../services/api.service'; import { SeoService } from '../../../services/seo.service'; import { formatNumber } from '@angular/common'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { download, formatterXAxis } from '../../../shared/graphs.utils'; +import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../../shared/graphs.utils'; import { StorageService } from '../../../services/storage.service'; import { MiningService } from '../../../services/mining.service'; import { ActivatedRoute } from '@angular/router'; @@ -26,7 +26,7 @@ import { Acceleration } from '../../../interfaces/node-api.interface'; `], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AccelerationFeesGraphComponent implements OnInit { +export class AccelerationFeesGraphComponent implements OnInit, OnDestroy { @Input() widget: boolean = false; @Input() right: number | string = 45; @Input() left: number | string = 75; @@ -42,6 +42,7 @@ export class AccelerationFeesGraphComponent implements OnInit { hrStatsObservable$: Observable; statsObservable$: Observable; + statsSubscription: Subscription; isLoading = true; formatNumber = formatNumber; timespan = ''; @@ -66,26 +67,17 @@ export class AccelerationFeesGraphComponent implements OnInit { ngOnInit(): void { this.seoService.setTitle($localize`:@@6c453b11fd7bd159ae30bc381f367bc736d86909:Acceleration Fees`); + this.isLoading = true; if (this.widget) { - this.miningWindowPreference = '1w'; - this.isLoading = true; + this.miningWindowPreference = '1m'; this.timespan = this.miningWindowPreference; - this.hrStatsObservable$ = (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: '24h' })).pipe( - map((accelerations) => { - return { - avgFeesPaid: accelerations.filter(acc => acc.status === 'completed' && acc.lastUpdated < (Date.now() - (24 * 60 * 60 * 1000))).reduce((total, acc) => total + acc.feePaid, 0) / accelerations.length - }; - }) - ); - this.statsObservable$ = combineLatest([ (this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })), this.apiService.getHistoricalBlockFees$(this.miningWindowPreference), ]).pipe( tap(([accelerations, blockFeesResponse]) => { this.prepareChartOptions(accelerations, blockFeesResponse.body); - this.isLoading = false; }), map(([accelerations, blockFeesResponse]) => { return { @@ -121,11 +113,13 @@ export class AccelerationFeesGraphComponent implements OnInit { ]).pipe( tap(([accelerations, blockFeesResponse]) => { this.prepareChartOptions(accelerations, blockFeesResponse.body); - this.isLoading = false; - this.cd.markForCheck(); }) ); } + this.statsSubscription = this.statsObservable$.subscribe(() => { + this.isLoading = false; + this.cd.markForCheck(); + }); } prepareChartOptions(accelerations, blockFees) { @@ -143,6 +137,8 @@ export class AccelerationFeesGraphComponent implements OnInit { } let last = null; + let minValue = Infinity; + let maxValue = 0; const data = []; for (const val of blockFees) { if (last == null) { @@ -151,31 +147,29 @@ export class AccelerationFeesGraphComponent implements OnInit { let totalFeeDelta = 0; let totalFeePaid = 0; let totalCount = 0; + let blockCount = 0; while (last <= val.avgHeight) { + blockCount++; totalFeeDelta += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feeDelta, 0); totalFeePaid += (blockAccelerations[last] || []).reduce((total, acc) => total + acc.feePaid, 0); totalCount += (blockAccelerations[last] || []).length; last++; } + minValue = Math.min(minValue, val.avgFees); + maxValue = Math.max(maxValue, val.avgFees); data.push({ ...val, feeDelta: totalFeeDelta, - feePaid: totalFeePaid, - accelerations: totalCount, + avgFeePaid: (totalFeePaid / blockCount), + accelerations: totalCount / blockCount, }); } this.chartOptions = { title: title, color: [ - '#1E88E5', - new graphic.LinearGradient(0, 0, 0, 0.65, [ - { offset: 0, color: '#F4511E' }, - { offset: 0.25, color: '#FB8C00' }, - { offset: 0.5, color: '#FFB300' }, - { offset: 0.75, color: '#FDD835' }, - { offset: 1, color: '#7CB342' } - ]), + '#8F5FF6', + '#6b6b6b', ], animation: false, grid: { @@ -205,11 +199,11 @@ export class AccelerationFeesGraphComponent implements OnInit { let tooltip = ` ${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}
`; - for (const tick of data) { - if (tick.seriesIndex === 1) { + for (const tick of data.reverse()) { + if (tick.data[1] >= 1_000_000) { + tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1] / 100_000_000, this.locale, '1.0-3')} BTC
`; + } else { tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')} sats
`; - } else if (tick.seriesIndex === 0) { - tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.0-0')}
`; } } @@ -224,24 +218,35 @@ export class AccelerationFeesGraphComponent implements OnInit { }, xAxis: data.length === 0 ? undefined : { - type: 'time', - splitNumber: this.isMobile() ? 5 : 10, + name: this.widget ? undefined : formatterXAxisLabel(this.locale, this.timespan), + nameLocation: 'middle', + nameTextStyle: { + padding: [10, 0, 0, 0], + }, + type: 'category', + boundaryGap: false, + axisLine: { onZero: true }, axisLabel: { + formatter: val => formatterXAxisTimeCategory(this.locale, this.timespan, parseInt(val, 10)), + align: 'center', + fontSize: 11, + lineHeight: 12, hideOverlap: true, - } + padding: [0, 5], + }, }, - legend: (this.widget || data.length === 0) ? undefined : { + legend: { data: [ { - name: 'Total accelerations', + name: 'In-band fees per block', inactiveColor: 'rgb(110, 112, 121)', - textStyle: { + textStyle: { color: 'white', }, icon: 'roundRect', }, { - name: 'Out-of-band fees paid', + name: 'Out-of-band fees per block', inactiveColor: 'rgb(110, 112, 121)', textStyle: { color: 'white', @@ -249,6 +254,11 @@ export class AccelerationFeesGraphComponent implements OnInit { icon: 'roundRect', }, ], + selected: { + 'In-band fees per block': false, + 'Out-of-band fees per block': true, + }, + show: !this.widget, }, yAxis: data.length === 0 ? undefined : [ { @@ -288,35 +298,23 @@ export class AccelerationFeesGraphComponent implements OnInit { series: data.length === 0 ? undefined : [ { legendHoverLink: false, - zlevel: 0, - yAxisIndex: 1, - name: 'Total accelerations', - data: data.map(block => [block.timestamp * 1000, block.accelerations, block.avgHeight]), - type: 'line', - symbol: 'none', - areaStyle: { - color: '#1E88E5', - opacity: 0.5 - }, - lineStyle: { - width: 1, - opacity: 1, - }, - step: 'middle', + zlevel: 1, + name: 'Out-of-band fees per block', + data: data.map(block => [block.timestamp * 1000, block.avgFeePaid, block.avgHeight]), + stack: 'Total', + type: 'bar', + barWidth: '100%', + large: true, }, { legendHoverLink: false, - zlevel: 1, - yAxisIndex: 0, - name: 'Out-of-band fees paid', - data: data.map(block => [block.timestamp * 1000, block.feePaid, block.avgHeight]), - type: 'line', - smooth: 0.25, - symbol: 'none', - lineStyle: { - width: 2, - opacity: 1, - } + zlevel: 0, + name: 'In-band fees per block', + data: data.map(block => [block.timestamp * 1000, block.avgFees, block.avgHeight]), + stack: 'Total', + type: 'bar', + barWidth: '100%', + large: true, }, ], dataZoom: (this.widget || data.length === 0 )? undefined : [{ @@ -344,6 +342,17 @@ export class AccelerationFeesGraphComponent implements OnInit { } }, }], + visualMap: { + type: 'continuous', + min: minValue, + max: maxValue, + dimension: 1, + seriesIndex: 1, + show: false, + inRange: { + color: ['#F4511E7f', '#FB8C007f', '#FFB3007f', '#FDD8357f', '#7CB3427f'].reverse() // Gradient color range + } + }, }; } @@ -372,4 +381,10 @@ export class AccelerationFeesGraphComponent implements OnInit { this.chartOptions.backgroundColor = 'none'; this.chartInstance.setOption(this.chartOptions); } + + ngOnDestroy(): void { + if (this.statsSubscription) { + this.statsSubscription.unsubscribe(); + } + } } diff --git a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts index 51f7360e1..d83303619 100644 --- a/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts +++ b/frontend/src/app/components/acceleration/acceleration-stats/acceleration-stats.component.ts @@ -12,7 +12,7 @@ import { Acceleration } from '../../../interfaces/node-api.interface'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccelerationStatsComponent implements OnInit { - @Input() timespan: '24h' | '1w' = '24h'; + @Input() timespan: '24h' | '1w' | '1m' = '24h'; @Input() accelerations$: Observable; public accelerationStats$: Observable; diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index a64c4724b..4a864b024 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -8,9 +8,16 @@ - - - + + + + + + + + + + @@ -19,20 +26,33 @@ - - - - + + + + + + + + + + + @@ -63,7 +83,8 @@
- There are no accelerations show here yet! + There are no active accelerations yet! + There are no accelerations show here yet!
diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss index 55cecdcbd..69aae18cc 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.scss @@ -50,7 +50,7 @@ tr, td, th { } .txid { - width: 30%; + width: 25%; @media (max-width: 1100px) { padding-right: 10px; } @@ -64,10 +64,18 @@ tr, td, th { } .fee { - width: 25%; + width: 35%; } -.fee-delta { +.block { + width: 20%; +} + +.bid { + width: 30%; +} + +.time { width: 25%; } 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 0dbba3219..91b721db6 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 @@ -7,7 +7,7 @@
- Pending accelerations + Active accelerations
@@ -18,16 +18,16 @@
- +
Acceleration stats  - (1008 blocks) + (1 month)
- +
@@ -54,11 +54,23 @@
- + + + +
-
Pending Accelerations
+
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 c3a40730b..79a77a600 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 @@ -56,7 +56,7 @@ export class AcceleratorDashboardComponent implements OnInit { this.accelerations$ = this.stateService.chainTip$.pipe( distinctUntilChanged(), switchMap((chainTip) => { - return this.apiService.getAccelerationHistory$({ timeframe: '1w' }); + return this.apiService.getAccelerationHistory$({ timeframe: '1m' }); }), catchError((e) => { return of([]); From 2cddc5fdd8d55d15e6ca2ea436d111671b67af7c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Fri, 8 Dec 2023 13:23:51 +0000 Subject: [PATCH 23/23] accelerations: not yet --- .../accelerations-list/accelerations-list.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html index 4a864b024..32012d363 100644 --- a/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html +++ b/frontend/src/app/components/acceleration/accelerations-list/accelerations-list.component.html @@ -83,8 +83,8 @@
- There are no active accelerations yet! - There are no accelerations show here yet! + There are no active accelerations + There are no recent accelerations
TXIDFinal FeeMax BidStatusFee RateAcceleration BidRequestedOut-of-band FeeBlockStatus
- {{ (acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee) | number }} sat - - ~ - - {{ acceleration.feeDelta | number }} sat - - Pending - Mined - Canceled - + + + {{ (acceleration.feeDelta) | number }} sat + + + + {{ (acceleration.feePaid) | number }} sat + + ~ + + {{ acceleration.blockHeight }} + + Pending + Mined + Canceled +