2022-01-17 12:54:56 +09:00
|
|
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
|
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
2022-01-14 18:09:40 +09:00
|
|
|
import { EChartsOption } from 'echarts';
|
2022-01-21 11:17:36 +09:00
|
|
|
import { combineLatest, Observable, of } from 'rxjs';
|
|
|
|
import { catchError, skip, startWith, switchMap, tap } from 'rxjs/operators';
|
|
|
|
import { StorageService } from '../..//services/storage.service';
|
2022-01-18 17:37:04 +09:00
|
|
|
import { MiningService, MiningStats } from '../../services/mining.service';
|
2022-01-21 11:17:36 +09:00
|
|
|
import { StateService } from '../../services/state.service';
|
2022-01-06 19:59:33 +09:00
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'app-pool-ranking',
|
|
|
|
templateUrl: './pool-ranking.component.html',
|
2022-01-14 18:09:40 +09:00
|
|
|
styles: [`
|
|
|
|
.loadingGraphs {
|
|
|
|
position: absolute;
|
2022-01-20 23:54:42 +09:00
|
|
|
top: 50%;
|
2022-01-14 18:09:40 +09:00
|
|
|
left: calc(50% - 16px);
|
|
|
|
z-index: 100;
|
|
|
|
}
|
|
|
|
`],
|
2022-01-06 19:59:33 +09:00
|
|
|
})
|
2022-01-17 12:54:56 +09:00
|
|
|
export class PoolRankingComponent implements OnInit, OnDestroy {
|
2022-01-06 19:59:33 +09:00
|
|
|
poolsWindowPreference: string;
|
2022-01-14 18:09:40 +09:00
|
|
|
radioGroupForm: FormGroup;
|
|
|
|
|
|
|
|
isLoading = true;
|
|
|
|
chartOptions: EChartsOption = {};
|
|
|
|
chartInitOptions = {
|
|
|
|
renderer: 'svg'
|
|
|
|
};
|
2022-01-06 19:59:33 +09:00
|
|
|
|
2022-01-21 11:17:36 +09:00
|
|
|
miningStatsObservable$: Observable<MiningStats>;
|
|
|
|
|
2022-01-06 19:59:33 +09:00
|
|
|
constructor(
|
2022-01-17 12:54:56 +09:00
|
|
|
private stateService: StateService,
|
2022-01-06 19:59:33 +09:00
|
|
|
private storageService: StorageService,
|
2022-01-14 18:09:40 +09:00
|
|
|
private formBuilder: FormBuilder,
|
|
|
|
private miningService: MiningService,
|
|
|
|
) {
|
2022-01-21 11:17:36 +09:00
|
|
|
this.poolsWindowPreference = this.storageService.getValue('poolsWindowPreference') ? this.storageService.getValue('poolsWindowPreference') : '1d';
|
2022-01-14 18:09:40 +09:00
|
|
|
this.radioGroupForm = this.formBuilder.group({ dateSpan: this.poolsWindowPreference });
|
|
|
|
this.radioGroupForm.controls.dateSpan.setValue(this.poolsWindowPreference);
|
2022-01-17 12:54:56 +09:00
|
|
|
}
|
2022-01-06 19:59:33 +09:00
|
|
|
|
2022-01-17 12:54:56 +09:00
|
|
|
ngOnInit(): void {
|
2022-01-21 11:17:36 +09:00
|
|
|
// When...
|
|
|
|
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);
|
|
|
|
})
|
|
|
|
);
|
2022-01-06 19:59:33 +09:00
|
|
|
}
|
|
|
|
|
2022-01-17 12:54:56 +09:00
|
|
|
ngOnDestroy(): void {
|
2022-01-06 19:59:33 +09:00
|
|
|
}
|
|
|
|
|
2022-01-21 11:17:36 +09:00
|
|
|
generatePoolsChartSerieData(miningStats) {
|
2022-01-14 18:09:40 +09:00
|
|
|
const poolShareThreshold = 0.5; // Do not draw pools which hashrate share is lower than that
|
|
|
|
const data: object[] = [];
|
|
|
|
|
2022-01-21 11:17:36 +09:00
|
|
|
miningStats.pools.forEach((pool) => {
|
2022-01-14 18:09:40 +09:00
|
|
|
if (parseFloat(pool.share) < poolShareThreshold) {
|
|
|
|
return;
|
2022-01-06 19:59:33 +09:00
|
|
|
}
|
2022-01-14 18:09:40 +09:00
|
|
|
data.push({
|
2022-01-17 15:34:34 +09:00
|
|
|
value: pool.share,
|
2022-01-21 11:17:36 +09:00
|
|
|
name: pool.name + ` (${pool.share}%)`,
|
2022-01-14 18:09:40 +09:00
|
|
|
label: { color: '#FFFFFF' },
|
|
|
|
tooltip: {
|
|
|
|
formatter: () => {
|
2022-01-18 17:37:04 +09:00
|
|
|
if (this.poolsWindowPreference === '1d') {
|
|
|
|
return `<u><b>${pool.name}</b></u><br>` +
|
|
|
|
pool.lastEstimatedHashrate.toString() + ' PH/s (' + pool.share + `%)
|
|
|
|
<br>(` + pool.blockCount.toString() + ` blocks)`;
|
|
|
|
} else {
|
|
|
|
return `<u><b>${pool.name}</b></u><br>` +
|
|
|
|
pool.blockCount.toString() + ` blocks`;
|
|
|
|
}
|
2022-01-14 18:09:40 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2022-01-06 19:59:33 +09:00
|
|
|
});
|
2022-01-14 18:09:40 +09:00
|
|
|
return data;
|
|
|
|
}
|
2022-01-06 19:59:33 +09:00
|
|
|
|
2022-01-21 11:17:36 +09:00
|
|
|
prepareChartOptions(miningStats) {
|
2022-01-14 18:09:40 +09:00
|
|
|
this.chartOptions = {
|
|
|
|
title: {
|
2022-01-18 17:37:04 +09:00
|
|
|
text: (this.poolsWindowPreference === '1d') ? 'Hashrate distribution' : 'Block distribution',
|
|
|
|
subtext: (this.poolsWindowPreference === '1d') ? 'Estimated from the # of blocks mined' : null,
|
2022-01-14 18:09:40 +09:00
|
|
|
left: 'center',
|
|
|
|
textStyle: {
|
|
|
|
color: '#FFFFFF',
|
|
|
|
},
|
|
|
|
subtextStyle: {
|
|
|
|
color: '#CCCCCC',
|
|
|
|
fontStyle: 'italic',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
tooltip: {
|
|
|
|
trigger: 'item'
|
|
|
|
},
|
2022-01-17 15:34:34 +09:00
|
|
|
legend: (window.innerWidth <= 767.98) ? {
|
|
|
|
bottom: '0%',
|
|
|
|
left: 'center',
|
|
|
|
textStyle: {
|
|
|
|
color: '#FFF'
|
|
|
|
}
|
|
|
|
} : null,
|
2022-01-14 18:09:40 +09:00
|
|
|
series: [
|
|
|
|
{
|
2022-01-17 15:34:34 +09:00
|
|
|
top: '5%',
|
2022-01-14 18:09:40 +09:00
|
|
|
name: 'Mining pool',
|
|
|
|
type: 'pie',
|
|
|
|
radius: ['30%', '70%'],
|
2022-01-21 11:17:36 +09:00
|
|
|
data: this.generatePoolsChartSerieData(miningStats),
|
2022-01-14 18:09:40 +09:00
|
|
|
labelLine: {
|
|
|
|
lineStyle: {
|
|
|
|
width: 2,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
label: {
|
2022-01-17 15:34:34 +09:00
|
|
|
show: (window.innerWidth > 767.98),
|
2022-01-14 18:09:40 +09:00
|
|
|
fontSize: 14,
|
|
|
|
},
|
|
|
|
itemStyle: {
|
|
|
|
borderRadius: 5,
|
|
|
|
borderWidth: 2,
|
|
|
|
borderColor: '#000',
|
|
|
|
},
|
|
|
|
emphasis: {
|
|
|
|
itemStyle: {
|
|
|
|
borderWidth: 5,
|
|
|
|
borderColor: '#000',
|
|
|
|
borderRadius: 20,
|
|
|
|
shadowBlur: 40,
|
|
|
|
shadowOffsetX: 0,
|
|
|
|
shadowColor: 'rgba(0, 0, 0, 0.75)'
|
|
|
|
},
|
|
|
|
labelLine: {
|
|
|
|
lineStyle: {
|
|
|
|
width: 3,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
getSQLInterval(uiInterval: string) {
|
|
|
|
switch (uiInterval) {
|
|
|
|
case '1d': return '1 DAY';
|
|
|
|
case '3d': return '3 DAY';
|
|
|
|
case '1w': return '1 WEEK';
|
|
|
|
case '1m': return '1 MONTH';
|
|
|
|
case '3m': return '3 MONTH';
|
|
|
|
case '6m': return '6 MONTH';
|
|
|
|
case '1y': return '1 YEAR';
|
|
|
|
case '2y': return '2 YEAR';
|
|
|
|
case '3y': return '3 YEAR';
|
|
|
|
default: return '1000 YEAR';
|
2022-01-06 19:59:33 +09:00
|
|
|
}
|
|
|
|
}
|
2022-01-14 18:09:40 +09:00
|
|
|
|
2022-01-21 11:17:36 +09:00
|
|
|
/**
|
|
|
|
* Default mining stats if something goes wrong
|
|
|
|
*/
|
|
|
|
getEmptyMiningStat() {
|
|
|
|
return {
|
|
|
|
lastEstimatedHashrate: 'Error',
|
|
|
|
blockCount: 0,
|
|
|
|
totalEmptyBlock: 0,
|
|
|
|
totalEmptyBlockRatio: '',
|
|
|
|
pools: [],
|
|
|
|
miningUnits: {
|
|
|
|
hashrateDivider: 1,
|
|
|
|
hashrateUnit: '',
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2022-01-06 19:59:33 +09:00
|
|
|
}
|
|
|
|
|