Add difficulty chart timespan selection
This commit is contained in:
		
							parent
							
								
									9fa7e58d82
								
							
						
					
					
						commit
						f45103e7e3
					
				@ -69,6 +69,19 @@ class Mining {
 | 
			
		||||
      emptyBlocks: emptyBlocks,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Return the historical difficulty adjustments and oldest indexed block timestamp
 | 
			
		||||
   */
 | 
			
		||||
  public async $getHistoricalDifficulty(interval: string | null): Promise<object> {
 | 
			
		||||
    const difficultyAdjustments = await BlocksRepository.$getBlocksDifficulty(interval);
 | 
			
		||||
    const oldestBlock = new Date(await BlocksRepository.$oldestBlockTimestamp());
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      adjustments: difficultyAdjustments,
 | 
			
		||||
      oldestIndexedBlockTimestamp: oldestBlock.getTime(),
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default new Mining();
 | 
			
		||||
 | 
			
		||||
@ -232,7 +232,7 @@ class BlocksRepository {
 | 
			
		||||
 | 
			
		||||
    const connection = await DB.pool.getConnection();
 | 
			
		||||
 | 
			
		||||
    let query = `SELECT MIN(blockTimestamp) as timestamp, difficulty, height
 | 
			
		||||
    let query = `SELECT MIN(UNIX_TIMESTAMP(blockTimestamp)) as timestamp, difficulty, height
 | 
			
		||||
      FROM blocks`;
 | 
			
		||||
 | 
			
		||||
    if (interval) {
 | 
			
		||||
 | 
			
		||||
@ -577,7 +577,7 @@ class Routes {
 | 
			
		||||
 | 
			
		||||
  public async $getHistoricalDifficulty(req: Request, res: Response) {
 | 
			
		||||
    try {
 | 
			
		||||
      const stats = await BlocksRepository.$getBlocksDifficulty(req.params.interval ?? null);
 | 
			
		||||
      const stats = await mining.$getHistoricalDifficulty(req.params.interval ?? null);
 | 
			
		||||
      res.header('Pragma', 'public');
 | 
			
		||||
      res.header('Cache-control', 'public');
 | 
			
		||||
      res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,31 @@
 | 
			
		||||
    <div class="spinner-border text-light"></div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="card-header mb-0 mb-lg-4">
 | 
			
		||||
    <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(difficultyObservable$ | async) as diffChanges">
 | 
			
		||||
      <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan">
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm" [routerLink]="['/mining/difficulty' | relativeUrl]" *ngIf="diffChanges.availableTimespanDay >= 90">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'3m'" fragment="3m"> 3M
 | 
			
		||||
        </label>
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm" [routerLink]="['/mining/difficulty' | relativeUrl]" *ngIf="diffChanges.availableTimespanDay >= 180">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'6m'" fragment="6m"> 6M
 | 
			
		||||
        </label>
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm" [routerLink]="['/mining/difficulty' | relativeUrl]" *ngIf="diffChanges.availableTimespanDay >= 365">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'1y'" fragment="1y"> 1Y
 | 
			
		||||
        </label>
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm" [routerLink]="['/mining/difficulty' | relativeUrl]" *ngIf="diffChanges.availableTimespanDay >= 730">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'2y'" fragment="2y"> 2Y
 | 
			
		||||
        </label>
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm" [routerLink]="['/mining/difficulty' | relativeUrl]" *ngIf="diffChanges.availableTimespanDay >= 1095">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'3y'" fragment="3y"> 3Y
 | 
			
		||||
        </label>
 | 
			
		||||
        <label ngbButtonLabel class="btn-primary btn-sm">
 | 
			
		||||
          <input ngbButton type="radio" [value]="'all'" [routerLink]="['/mining/difficulty' | relativeUrl]" fragment="all"> ALL
 | 
			
		||||
        </label>
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <table class="table table-borderless table-sm text-center">
 | 
			
		||||
    <thead>
 | 
			
		||||
      <tr>
 | 
			
		||||
@ -14,12 +39,12 @@
 | 
			
		||||
        <th i18n="mining.change">Change</th>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </thead>
 | 
			
		||||
    <tbody *ngIf="(difficultyObservable$ | async) as diffChange">
 | 
			
		||||
      <tr *ngFor="let change of diffChange">
 | 
			
		||||
        <td><a [routerLink]="['/block' | relativeUrl, change[2]]">{{ change[2] }}</a></td>
 | 
			
		||||
        <td>‎{{ change[0] | date:'yyyy-MM-dd HH:mm' }}</td>
 | 
			
		||||
        <td>{{ formatNumber(change[1], locale, '1.2-2') }}</td>
 | 
			
		||||
        <td [style]="change[3] >= 0 ? 'color: #42B747' : 'color: #B74242'">{{ formatNumber(change[3], locale, '1.2-2') }}%</td>
 | 
			
		||||
    <tbody *ngIf="(difficultyObservable$ | async) as diffChanges">
 | 
			
		||||
      <tr *ngFor="let diffChange of diffChanges.data">
 | 
			
		||||
        <td><a [routerLink]="['/block' | relativeUrl, diffChange.height]">{{ diffChange.height }}</a></td>
 | 
			
		||||
        <td>‎{{ diffChange.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
 | 
			
		||||
        <td>{{ formatNumber(diffChange.difficulty, locale, '1.2-2') }}</td>
 | 
			
		||||
        <td [style]="diffChange.change >= 0 ? 'color: #42B747' : 'color: #B74242'">{{ formatNumber(diffChange.change, locale, '1.2-2') }}%</td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </tbody>
 | 
			
		||||
  </table>
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,11 @@
 | 
			
		||||
import { Component, Inject, LOCALE_ID, OnInit } from '@angular/core';
 | 
			
		||||
import { EChartsOption } from 'echarts';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { map, share, tap } from 'rxjs/operators';
 | 
			
		||||
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 { formatNumber } from '@angular/common';
 | 
			
		||||
import { FormBuilder, FormGroup } from '@angular/forms';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'app-difficulty-chart',
 | 
			
		||||
@ -20,6 +21,8 @@ import { formatNumber } from "@angular/common";
 | 
			
		||||
  `],
 | 
			
		||||
})
 | 
			
		||||
