[graph] add price chart in available graphs

This commit is contained in:
nymkappa 2026-03-12 21:47:09 +01:00
parent 968080ca26
commit 31431d3a11
No known key found for this signature in database
GPG key ID: A7895A957BA87DBA
3 changed files with 47 additions and 39 deletions

View file

@ -1,4 +1,4 @@
<div *ngIf="stateService.env.MINING_DASHBOARD || stateService.env.LIGHTNING || stateService.env.ACCELERATOR" class="mb-3 d-flex menu" [style]="{'flex-wrap': flexWrap ? 'wrap' : ''}">
<div *ngIf="stateService.env.MINING_DASHBOARD || stateService.env.LIGHTNING || stateService.env.ACCELERATOR || (stateService.env.HISTORICAL_PRICE && isMainnet)" class="mb-3 d-flex menu" [style]="{'flex-wrap': flexWrap ? 'wrap' : ''}">
<a routerLinkActive="active" class="btn btn-primary w-33"
[routerLink]="['/graphs/mempool' | relativeUrl]">Mempool</a>
@ -52,6 +52,12 @@
i18n="accelerator.acceleration-fees">Acceleration Fees</a>
</div>
</div>
@if (stateService.env.HISTORICAL_PRICE && isMainnet) {
<a routerLinkActive="active" class="btn btn-primary w-33"
[routerLink]="['/graphs/price' | relativeUrl]" i18n="price">Price</a>
}
</div>
<router-outlet></router-outlet>

View file

@ -3,7 +3,7 @@
<div [class]="widget === false ? 'full-container' : ''">
<div *ngIf="widget">
<div class="pool-distribution" *ngIf="(statsObservable$ | async) as stats; else loadingStats">
<div class="pool-distribution" *ngIf="(pricesObservable$ | async) as stats; else loadingStats">
<div class="item">
<h5 class="card-title" i18n="price-chart.bitcoin-price">Bitcoin Price</h5>
<p class="card-text">
@ -27,24 +27,24 @@
</button>
</div>
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(statsObservable$ | async) as stats">
<form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="pricesObservable$ | async">
<div class="btn-group btn-group-toggle" name="radioBasic">
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 4320" [class.active]="radioGroupForm.get('dateSpan').value === '1m'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '1m'">
<input type="radio" [value]="'1m'" fragment="1m" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 1M
</label>
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 12960" [class.active]="radioGroupForm.get('dateSpan').value === '3m'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '3m'">
<input type="radio" [value]="'3m'" fragment="3m" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 3M
</label>
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 25920" [class.active]="radioGroupForm.get('dateSpan').value === '6m'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '6m'">
<input type="radio" [value]="'6m'" fragment="6m" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 6M
</label>
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 52560" [class.active]="radioGroupForm.get('dateSpan').value === '1y'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '1y'">
<input type="radio" [value]="'1y'" fragment="1y" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 1Y
</label>
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 105120" [class.active]="radioGroupForm.get('dateSpan').value === '2y'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '2y'">
<input type="radio" [value]="'2y'" fragment="2y" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 2Y
</label>
<label class="btn btn-primary btn-sm" *ngIf="stats.blockCount >= 157680" [class.active]="radioGroupForm.get('dateSpan').value === '3y'">
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === '3y'">
<input type="radio" [value]="'3y'" fragment="3y" [routerLink]="['/graphs/price' | relativeUrl]" formControlName="dateSpan"> 3Y
</label>
<label class="btn btn-primary btn-sm" [class.active]="radioGroupForm.get('dateSpan').value === 'all'">

View file

