Add share % in pie chart label
This commit is contained in:
parent
5b32ab6dde
commit
8eaa9b3c7b
@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<form [formGroup]="radioGroupForm" class="formRadioGroup">
|
<form [formGroup]="radioGroupForm" class="formRadioGroup">
|
||||||
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"
|
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
|
||||||
(change)="onChangeWindowPreference($event)">
|
|
||||||
<label ngbButtonLabel class="btn-primary btn-sm">
|
<label ngbButtonLabel class="btn-primary btn-sm">
|
||||||
<input ngbButton type="radio" [value]="'1d'" [routerLink]="['/pools' | relativeUrl]" fragment="1d"> 1D
|
<input ngbButton type="radio" [value]="'1d'" [routerLink]="['/pools' | relativeUrl]" fragment="1d"> 1D
|
||||||
</label>
|
</label>
|
||||||
@ -54,7 +53,7 @@
|
|||||||
<th i18n="latest-blocks.mined">Block Count (%)</th>
|
<th i18n="latest-blocks.mined">Block Count (%)</th>
|
||||||
<th class="d-none d-md-block" i18n="latest-blocks.transactions">Empty Blocks (%)</th>
|
<th class="d-none d-md-block" i18n="latest-blocks.transactions">Empty Blocks (%)</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody *ngIf="(miningStatsEmitter$ | async) as miningStats">
|
<tbody *ngIf="(miningStatsObservable$ | async) as miningStats">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="d-none d-md-block">-</td>
|
<td class="d-none d-md-block">-</td>
|
||||||
<td><!-- LOGO --></td>
|
<td><!-- LOGO --></td>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { EChartsOption } from 'echarts';
|
import { EChartsOption } from 'echarts';
|
||||||
import { BehaviorSubject, Subscription } from 'rxjs';
|
import { combineLatest, Observable, of } from 'rxjs';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { catchError, skip, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { StorageService } from 'src/app/services/storage.service';
|
import { StorageService } from '../..//services/storage.service';
|
||||||
import { MiningService, MiningStats } from '../../services/mining.service';
|
import { MiningService, MiningStats } from '../../services/mining.service';
|
||||||
|
import { StateService } from '../../services/state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pool-ranking',
|
selector: 'app-pool-ranking',
|
||||||
@ -22,79 +23,74 @@ export class PoolRankingComponent implements OnInit, OnDestroy {
|
|||||||
poolsWindowPreference: string;
|
poolsWindowPreference: string;
|
||||||
radioGroupForm: FormGroup;
|
radioGroupForm: FormGroup;
|
||||||
|
|
||||||
miningStats!: MiningStats;
|
|
||||||
miningStatsEmitter$ = new BehaviorSubject<MiningStats>(this.miningStats);
|
|
||||||
blocksSubscription: Subscription;
|
|
||||||
miningSubscription: Subscription;
|
|
||||||
|
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
chartOptions: EChartsOption = {};
|
chartOptions: EChartsOption = {};
|
||||||
chartInitOptions = {
|
chartInitOptions = {
|
||||||
renderer: 'svg'
|
renderer: 'svg'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
miningStatsObservable$: Observable<MiningStats>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private miningService: MiningService,
|
private miningService: MiningService,
|
||||||
) {
|
) {
|
||||||
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference').trim() : '2h';
|
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1d';
|
||||||
|
|
||||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
|
||||||
this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
|
this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.refreshMiningStats();
|
// When...
|
||||||
this.watchBlocks();
|
this.miningStatsObservable$ = combineLatest([
|
||||||
|
// ...a new block is mined
|
||||||
|
this.stateService.blocks$
|
||||||
|
.pipe(
|
||||||
|
// (we always receives some blocks at start so only trigger for the last one)
|
||||||
|
skip(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT - 1),
|
||||||
|
),
|
||||||
|
// ...or we change the timespan
|
||||||
|
this.radioGroupForm.get('dateSpan').valueChanges
|
||||||
|
.pipe(
|
||||||
|
startWith(this.poolsWindowPreference), // (trigger when the page loads)
|
||||||
|
tap((value) => {
|
||||||
|
this.storageService.setValue('poolsWindowPreference', value);
|
||||||
|
this.poolsWindowPreference = value;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
])
|
||||||
|
// ...then refresh the mining stats
|
||||||
|
.pipe(
|
||||||
|
switchMap(() => {
|
||||||
|
this.isLoading = true;
|
||||||
|
return this.miningService.getMiningStats(this.getSQLInterval(this.poolsWindowPreference))
|
||||||
|
.pipe(
|
||||||
|
catchError((e) => of(this.getEmptyMiningStat()))
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
tap(data => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.prepareChartOptions(data);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.blocksSubscription.unsubscribe();
|
|
||||||
this.miningSubscription.unsubscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshMiningStats() {
|
generatePoolsChartSerieData(miningStats) {
|
||||||
this.miningSubscription = this.miningService.getMiningStats(this.getSQLInterval(this.poolsWindowPreference))
|
|
||||||
.subscribe(async data => {
|
|
||||||
this.miningStats = data;
|
|
||||||
this.miningStatsEmitter$.next(this.miningStats);
|
|
||||||
this.prepareChartOptions();
|
|
||||||
this.isLoading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.miningSubscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
watchBlocks() {
|
|
||||||
this.blocksSubscription = this.stateService.blocks$
|
|
||||||
.subscribe(() => {
|
|
||||||
if (!this.miningStats) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.refreshMiningStats();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeWindowPreference(e) {
|
|
||||||
this.storageService.setValue('poolsWindowPreference', e.target.value);
|
|
||||||
this.poolsWindowPreference = e.target.value;
|
|
||||||
this.isLoading = true;
|
|
||||||
this.refreshMiningStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
generatePoolsChartSerieData() {
|
|
||||||
const poolShareThreshold = 0.5; // Do not draw pools which hashrate share is lower than that
|
const poolShareThreshold = 0.5; // Do not draw pools which hashrate share is lower than that
|
||||||
const data: object[] = [];
|
const data: object[] = [];
|
||||||
|
|
||||||
this.miningStats.pools.forEach((pool) => {
|
miningStats.pools.forEach((pool) => {
|
||||||
if (parseFloat(pool.share) < poolShareThreshold) {
|
if (parseFloat(pool.share) < poolShareThreshold) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.push({
|
data.push({
|
||||||
value: pool.share,
|
value: pool.share,
|
||||||
name: pool.name,
|
name: pool.name + ` (${pool.share}%)`,
|
||||||
label: { color: '#FFFFFF' },
|
label: { color: '#FFFFFF' },
|
||||||
tooltip: {
|
tooltip: {
|
||||||
formatter: () => {
|
formatter: () => {
|
||||||
@ -113,7 +109,7 @@ export class PoolRankingComponent implements OnInit, OnDestroy {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareChartOptions() {
|
prepareChartOptions(miningStats) {
|
||||||
this.chartOptions = {
|
this.chartOptions = {
|
||||||
title: {
|
title: {
|
||||||
text: (this.poolsWindowPreference === '1d') ? 'Hashrate distribution' : 'Block distribution',
|
text: (this.poolsWindowPreference === '1d') ? 'Hashrate distribution' : 'Block distribution',
|
||||||
@ -143,7 +139,7 @@ export class PoolRankingComponent implements OnInit, OnDestroy {
|
|||||||
name: 'Mining pool',
|
name: 'Mining pool',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['30%', '70%'],
|
radius: ['30%', '70%'],
|
||||||
data: this.generatePoolsChartSerieData(),
|
data: this.generatePoolsChartSerieData(miningStats),
|
||||||
labelLine: {
|
labelLine: {
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
@ -193,5 +189,21 @@ export class PoolRankingComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default mining stats if something goes wrong
|
||||||
|
*/
|
||||||
|
getEmptyMiningStat() {
|
||||||
|
return {
|
||||||
|
lastEstimatedHashrate: 'Error',
|
||||||
|
blockCount: 0,
|
||||||
|
totalEmptyBlock: 0,
|
||||||
|
totalEmptyBlockRatio: '',
|
||||||
|
pools: [],
|
||||||
|
miningUnits: {
|
||||||
|
hashrateDivider: 1,
|
||||||
|
hashrateUnit: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,22 +53,22 @@ export interface LiquidPegs {
|
|||||||
export interface ITranslators { [language: string]: string; }
|
export interface ITranslators { [language: string]: string; }
|
||||||
|
|
||||||
export interface SinglePoolStats {
|
export interface SinglePoolStats {
|
||||||
pooldId: number,
|
pooldId: number;
|
||||||
name: string,
|
name: string;
|
||||||
link: string,
|
link: string;
|
||||||
blockCount: number,
|
blockCount: number;
|
||||||
emptyBlocks: number,
|
emptyBlocks: number;
|
||||||
rank: number,
|
rank: number;
|
||||||
share: string,
|
share: string;
|
||||||
lastEstimatedHashrate: string,
|
lastEstimatedHashrate: string;
|
||||||
emptyBlockRatio: string,
|
emptyBlockRatio: string;
|
||||||
logo: string,
|
logo: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PoolsStats {
|
export interface PoolsStats {
|
||||||
blockCount: number,
|
blockCount: number;
|
||||||
lastEstimatedHashrate: number,
|
lastEstimatedHashrate: number;
|
||||||
pools: SinglePoolStats[],
|
pools: SinglePoolStats[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITranslators { [language: string]: string; }
|
export interface ITranslators { [language: string]: string; }
|
||||||
|
@ -6,17 +6,17 @@ import { ApiService } from '../services/api.service';
|
|||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
|
|
||||||
export interface MiningUnits {
|
export interface MiningUnits {
|
||||||
hashrateDivider: number,
|
hashrateDivider: number;
|
||||||
hashrateUnit: string,
|
hashrateUnit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MiningStats {
|
export interface MiningStats {
|
||||||
lastEstimatedHashrate: string,
|
lastEstimatedHashrate: string;
|
||||||
blockCount: number,
|
blockCount: number;
|
||||||
totalEmptyBlock: number,
|
totalEmptyBlock: number;
|
||||||
totalEmptyBlockRatio: string,
|
totalEmptyBlockRatio: string;
|
||||||
pools: SinglePoolStats[],
|
pools: SinglePoolStats[];
|
||||||
miningUnits: MiningUnits,
|
miningUnits: MiningUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -40,13 +40,13 @@ export class MiningService {
|
|||||||
*/
|
*/
|
||||||
public getMiningUnits(): MiningUnits {
|
public getMiningUnits(): MiningUnits {
|
||||||
const powerTable = {
|
const powerTable = {
|
||||||
0: "H/s",
|
0: 'H/s',
|
||||||
3: "kH/s",
|
3: 'kH/s',
|
||||||
6: "MH/s",
|
6: 'MH/s',
|
||||||
9: "GH/s",
|
9: 'GH/s',
|
||||||
12: "TH/s",
|
12: 'TH/s',
|
||||||
15: "PH/s",
|
15: 'PH/s',
|
||||||
18: "EH/s",
|
18: 'EH/s',
|
||||||
};
|
};
|
||||||
|
|
||||||
// I think it's fine to hardcode this since we don't have x1000 hashrate jump everyday
|
// I think it's fine to hardcode this since we don't have x1000 hashrate jump everyday
|
||||||
@ -77,7 +77,7 @@ export class MiningService {
|
|||||||
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
|
emptyBlockRatio: (poolStat.emptyBlocks / poolStat.blockCount * 100).toFixed(2),
|
||||||
logo: `./resources/mining-pools/` + poolStat.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg',
|
logo: `./resources/mining-pools/` + poolStat.name.toLowerCase().replace(' ', '').replace('.', '') + '.svg',
|
||||||
...poolStat
|
...poolStat
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user