Add USD serie in block fee/reward charts

This commit is contained in:
nymkappa
2022-06-06 10:14:40 +02:00
parent 7257539482
commit bda8b4612b
11 changed files with 275 additions and 75 deletions

View File

@@ -13,6 +13,7 @@ import { SharedModule } from './shared/shared.module';
import { StorageService } from './services/storage.service';
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
import { LanguageService } from './services/language.service';
import { FiatShortenerPipe } from './shared/pipes/fiat-shortener.pipe';
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
@@ -37,6 +38,7 @@ import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe
StorageService,
LanguageService,
ShortenStringPipe,
FiatShortenerPipe,
CapAddressPipe,
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
],

View File

@@ -180,8 +180,8 @@ export class BlockFeeRatesGraphComponent implements OnInit {
}
let tooltip = `<b style="color: white; margin-left: 2px">${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}</b><br>`;
for (const pool of data.reverse()) {
tooltip += `${pool.marker} ${pool.seriesName}: ${pool.data[1]} sats/vByte<br>`;
for (const rate of data.reverse()) {
tooltip += `${rate.marker} ${rate.seriesName}: ${rate.data[1]} sats/vByte<br>`;
}
if (['24h', '3d'].includes(this.timespan)) {

View File

@@ -4,12 +4,13 @@ import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from 'src/app/services/api.service';
import { SeoService } from 'src/app/services/seo.service';
import { formatNumber } from '@angular/common';
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
import { FormBuilder, FormGroup } from '@angular/forms';
import { download, formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from 'src/app/shared/graphs.utils';
import { StorageService } from 'src/app/services/storage.service';
import { MiningService } from 'src/app/services/mining.service';
import { ActivatedRoute } from '@angular/router';
import { FiatShortenerPipe } from 'src/app/shared/pipes/fiat-shortener.pipe';
@Component({
selector: 'app-block-fees-graph',
@@ -51,6 +52,7 @@ export class BlockFeesGraphComponent implements OnInit {
private storageService: StorageService,
private miningService: MiningService,
private route: ActivatedRoute,
private fiatShortenerPipe: FiatShortenerPipe,
) {
this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
this.radioGroupForm.controls.dateSpan.setValue('1y');
@@ -82,6 +84,7 @@ export class BlockFeesGraphComponent implements OnInit {
tap((response) => {
this.prepareChartOptions({
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]),
blockFeesUSD: response.body.filter(val => val.usd > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.usd, val.avgHeight]),
});
this.isLoading = false;
}),
@@ -98,16 +101,17 @@ export class BlockFeesGraphComponent implements OnInit {
prepareChartOptions(data) {
this.chartOptions = {
animation: false,
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' }
new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#00ACC1' },
{ offset: 1, color: '#0D47A1' },
]),
new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#FDD835' },
{ offset: 1, color: '#FB8C00' },
]),
],
animation: false,
grid: {
top: 30,
bottom: 80,
@@ -128,28 +132,52 @@ export class BlockFeesGraphComponent implements OnInit {
align: 'left',
},
borderColor: '#000',
formatter: (ticks) => {
let tooltip = `<b style="color: white; margin-left: 2px">${formatterXAxis(this.locale, this.timespan, parseInt(ticks[0].axisValue, 10))}</b><br>`;
tooltip += `${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(ticks[0].data[1], this.locale, '1.3-3')} BTC`;
tooltip += `<br>`;
formatter: function (data) {
if (data.length <= 0) {
return '';
}
let tooltip = `<b style="color: white; margin-left: 2px">
${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}</b><br>`;
if (['24h', '3d'].includes(this.timespan)) {
tooltip += `<small>` + $localize`At block: ${ticks[0].data[2]}` + `</small>`;
} else {
tooltip += `<small>` + $localize`Around block: ${ticks[0].data[2]}` + `</small>`;
for (const tick of data) {
if (tick.seriesIndex === 0) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
} else if (tick.seriesIndex === 1) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
}
}
tooltip += `<small>* On average around block ${data[0].data[2]}</small>`;
return tooltip;
}
}.bind(this)
},
xAxis: {
name: formatterXAxisLabel(this.locale, this.timespan),
nameLocation: 'middle',
nameTextStyle: {
padding: [10, 0, 0, 0],
},
xAxis: data.blockFees.length === 0 ? undefined :
{
type: 'time',
splitNumber: this.isMobile() ? 5 : 10,
axisLabel: {
hideOverlap: true,
}
},
legend: {
data: [
{
name: 'Fees BTC',
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
},
icon: 'roundRect',
},
{
name: 'Fees USD',
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
},
icon: 'roundRect',
},
],
},
yAxis: [
{
@@ -160,6 +188,9 @@ export class BlockFeesGraphComponent implements OnInit {
return `${val} BTC`;
}
},
max: (value) => {
return Math.floor(value.max * 2 * 10) / 10;
},
splitLine: {
lineStyle: {
type: 'dotted',
@@ -168,18 +199,47 @@ export class BlockFeesGraphComponent implements OnInit {
}
},
},
{
type: 'value',
position: 'right',
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: function(val) {
return this.fiatShortenerPipe.transform(val);
}.bind(this)
},
splitLine: {
show: false,
},
},
],
series: [
{
legendHoverLink: false,
zlevel: 0,
name: $localize`:@@c20172223f84462032664d717d739297e5a9e2fe:Fees`,
showSymbol: false,
symbol: 'none',
yAxisIndex: 0,
name: 'Fees BTC',
data: data.blockFees,
type: 'line',
smooth: 0.25,
symbol: 'none',
areaStyle: {
opacity: 0.25,
},
},
{
legendHoverLink: false,
zlevel: 1,
yAxisIndex: 1,
name: 'Fees USD',
data: data.blockFeesUSD,
type: 'line',
smooth: 0.25,
symbol: 'none',
lineStyle: {
width: 2,
},
opacity: 0.75,
}
},
],
dataZoom: [{

View File

@@ -4,12 +4,13 @@ import { Observable } from 'rxjs';
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
import { ApiService } from 'src/app/services/api.service';
import { SeoService } from 'src/app/services/seo.service';
import { formatNumber } from '@angular/common';
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
import { FormBuilder, FormGroup } from '@angular/forms';
import { download, formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils';
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from 'src/app/shared/graphs.utils';
import { MiningService } from 'src/app/services/mining.service';
import { StorageService } from 'src/app/services/storage.service';
import { ActivatedRoute } from '@angular/router';
import { FiatShortenerPipe } from 'src/app/shared/pipes/fiat-shortener.pipe';
@Component({
selector: 'app-block-rewards-graph',
@@ -51,6 +52,7 @@ export class BlockRewardsGraphComponent implements OnInit {
private miningService: MiningService,
private storageService: StorageService,
private route: ActivatedRoute,
private fiatShortenerPipe: FiatShortenerPipe,
) {
}
@@ -80,6 +82,7 @@ export class BlockRewardsGraphComponent implements OnInit {
tap((response) => {
this.prepareChartOptions({
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]),
blockRewardsUSD: response.body.filter(val => val.usd > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.usd, val.avgHeight]),
});
this.isLoading = false;
}),
@@ -95,15 +98,18 @@ export class BlockRewardsGraphComponent implements OnInit {
}
prepareChartOptions(data) {
const scaleFactor = 0.1;
this.chartOptions = {
animation: false,
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' }
new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#00ACC1' },
{ offset: 1, color: '#0D47A1' },
]),
new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#FDD835' },
{ offset: 1, color: '#FB8C00' },
]),
],
grid: {
@@ -126,33 +132,55 @@ export class BlockRewardsGraphComponent implements OnInit {
align: 'left',
},
borderColor: '#000',
formatter: (ticks) => {
let tooltip = `<b style="color: white; margin-left: 2px">${formatterXAxis(this.locale, this.timespan, parseInt(ticks[0].axisValue, 10))}</b><br>`;
tooltip += `${ticks[0].marker} ${ticks[0].seriesName}: ${formatNumber(ticks[0].data[1], this.locale, '1.3-3')} BTC`;
tooltip += `<br>`;
formatter: function (data) {
if (data.length <= 0) {
return '';
}
let tooltip = `<b style="color: white; margin-left: 2px">
${formatterXAxis(this.locale, this.timespan, parseInt(data[0].axisValue, 10))}</b><br>`;
if (['24h', '3d'].includes(this.timespan)) {
tooltip += `<small>` + $localize`At block: ${ticks[0].data[2]}` + `</small>`;
} else {
tooltip += `<small>` + $localize`Around block: ${ticks[0].data[2]}` + `</small>`;
for (const tick of data) {
if (tick.seriesIndex === 0) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
} else if (tick.seriesIndex === 1) {
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
}
}
tooltip += `<small>* On average around block ${data[0].data[2]}</small>`;
return tooltip;
}
}.bind(this)
},
xAxis: {
name: formatterXAxisLabel(this.locale, this.timespan),
nameLocation: 'middle',
nameTextStyle: {
padding: [10, 0, 0, 0],
},
xAxis: data.blockRewards.length === 0 ? undefined :
{
type: 'time',
splitNumber: this.isMobile() ? 5 : 10,
axisLabel: {
hideOverlap: true,
}
},
legend: {
data: [
{
name: 'Rewards BTC',
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
},
icon: 'roundRect',
},
{
name: 'Rewards USD',
inactiveColor: 'rgb(110, 112, 121)',
textStyle: {
color: 'white',
},
icon: 'roundRect',
},
],
},
yAxis: [
{
min: value => Math.round(10 * value.min * 0.99) / 10,
max: value => Math.round(10 * value.max * 1.01) / 10,
type: 'value',
axisLabel: {
color: 'rgb(110, 112, 121)',
@@ -160,6 +188,12 @@ export class BlockRewardsGraphComponent implements OnInit {
return `${val} BTC`;
}
},
min: (value) => {
return Math.round(value.min * (1.0 - scaleFactor) * 10) / 10;
},
max: (value) => {
return Math.round(value.max * (1.0 + scaleFactor) * 10) / 10;
},
splitLine: {
lineStyle: {
type: 'dotted',
@@ -168,18 +202,53 @@ export class BlockRewardsGraphComponent implements OnInit {
}
},
},
{
min: (value) => {
return Math.round(value.min * (1.0 - scaleFactor) * 10) / 10;
},
max: (value) => {
return Math.round(value.max * (1.0 + scaleFactor) * 10) / 10;
},
type: 'value',
position: 'right',
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: function(val) {
return this.fiatShortenerPipe.transform(val);
}.bind(this)
},
splitLine: {
show: false,
},
},
],
series: [
{
legendHoverLink: false,
zlevel: 0,
name: $localize`:@@12f86e6747a5ad39e62d3480ddc472b1aeab5b76:Reward`,
showSymbol: false,
symbol: 'none',
yAxisIndex: 0,
name: 'Rewards BTC',
data: data.blockRewards,
type: 'line',
smooth: 0.25,
symbol: 'none',
areaStyle: {
opacity: 0.25,
},
},
{
legendHoverLink: false,
zlevel: 1,
yAxisIndex: 1,
name: 'Rewards USD',
data: data.blockRewardsUSD,
type: 'line',
smooth: 0.25,
symbol: 'none',
lineStyle: {
width: 2,
},
opacity: 0.75,
}
},
],
dataZoom: [{

View File

@@ -351,6 +351,7 @@ export class HashrateChartComponent implements OnInit {
series: data.hashrates.length === 0 ? [] : [
{
zlevel: 0,
yAxisIndex: 0,
name: $localize`:@@79a9dc5b1caca3cbeb1733a19515edacc5fc7920:Hashrate`,
showSymbol: false,
symbol: 'none',

View File

@@ -0,0 +1,37 @@
import { formatCurrency, getCurrencySymbol } from '@angular/common';
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fiatShortener'
})
export class FiatShortenerPipe implements PipeTransform {
constructor(
@Inject(LOCALE_ID) public locale: string
) {}
transform(num: number, ...args: any[]): unknown {
const digits = args[0] || 1;
const unit = args[1] || undefined;
if (num < 1000) {
return num.toFixed(digits);
}
const lookup = [
{ value: 1, symbol: '' },
{ value: 1e3, symbol: 'k' },
{ value: 1e6, symbol: 'M' },
{ value: 1e9, symbol: 'G' },
{ value: 1e12, symbol: 'T' },
{ value: 1e15, symbol: 'P' },
{ value: 1e18, symbol: 'E' }
];
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
const item = lookup.slice().reverse().find((item) => num >= item.value);
let result = item ? (num / item.value).toFixed(digits).replace(rx, '$1') : '0';
result = formatCurrency(parseInt(result, 10), this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0');
return result + item.symbol;
}
}