Merge pull request #1331 from nymkappa/feature/show-indexing-progress
Show current indexing progress in charts without data
This commit is contained in:
		
						commit
						e83e1067c1
					
				| @ -75,6 +75,7 @@ import { HashrateChartPoolsComponent } from './components/hashrates-chart-pools/ | ||||
| import { MiningStartComponent } from './components/mining-start/mining-start.component'; | ||||
| import { AmountShortenerPipe } from './shared/pipes/amount-shortener.pipe'; | ||||
| import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe'; | ||||
| import { DifficultyAdjustmentsTable } from './components/difficulty-adjustments-table/difficulty-adjustments-table.components'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|   declarations: [ | ||||
| @ -131,6 +132,7 @@ import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-st | ||||
|     HashrateChartPoolsComponent, | ||||
|     MiningStartComponent, | ||||
|     AmountShortenerPipe, | ||||
|     DifficultyAdjustmentsTable, | ||||
|   ], | ||||
|   imports: [ | ||||
|     BrowserModule.withServerTransition({ appId: 'serverApp' }), | ||||
|  | ||||
| @ -0,0 +1,33 @@ | ||||
| <div> | ||||
|   <table class="table latest-transactions" style="min-height: 295px"> | ||||
|     <thead> | ||||
|       <tr> | ||||
|         <th class="d-none d-md-block" i18n="block.height">Height</th> | ||||
|         <th i18n="mining.adjusted" class="text-left">Adjusted</th> | ||||
|         <th i18n="mining.difficulty" class="text-right">Difficulty</th> | ||||
|         <th i18n="mining.change" class="text-right">Change</th> | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody *ngIf="(hashrateObservable$ | async) as data"> | ||||
|       <tr *ngFor="let diffChange of data.difficulty"> | ||||
|         <td class="d-none d-md-block"><a [routerLink]="['/block' | relativeUrl, diffChange.height]">{{ diffChange.height | ||||
|             }}</a></td> | ||||
|         <td class="text-left"> | ||||
|           <app-time-since [time]="diffChange.timestamp" [fastRender]="true"></app-time-since> | ||||
|         </td> | ||||
|         <td class="text-right">{{ diffChange.difficultyShorten }}</td> | ||||
|         <td class="text-right" [style]="diffChange.change >= 0 ? 'color: #42B747' : 'color: #B74242'"> | ||||
|           {{ diffChange.change >= 0 ? '+' : '' }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}% | ||||
|         </td> | ||||
|       </tr> | ||||
|     </tbody> | ||||
|     <tbody *ngIf="isLoading"> | ||||
|       <tr *ngFor="let item of [1,2,3,4,5]"> | ||||
|         <td class="d-none d-md-block w-75"><span class="skeleton-loader"></span></td> | ||||
|         <td class="text-left"><span class="skeleton-loader w-75"></span></td> | ||||
|         <td class="text-right"><span class="skeleton-loader w-75"></span></td> | ||||
|         <td class="text-right"><span class="skeleton-loader w-75"></span></td> | ||||
|       </tr> | ||||
|     </tbody> | ||||
|   </table> | ||||
| </div> | ||||
| @ -0,0 +1,40 @@ | ||||
| .latest-transactions { | ||||
|   width: 100%; | ||||
|   text-align: left; | ||||
|   table-layout:fixed; | ||||
|   tr, td, th { | ||||
|     border: 0px; | ||||
|   } | ||||
|   td { | ||||
|     width: 25%; | ||||
|   } | ||||
|   .table-cell-satoshis { | ||||
|     display: none; | ||||
|     text-align: right; | ||||
|     @media (min-width: 576px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|     @media (min-width: 768px) { | ||||
|       display: none; | ||||
|     } | ||||
|     @media (min-width: 1100px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|   } | ||||
|   .table-cell-fiat { | ||||
|     display: none; | ||||
|     text-align: right; | ||||
|     @media (min-width: 485px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|     @media (min-width: 768px) { | ||||
|       display: none; | ||||
|     } | ||||
|     @media (min-width: 992px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|   } | ||||
|   .table-cell-fees { | ||||
|     text-align: right; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,65 @@ | ||||
| import { Component, Inject, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { map } from 'rxjs/operators'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| import { formatNumber } from '@angular/common'; | ||||
| import { selectPowerOfTen } from 'src/app/bitcoin.utils'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-difficulty-adjustments-table', | ||||
|   templateUrl: './difficulty-adjustments-table.component.html', | ||||
|   styleUrls: ['./difficulty-adjustments-table.component.scss'], | ||||
|   styles: [` | ||||
|     .loadingGraphs { | ||||
|       position: absolute; | ||||
|       top: 50%; | ||||
|       left: calc(50% - 15px); | ||||
|       z-index: 100; | ||||
|     } | ||||
|   `],
 | ||||
| }) | ||||
| export class DifficultyAdjustmentsTable implements OnInit { | ||||
|   hashrateObservable$: Observable<any>; | ||||
|   isLoading = true; | ||||
|   formatNumber = formatNumber; | ||||
| 
 | ||||
|   constructor( | ||||
|     @Inject(LOCALE_ID) public locale: string, | ||||
|     private apiService: ApiService, | ||||
|   ) { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.hashrateObservable$ = this.apiService.getHistoricalHashrate$('1y') | ||||
|       .pipe( | ||||
|         map((data: any) => { | ||||
|           const availableTimespanDay = ( | ||||
|             (new Date().getTime() / 1000) - (data.oldestIndexedBlockTimestamp) | ||||
|           ) / 3600 / 24; | ||||
| 
 | ||||
|           const tableData = []; | ||||
|           for (let i = data.difficulty.length - 1; i > 0; --i) { | ||||
|             const selectedPowerOfTen: any = selectPowerOfTen(data.difficulty[i].difficulty); | ||||
|             const change = (data.difficulty[i].difficulty / data.difficulty[i - 1].difficulty - 1) * 100; | ||||
| 
 | ||||
|             tableData.push(Object.assign(data.difficulty[i], { | ||||
|               change: change, | ||||
|               difficultyShorten: formatNumber( | ||||
|                 data.difficulty[i].difficulty / selectedPowerOfTen.divider, | ||||
|                 this.locale, '1.2-2') + selectedPowerOfTen.unit | ||||
|             })); | ||||
|           } | ||||
|           this.isLoading = false; | ||||
| 
 | ||||
|           return { | ||||
|             availableTimespanDay: availableTimespanDay, | ||||
|             difficulty: tableData.slice(0, 5), | ||||
|           }; | ||||
|         }), | ||||
|       ); | ||||
|   } | ||||
| 
 | ||||
|   isMobile() { | ||||
|     return (window.innerWidth <= 767.98); | ||||
|   } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| <div [class]="widget === false ? 'full-container' : ''"> | ||||
| 
 | ||||
|   <div *ngIf="!tableOnly" class="card-header mb-0 mb-md-4" [style]="widget ? 'display:none' : ''"> | ||||
|   <div class="card-header mb-0 mb-md-4" [style]="widget ? 'display:none' : ''"> | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(hashrateObservable$ | async) as hashrates"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
|         <label ngbButtonLabel class="btn-primary btn-sm" *ngIf="hashrates.availableTimespanDay >= 90"> | ||||
| @ -25,39 +25,10 @@ | ||||
|     </form> | ||||
|   </div> | ||||
| 
 | ||||
|   <div *ngIf="!tableOnly" [class]="!widget ? 'chart' : 'chart-widget'" | ||||
|   <div [class]="!widget ? 'chart' : 'chart-widget'" | ||||
|     echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div> | ||||
|   <div class="text-center loadingGraphs" *ngIf="isLoading && !tableOnly"> | ||||
|   <div class="text-center loadingGraphs" *ngIf="isLoading"> | ||||
|     <div class="spinner-border text-light"></div> | ||||
|   </div> | ||||
|    | ||||
|   <table *ngIf="tableOnly" class="table latest-transactions" style="min-height: 295px"> | ||||
|     <thead> | ||||
|       <tr> | ||||
|         <th class="d-none d-md-block" i18n="block.height">Height</th> | ||||
|         <th i18n="mining.adjusted" class="text-left">Adjusted</th> | ||||
|         <th i18n="mining.difficulty" class="text-right">Difficulty</th> | ||||
|         <th i18n="mining.change" class="text-right">Change</th> | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody *ngIf="(hashrateObservable$ | async) as data"> | ||||
|       <tr *ngFor="let diffChange of data.difficulty"> | ||||
|         <td class="d-none d-md-block"><a [routerLink]="['/block' | relativeUrl, diffChange.height]">{{ diffChange.height }}</a></td> | ||||
|         <td class="text-left"><app-time-since [time]="diffChange.timestamp" [fastRender]="true"></app-time-since></td> | ||||
|         <td class="text-right">{{ diffChange.difficultyShorten }}</td> | ||||
|         <td class="text-right" [style]="diffChange.change >= 0 ? 'color: #42B747' : 'color: #B74242'"> | ||||
|           {{ diffChange.change >= 0 ? '+' : '' }}{{ formatNumber(diffChange.change, locale, '1.2-2') }}% | ||||
|         </td> | ||||
|       </tr> | ||||
|     </tbody> | ||||
|     <tbody *ngIf="isLoading"> | ||||
|       <tr *ngFor="let item of [1,2,3,4,5]"> | ||||
|         <td class="d-none d-md-block w-75"><span class="skeleton-loader"></span></td> | ||||
|         <td class="text-left"><span class="skeleton-loader w-75"></span></td> | ||||
|         <td class="text-right"><span class="skeleton-loader w-75"></span></td> | ||||
|         <td class="text-right"><span class="skeleton-loader w-75"></span></td> | ||||
|       </tr> | ||||
|   </tbody> | ||||
|   </table> | ||||
| 
 | ||||
| </div> | ||||
|  | ||||
| @ -48,44 +48,3 @@ | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .latest-transactions { | ||||
|   width: 100%; | ||||
|   text-align: left; | ||||
|   table-layout:fixed; | ||||
|   tr, td, th { | ||||
|     border: 0px; | ||||
|   } | ||||
|   td { | ||||
|     width: 25%; | ||||
|   } | ||||
|   .table-cell-satoshis { | ||||
|     display: none; | ||||
|     text-align: right; | ||||
|     @media (min-width: 576px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|     @media (min-width: 768px) { | ||||
|       display: none; | ||||
|     } | ||||
|     @media (min-width: 1100px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|   } | ||||
|   .table-cell-fiat { | ||||
|     display: none; | ||||
|     text-align: right; | ||||
|     @media (min-width: 485px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|     @media (min-width: 768px) { | ||||
|       display: none; | ||||
|     } | ||||
|     @media (min-width: 992px) { | ||||
|       display: table-cell; | ||||
|     } | ||||
|   } | ||||
|   .table-cell-fees { | ||||
|     text-align: right; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { EChartsOption, graphic } from 'echarts'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; | ||||
| import { delay, map, retryWhen, 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'; | ||||
| @ -20,6 +20,7 @@ import { selectPowerOfTen } from 'src/app/bitcoin.utils'; | ||||
|       z-index: 100; | ||||
|     } | ||||
|   `],
 | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush, | ||||
| }) | ||||
| export class HashrateChartComponent implements OnInit { | ||||
|   @Input() tableOnly = false; | ||||
| @ -45,6 +46,7 @@ export class HashrateChartComponent implements OnInit { | ||||
|     private seoService: SeoService, | ||||
|     private apiService: ApiService, | ||||
|     private formBuilder: FormBuilder, | ||||
|     private cd: ChangeDetectorRef, | ||||
|   ) { | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue('1y'); | ||||
| @ -92,9 +94,15 @@ export class HashrateChartComponent implements OnInit { | ||||
| 
 | ||||
|                 this.prepareChartOptions({ | ||||
|                   hashrates: data.hashrates.map(val => [val.timestamp * 1000, val.avgHashrate]), | ||||
|                   difficulty: diffFixed.map(val => [val.timestamp * 1000, val.difficulty]) | ||||
|                   difficulty: diffFixed.map(val => [val.timestamp * 1000, val.difficulty]), | ||||
|                   timestamp: data.oldestIndexedBlockTimestamp, | ||||
|                 }); | ||||
|                 this.isLoading = false; | ||||
| 
 | ||||
|                 if (data.hashrates.length === 0) { | ||||
|                   this.cd.markForCheck(); | ||||
|                   throw new Error(); | ||||
|                 } | ||||
|               }), | ||||
|               map((data: any) => { | ||||
|                 const availableTimespanDay = ( | ||||
| @ -115,9 +123,12 @@ export class HashrateChartComponent implements OnInit { | ||||
|                 } | ||||
|                 return { | ||||
|                   availableTimespanDay: availableTimespanDay, | ||||
|                   difficulty: this.tableOnly ? tableData.slice(0, 5) : tableData | ||||
|                   difficulty: this.tableOnly ? tableData.slice(0, 5) : tableData, | ||||
|                 }; | ||||
|               }), | ||||
|               retryWhen((errors) => errors.pipe( | ||||
|                   delay(60000) | ||||
|               )) | ||||
|             ); | ||||
|         }), | ||||
|         share() | ||||
| @ -125,16 +136,20 @@ export class HashrateChartComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   prepareChartOptions(data) { | ||||
|     let title = undefined; | ||||
|     let title: object; | ||||
|     if (data.hashrates.length === 0) { | ||||
|       const lastBlock = new Date(data.timestamp * 1000); | ||||
|       const dd = String(lastBlock.getDate()).padStart(2, '0'); | ||||
|       const mm = String(lastBlock.getMonth() + 1).padStart(2, '0'); // January is 0!
 | ||||
|       const yyyy = lastBlock.getFullYear(); | ||||
|       title = { | ||||
|         textStyle: { | ||||
|             color: "grey", | ||||
|             color: 'grey', | ||||
|             fontSize: 15 | ||||
|         }, | ||||
|         text: "Indexing in progress...", | ||||
|         left: "center", | ||||
|         top: "center" | ||||
|         text: `Indexing in progess - ${yyyy}-${mm}-${dd}`, | ||||
|         left: 'center', | ||||
|         top: 'center' | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
| @ -190,11 +205,11 @@ export class HashrateChartComponent implements OnInit { | ||||
|           `;
 | ||||
|         }.bind(this) | ||||
|       }, | ||||
|       xAxis: { | ||||
|       xAxis: data.hashrates.length === 0 ? undefined : { | ||||
|         type: 'time', | ||||
|         splitNumber: (this.isMobile() || this.widget) ? 5 : 10, | ||||
|       }, | ||||
|       legend: { | ||||
|       legend: data.hashrates.length === 0 ? undefined : { | ||||
|         data: [ | ||||
|           { | ||||
|             name: 'Hashrate', | ||||
| @ -220,7 +235,7 @@ export class HashrateChartComponent implements OnInit { | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|       yAxis: [ | ||||
|       yAxis: data.hashrates.length === 0 ? undefined : [ | ||||
|         { | ||||
|           min: function (value) { | ||||
|             return value.min * 0.9; | ||||
| @ -259,7 +274,7 @@ export class HashrateChartComponent implements OnInit { | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       series: [ | ||||
|       series: data.hashrates.length === 0 ? [] : [ | ||||
|         { | ||||
|           name: 'Hashrate', | ||||
|           showSymbol: false, | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { EChartsOption } from 'echarts'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; | ||||
| import { delay, map, retryWhen, share, startWith, switchMap, tap } from 'rxjs/operators'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| import { SeoService } from 'src/app/services/seo.service'; | ||||
| import { FormBuilder, FormGroup } from '@angular/forms'; | ||||
| @ -22,7 +22,7 @@ import { poolsColor } from 'src/app/app.constants'; | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush, | ||||
| }) | ||||
| export class HashrateChartPoolsComponent implements OnInit { | ||||
|   @Input() widget: boolean = false; | ||||
|   @Input() widget = false; | ||||
|   @Input() right: number | string = 40; | ||||
|   @Input() left: number | string = 25; | ||||
| 
 | ||||
| @ -43,6 +43,7 @@ export class HashrateChartPoolsComponent implements OnInit { | ||||
|     private seoService: SeoService, | ||||
|     private apiService: ApiService, | ||||
|     private formBuilder: FormBuilder, | ||||
|     private cd: ChangeDetectorRef, | ||||
|   ) { | ||||
|     this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' }); | ||||
|     this.radioGroupForm.controls.dateSpan.setValue('1y'); | ||||
| @ -105,9 +106,15 @@ export class HashrateChartPoolsComponent implements OnInit { | ||||
| 
 | ||||
|                 this.prepareChartOptions({ | ||||
|                   legends: legends, | ||||
|                   series: series | ||||
|                   series: series, | ||||
|                   timestamp: data.oldestIndexedBlockTimestamp, | ||||
|                 }); | ||||
|                 this.isLoading = false; | ||||
| 
 | ||||
|                 if (series.length === 0) { | ||||
|                   this.cd.markForCheck(); | ||||
|                   throw new Error(); | ||||
|                 } | ||||
|               }), | ||||
|               map((data: any) => { | ||||
|                 const availableTimespanDay = ( | ||||
| @ -117,6 +124,9 @@ export class HashrateChartPoolsComponent implements OnInit { | ||||
|                   availableTimespanDay: availableTimespanDay, | ||||
|                 }; | ||||
|               }), | ||||
|               retryWhen((errors) => errors.pipe( | ||||
|                 delay(60000) | ||||
|               )) | ||||
|             ); | ||||
|         }), | ||||
|         share() | ||||
| @ -124,16 +134,20 @@ export class HashrateChartPoolsComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   prepareChartOptions(data) { | ||||
|     let title = undefined; | ||||
|     let title: object; | ||||
|     if (data.series.length === 0) { | ||||
|       const lastBlock = new Date(data.timestamp * 1000); | ||||
|       const dd = String(lastBlock.getDate()).padStart(2, '0'); | ||||
|       const mm = String(lastBlock.getMonth() + 1).padStart(2, '0'); // January is 0!
 | ||||
|       const yyyy = lastBlock.getFullYear(); | ||||
|       title = { | ||||
|         textStyle: { | ||||
|             color: "grey", | ||||
|             fontSize: 15 | ||||
|           color: 'grey', | ||||
|           fontSize: 15 | ||||
|         }, | ||||
|         text: "Indexing in progress...", | ||||
|         left: "center", | ||||
|         top: this.widget ? 115 : this.isMobile() ? 'center' : 225, | ||||
|         text: `Indexing in progess - ${yyyy}-${mm}-${dd}`, | ||||
|         left: 'center', | ||||
|         top: 'center', | ||||
|       }; | ||||
|     } | ||||
| 
 | ||||
| @ -171,14 +185,14 @@ export class HashrateChartPoolsComponent implements OnInit { | ||||
|           return tooltip; | ||||
|         }.bind(this) | ||||
|       }, | ||||
|       xAxis: { | ||||
|       xAxis: data.series.length === 0 ? undefined : { | ||||
|         type: 'time', | ||||
|         splitNumber: (this.isMobile() || this.widget) ? 5 : 10, | ||||
|       }, | ||||
|       legend: (this.isMobile() || this.widget) ? undefined : { | ||||
|       legend: (this.isMobile() || this.widget || data.series.length === 0) ? undefined : { | ||||
|         data: data.legends | ||||
|       }, | ||||
|       yAxis: { | ||||
|       yAxis: data.series.length === 0 ? undefined : { | ||||
|         position: 'right', | ||||
|         axisLabel: { | ||||
|           color: 'rgb(110, 112, 121)', | ||||
|  | ||||
| @ -114,7 +114,7 @@ | ||||
|           <h5 class="card-title"> | ||||
|             Adjustments | ||||
|           </h5> | ||||
|           <app-hashrate-chart [tableOnly]=true [widget]=true></app-hashrate-chart> | ||||
|           <app-difficulty-adjustments-table></app-difficulty-adjustments-table> | ||||
|           <div class="mt-1"><a [routerLink]="['/mining/hashrate' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
|         </div> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user