mempool/frontend/src/app/components/pool/pool.component.ts

201 lines
5.8 KiB
TypeScript
Raw Normal View History

2022-03-08 12:50:47 +01:00
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
2022-02-09 19:41:05 +09:00
import { ActivatedRoute } from '@angular/router';
2022-03-08 12:50:47 +01:00
import { EChartsOption, graphic } from 'echarts';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map, switchMap, tap, toArray } from 'rxjs/operators';
2022-02-09 19:41:05 +09:00
import { BlockExtended, PoolStat } from 'src/app/interfaces/node-api.interface';
import { ApiService } from 'src/app/services/api.service';
import { StateService } from 'src/app/services/state.service';
2022-03-08 12:50:47 +01:00
import { selectPowerOfTen } from 'src/app/bitcoin.utils';
import { formatNumber } from '@angular/common';
2022-02-08 18:56:51 +09:00
@Component({
selector: 'app-pool',
templateUrl: './pool.component.html',
styleUrls: ['./pool.component.scss'],
styles: [`
.loadingGraphs {
position: absolute;
top: 50%;
left: calc(50% - 15px);
z-index: 100;
}
`],
changeDetection: ChangeDetectionStrategy.OnPush
2022-02-08 18:56:51 +09:00
})
export class PoolComponent implements OnInit {
2022-03-08 12:50:47 +01:00
@Input() right: number | string = 45;
@Input() left: number | string = 75;
2022-02-09 19:41:05 +09:00
poolStats$: Observable<PoolStat>;
blocks$: Observable<BlockExtended[]>;
2022-03-08 12:50:47 +01:00
isLoading = true;
chartOptions: EChartsOption = {};
chartInitOptions = {
renderer: 'svg',
width: 'auto',
height: 'auto',
};
2022-02-09 19:41:05 +09:00
fromHeight: number = -1;
fromHeightSubject: BehaviorSubject<number> = new BehaviorSubject(this.fromHeight);
2022-02-09 19:41:05 +09:00
blocks: BlockExtended[] = [];
poolId: number = undefined;
2022-02-09 19:41:05 +09:00
2022-02-08 18:56:51 +09:00
constructor(
2022-03-08 12:50:47 +01:00
@Inject(LOCALE_ID) public locale: string,
2022-02-09 19:41:05 +09:00
private apiService: ApiService,
private route: ActivatedRoute,
public stateService: StateService,
) {
}
2022-02-08 18:56:51 +09:00
ngOnInit(): void {
this.poolStats$ = this.route.params.pipe(map((params) => params.poolId))
2022-02-09 19:41:05 +09:00
.pipe(
switchMap((poolId: any) => {
this.isLoading = true;
this.poolId = poolId;
return this.apiService.getPoolHashrate$(this.poolId)
2022-03-08 12:50:47 +01:00
.pipe(
switchMap((data) => {
this.isLoading = false;
2022-03-08 12:50:47 +01:00
this.prepareChartOptions(data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate]));
return poolId;
2022-03-08 12:50:47 +01:00
}),
)
}),
switchMap(() => {
if (this.blocks.length === 0) {
this.fromHeightSubject.next(undefined);
}
return this.apiService.getPoolStats$(this.poolId);
2022-02-09 19:41:05 +09:00
}),
map((poolStats) => {
let regexes = '"';
for (const regex of poolStats.pool.regexes) {
regexes += regex + '", "';
}
poolStats.pool.regexes = regexes.slice(0, -3);
poolStats.pool.addresses = poolStats.pool.addresses;
return Object.assign({
logo: `./resources/mining-pools/` + poolStats.pool.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg'
}, poolStats);
})
2022-02-09 19:41:05 +09:00
);
this.blocks$ = this.fromHeightSubject
.pipe(
distinctUntilChanged(),
switchMap((fromHeight) => {
return this.apiService.getPoolBlocks$(this.poolId, fromHeight);
}),
tap((newBlocks) => {
this.blocks = this.blocks.concat(newBlocks);
}),
map(() => this.blocks)
)
}
2022-02-09 19:41:05 +09:00
2022-03-08 12:50:47 +01:00
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' }
]),
'#D81B60',
],
grid: {
right: this.right,
left: this.left,
bottom: 60,
},
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) {
let hashratePowerOfTen: any = selectPowerOfTen(1);
let hashrate = data[0].data[1];
if (this.isMobile()) {
hashratePowerOfTen = selectPowerOfTen(data[0].data[1]);
hashrate = Math.round(data[0].data[1] / hashratePowerOfTen.divider);
}
return `
<b style="color: white; margin-left: 18px">${data[0].axisValueLabel}</b><br>
<span>${data[0].marker} ${data[0].seriesName}: ${formatNumber(hashrate, this.locale, '1.0-0')} ${hashratePowerOfTen.unit}H/s</span><br>
`;
}.bind(this)
},
xAxis: {
type: 'time',
splitNumber: (this.isMobile()) ? 5 : 10,
},
yAxis: [
{
min: function (value) {
return value.min * 0.9;
},
type: 'value',
name: 'Hashrate',
axisLabel: {
color: 'rgb(110, 112, 121)',
formatter: (val) => {
const selectedPowerOfTen: any = selectPowerOfTen(val);
const newVal = Math.round(val / selectedPowerOfTen.divider);
return `${newVal} ${selectedPowerOfTen.unit}H/s`
}
},
splitLine: {
show: false,
}
},
],
series: [
{
name: 'Hashrate',
showSymbol: false,
symbol: 'none',
data: data,
type: 'line',
lineStyle: {
width: 2,
},
},
],
};
}
isMobile() {
return (window.innerWidth <= 767.98);
}
loadMore() {
this.fromHeightSubject.next(this.blocks[this.blocks.length - 1]?.height);
2022-02-09 19:41:05 +09:00
}
trackByBlock(index: number, block: BlockExtended) {
return block.height;
2022-02-08 18:56:51 +09:00
}
}