@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
import { echarts, EChartsOption } from '@app/graphs/echarts';
import { Observable } from 'rxjs';
import { combineLatest, Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from '@app/services/api.service';
import { SeoService } from '@app/services/seo.service';
@ -45,7 +45,7 @@ export class PriceChartComponent implements OnInit {
renderer: 'svg',
};
statsObservable$: Observable<any>;
pricesObservable$: Observable<any>;
isLoading = true;
formatNumber = formatNumber;
chartInstance: any = undefined;
@ -89,32 +89,48 @@ export class PriceChartComponent implements OnInit {
}
});
this.statsObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
.pipe(
startWith(this.radioGroupForm.controls.dateSpan.value),
switchMap((timespan) => {
this.pricesObservable$ = combineLatest([
this.radioGroupForm.get('dateSpan').valueChanges.pipe(startWith(this.radioGroupForm.controls.dateSpan.value)),
this.stateService.fiatCurrency$,
]).pipe(
switchMap(([timespan, currency]) => {
this.currency = currency;
const now = new Date();
let startTimestamp = 0;
if (timespan === '1m') {
startTimestamp = Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, now.getUTCDate());
} else if (timespan === '3m') {
startTimestamp = Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 3, now.getUTCDate());
} else if (timespan === '6m') {
startTimestamp = Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 6, now.getUTCDate());
} else if (timespan === '1y') {
startTimestamp = Date.UTC(now.getUTCFullYear() - 1, now.getUTCMonth(), now.getUTCDate());
} else if (timespan === '2y') {
startTimestamp = Date.UTC(now.getUTCFullYear() - 2, now.getUTCMonth(), now.getUTCDate());
} else if (timespan === '3y') {
startTimestamp = Date.UTC(now.getUTCFullYear() - 3, now.getUTCMonth(), now.getUTCDate());
} // 'all' keeps startTimestamp = 0
this.isLoading = true;
if (!this.widget) {
this.storageService.setValue('miningWindowPreference', timespan);
}
this.currentTimespan = timespan;
this.isLoading = true;
return this.apiService.getHistoricalBlockFees$(timespan)
return this.apiService.getHistoricalPrice$(undefined, this.currency)
.pipe(
tap((response) => {
tap((response: any) => {
this.prepareChartOptions({
priceData: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val[this.currency], val.avgHeight]),
priceData: response.prices.filter((p: any) => p[this.currency] > 0 && p.time * 1000 >= startTimestamp).map((p: any) => [p.time * 1000, p[this.currency]]),
});
this.isLoading = false;
}),
map((response) => {
const priceData = response.body.filter(val => val[this.currency] > 0);
const latestPrice = priceData.length > 0 ? priceData[priceData.length - 1][this.currency] : null;
const firstPrice = priceData.length > 0 ? priceData[0][this.currency] : null;
map((response: any) => {
const priceData = response.prices.filter((p: any) => p[this.currency] > 0 && p.time * 1000 >= startTimestamp).map((p: any) => p[this.currency]);
const latestPrice = priceData.length > 0 ? priceData[0] : null;
const firstPrice = priceData.length > 0 ? priceData[priceData.length - 1] : null;
const percentChange = (latestPrice && firstPrice) ? ((latestPrice - firstPrice) / firstPrice) * 100 : 0;
return {
blockCount: parseInt(response.headers.get('x-total-count'), 10),
latestPrice: latestPrice,
percentChange: percentChange,
};
@ -179,8 +195,6 @@ export class PriceChartComponent implements OnInit {
for (const tick of data) {
tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency)}<br>`;
}
tooltip += `<small>* On average around block ${data[0].data[2]}</small>`;
return tooltip;
}.bind(this)
},
@ -192,18 +206,6 @@ export class PriceChartComponent implements OnInit {
hideOverlap: true,
}
},
// legend: data.priceData.length === 0 ? undefined : {
// data: [
// {
// name: 'BTC Price (' + this.currency + ')',
// inactiveColor: 'rgb(110, 112, 121)',
// textStyle: {
// color: 'white',
// },
// icon: 'roundRect',
// },
// ],
// },
yAxis: data.priceData.length === 0 ? undefined : [
{
type: 'value',
@ -228,7 +230,7 @@ export class PriceChartComponent implements OnInit {
// legendHoverLink: false,
zlevel: 0,
yAxisIndex: 0,
name: 'BTC Price (' + this.currency + ')',
name: `BTC${this.currency}`,
data: data.priceData,
type: 'line',
smooth: 0.25,