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