export class DifficultyChartComponent implements OnInit {
 | 
			
		||||
  radioGroupForm: FormGroup;
 | 
			
		||||
 | 
			
		||||
  chartOptions: EChartsOption = {};
 | 
			
		||||
  chartInitOptions = {
 | 
			
		||||
    renderer: 'svg'
 | 
			
		||||
@ -33,34 +36,45 @@ export class DifficultyChartComponent implements OnInit {
 | 
			
		||||
    @Inject(LOCALE_ID) public locale: string,
 | 
			
		||||
    private seoService: SeoService,
 | 
			
		||||
    private apiService: ApiService,
 | 
			
		||||
    private formBuilder: FormBuilder,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.seoService.setTitle($localize`:@@mining.difficulty:Difficulty`);
 | 
			
		||||
    this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
 | 
			
		||||
    this.radioGroupForm.controls.dateSpan.setValue('1y');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.difficultyObservable$ = this.apiService.getHistoricalDifficulty$(undefined)
 | 
			
		||||
    this.difficultyObservable$ = this.radioGroupForm.get('dateSpan').valueChanges
 | 
			
		||||
      .pipe(
 | 
			
		||||
        map(data => {
 | 
			
		||||
          let formatted = [];
 | 
			
		||||
          for (let i = 0; i < data.length - 1; ++i) {
 | 
			
		||||
            const change = (data[i].difficulty / data[i + 1].difficulty - 1) * 100;
 | 
			
		||||
            formatted.push([
 | 
			
		||||
              data[i].timestamp,
 | 
			
		||||
              data[i].difficulty,
 | 
			
		||||
              data[i].height,
 | 
			
		||||
              formatNumber(change, this.locale, '1.2-2'),
 | 
			
		||||
              change,
 | 
			
		||||
              formatNumber(data[i].difficulty, this.locale, '1.2-2'),
 | 
			
		||||
            ]);
 | 
			
		||||
          }
 | 
			
		||||
          return formatted;
 | 
			
		||||
        }),
 | 
			
		||||
        tap(data => {
 | 
			
		||||
          this.prepareChartOptions(data);
 | 
			
		||||
          this.isLoading = false;
 | 
			
		||||
        }),
 | 
			
		||||
        share()
 | 
			
		||||
      )
 | 
			
		||||
        startWith('1y'),
 | 
			
		||||
        switchMap((timespan) => {
 | 
			
		||||
          return this.apiService.getHistoricalDifficulty$(timespan)
 | 
			
		||||
            .pipe(
 | 
			
		||||
              tap(data => {
 | 
			
		||||
                this.prepareChartOptions(data.adjustments.map(val => [val.timestamp * 1000, val.difficulty]));
 | 
			
		||||
                this.isLoading = false;
 | 
			
		||||
              }),
 | 
			
		||||
              map(data => {
 | 
			
		||||
                const availableTimespanDay = (
 | 
			
		||||
                  (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp / 1000)
 | 
			
		||||
                ) / 3600 / 24;
 | 
			
		||||
 | 
			
		||||
                const tableData = [];
 | 
			
		||||
                for (let i = 0; i < data.adjustments.length - 1; ++i) {
 | 
			
		||||
                  const change = (data.adjustments[i].difficulty / data.adjustments[i + 1].difficulty - 1) * 100;
 | 
			
		||||
                  tableData.push(Object.assign(data.adjustments[i], {
 | 
			
		||||
                    change: change
 | 
			
		||||
                  }));
 | 
			
		||||
                }
 | 
			
		||||
                return {
 | 
			
		||||
                  availableTimespanDay: availableTimespanDay,
 | 
			
		||||
                  data: tableData
 | 
			
		||||
                };
 | 
			
		||||
              }),
 | 
			
		||||
            );
 | 
			
		||||
          }),
 | 
			
		||||
          share()
 | 
			
		||||
        );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  prepareChartOptions(data) {
 | 
			
		||||
@ -88,7 +102,7 @@ export class DifficultyChartComponent implements OnInit {
 | 
			
		||||
        type: 'value',
 | 
			
		||||
        axisLabel: {
 | 
			
		||||
          fontSize: 11,
 | 
			
		||||
          formatter: function(val) {
 | 
			
		||||
          formatter: (val) => {
 | 
			
		||||
            const diff = val / Math.pow(10, 12); // terra
 | 
			
		||||
            return diff.toString() + 'T';
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -107,7 +107,7 @@ export class PoolRankingComponent implements OnInit {
 | 
			
		||||
      if (parseFloat(pool.share) < poolShareThreshold) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      data.push(<PieSeriesOption>{
 | 
			
		||||
      data.push({
 | 
			
		||||
        value: pool.share,
 | 
			
		||||
        name: pool.name + (this.isMobile() ? `` : ` (${pool.share}%)`),
 | 
			
		||||
        label: {
 | 
			
		||||
@ -115,9 +115,9 @@ export class PoolRankingComponent implements OnInit {
 | 
			
		||||
          overflow: 'break',
 | 
			
		||||
        },
 | 
			
		||||
        tooltip: {
 | 
			
		||||
          backgroundColor: "#282d47",
 | 
			
		||||
          backgroundColor: '#282d47',
 | 
			
		||||
          textStyle: {
 | 
			
		||||
            color: "#FFFFFF",
 | 
			
		||||
            color: '#FFFFFF',
 | 
			
		||||
          },
 | 
			
		||||
          formatter: () => {
 | 
			
		||||
            if (this.poolsWindowPreference === '24h') {
 | 
			
		||||
@ -131,7 +131,7 @@ export class PoolRankingComponent implements OnInit {
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        data: pool.poolId,
 | 
			
		||||
      });
 | 
			
		||||
      } as PieSeriesOption);
 | 
			
		||||
    });
 | 
			
		||||
    return data;
 | 
			
		||||
  }
 | 
			
		||||
@ -205,10 +205,10 @@ export class PoolRankingComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
    this.chartInstance = ec;
 | 
			
		||||
    this.chartInstance.on('click', (e) => {
 | 
			
		||||
      this.router.navigate(['/mining/pool/', e.data.data]); 
 | 
			
		||||
    })
 | 
			
		||||
      this.router.navigate(['/mining/pool/', e.data.data]);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Default mining stats if something goes wrong
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
@ -150,7 +150,7 @@ export class ApiService {
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getHistoricalDifficulty$(interval: string | undefined): Observable<any[]> {
 | 
			
		||||
  getHistoricalDifficulty$(interval: string | undefined): Observable<any> {
 | 
			
		||||
    return this.httpClient.get<any[]>(
 | 
			
		||||
        this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty` +
 | 
			
		||||
        (interval !== undefined ? `/${interval}` : '')
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user