Merge branch 'master' into simon/asset-amount-filter
This commit is contained in:
		
						commit
						d750b5ccd3
					
				| @ -274,7 +274,7 @@ class BlocksRepository { | ||||
|     } | ||||
| 
 | ||||
|     query += ` GROUP BY difficulty
 | ||||
|       ORDER BY blockTimestamp DESC`;
 | ||||
|       ORDER BY blockTimestamp`;
 | ||||
| 
 | ||||
|     const [rows]: any[] = await connection.query(query); | ||||
|     connection.release(); | ||||
|  | ||||
| @ -41,7 +41,7 @@ class HashratesRepository { | ||||
|       query += ` WHERE hashrate_timestamp BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`; | ||||
|     } | ||||
| 
 | ||||
|     query += ` ORDER by hashrate_timestamp DESC`; | ||||
|     query += ` ORDER by hashrate_timestamp`; | ||||
| 
 | ||||
|     const [rows]: any[] = await connection.query(query); | ||||
|     connection.release(); | ||||
|  | ||||
| @ -13,7 +13,10 @@ | ||||
|         <div class="fee-span"> | ||||
|           {{ block?.extras?.feeRange[1] | number:feeRounding }} - {{ block?.extras?.feeRange[block?.extras?.feeRange.length - 1] | number:feeRounding }} <ng-container i18n="shared.sat-vbyte|sat/vB">sat/vB</ng-container> | ||||
|         </div> | ||||
|         <div class="block-size" [innerHTML]="'‎' + (block.size | bytes: 2)"></div> | ||||
|         <div *ngIf="showMiningInfo" class="block-size"> | ||||
|           <app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2" [noFiat]="true"></app-amount> | ||||
|         </div> | ||||
|         <div *ngIf="!showMiningInfo" class="block-size" [innerHTML]="'‎' + (block.size | bytes: 2)"></div> | ||||
|         <div class="transaction-count"> | ||||
|           <ng-container *ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | number}"></ng-container> | ||||
|           <ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template> | ||||
|  | ||||
| @ -1,10 +1,5 @@ | ||||
| <div [class]="widget === false ? 'container-xl' : ''"> | ||||
| 
 | ||||
|   <div *ngIf="difficultyObservable$ | async" class="" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div> | ||||
|   <div class="text-center loadingGraphs" *ngIf="isLoading"> | ||||
|     <div class="spinner-border text-light"></div> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="card-header mb-0 mb-lg-4" [style]="widget ? 'display:none' : ''"> | ||||
|     <form [formGroup]="radioGroupForm" class="formRadioGroup" *ngIf="(difficultyObservable$ | async) as diffChanges"> | ||||
|       <div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="dateSpan"> | ||||
| @ -30,6 +25,11 @@ | ||||
|     </form> | ||||
|   </div> | ||||
| 
 | ||||
|   <div *ngIf="difficultyObservable$ | async" class="mb-5" echarts [initOpts]="chartInitOptions" [options]="chartOptions"></div> | ||||
|   <div class="text-center loadingGraphs" *ngIf="isLoading"> | ||||
|     <div class="spinner-border text-light"></div> | ||||
|   </div> | ||||
| 
 | ||||
|   <table class="table table-borderless table-sm text-center" *ngIf="!widget"> | ||||
|     <thead> | ||||
|       <tr> | ||||
|  | ||||
| @ -8,3 +8,20 @@ | ||||
|   text-align: center; | ||||
|   padding-bottom: 3px; | ||||
| } | ||||
| 
 | ||||
| .formRadioGroup { | ||||
|   margin-top: 6px; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   @media (min-width: 830px) { | ||||
|     flex-direction: row; | ||||
|     float: right; | ||||
|     margin-top: 0px; | ||||
|   } | ||||
|   .btn-sm { | ||||
|     font-size: 9px; | ||||
|     @media (min-width: 830px) { | ||||
|       font-size: 14px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { EChartsOption } from 'echarts'; | ||||
| import { EChartsOption, graphic } from 'echarts'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| @ -47,13 +47,6 @@ export class DifficultyChartComponent implements OnInit { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     const powerOfTen = { | ||||
|       terra: Math.pow(10, 12), | ||||
|       giga: Math.pow(10, 9), | ||||
|       mega: Math.pow(10, 6), | ||||
|       kilo: Math.pow(10, 3), | ||||
|     } | ||||
| 
 | ||||
|     this.difficultyObservable$ = this.radioGroupForm.get('dateSpan').valueChanges | ||||
|       .pipe( | ||||
|         startWith('1y'), | ||||
| @ -70,9 +63,9 @@ export class DifficultyChartComponent implements OnInit { | ||||
|                 ) / 3600 / 24; | ||||
| 
 | ||||
|                 const tableData = []; | ||||
|                 for (let i = 0; i < data.adjustments.length - 1; ++i) { | ||||
|                 for (let i = data.adjustments.length - 1; i > 0; --i) { | ||||
|                   const selectedPowerOfTen: any = selectPowerOfTen(data.adjustments[i].difficulty); | ||||
|                   const change = (data.adjustments[i].difficulty / data.adjustments[i + 1].difficulty - 1) * 100; | ||||
|                   const change = (data.adjustments[i].difficulty / data.adjustments[i - 1].difficulty - 1) * 100; | ||||
| 
 | ||||
|                   tableData.push(Object.assign(data.adjustments[i], { | ||||
|                     change: change, | ||||
| @ -94,6 +87,13 @@ export class DifficultyChartComponent implements OnInit { | ||||
| 
 | ||||
|   prepareChartOptions(data) { | ||||
|     this.chartOptions = { | ||||
|       color: new graphic.LinearGradient(0, 0, 0, 0.65, [ | ||||
|         { offset: 0, color: '#D81B60' }, | ||||
|         { offset: 0.25, color: '#8E24AA' }, | ||||
|         { offset: 0.5, color: '#5E35B1' }, | ||||
|         { offset: 0.75, color: '#3949AB' }, | ||||
|         { offset: 1, color: '#1E88E5' } | ||||
|       ]), | ||||
|       title: { | ||||
|         text: this.widget? '' : $localize`:@@mining.difficulty:Difficulty`, | ||||
|         left: 'center', | ||||
| @ -104,6 +104,17 @@ export class DifficultyChartComponent implements OnInit { | ||||
|       tooltip: { | ||||
|         show: true, | ||||
|         trigger: 'axis', | ||||
|         backgroundColor: 'rgba(17, 19, 31, 1)', | ||||
|         borderRadius: 4, | ||||
|         shadowColor: 'rgba(0, 0, 0, 0.5)', | ||||
|         textStyle: { | ||||
|           color: '#b1b1b1', | ||||
|         }, | ||||
|         borderColor: '#000', | ||||
|         formatter: params => { | ||||
|           return `<b style="color: white">${params[0].axisValueLabel}</b><br>
 | ||||
|             ${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')}` | ||||
|         } | ||||
|       }, | ||||
|       axisPointer: { | ||||
|         type: 'line', | ||||
| @ -137,10 +148,32 @@ export class DifficultyChartComponent implements OnInit { | ||||
|         lineStyle: { | ||||
|           width: 2, | ||||
|         }, | ||||
|       }, | ||||
|       dataZoom: this.widget ? null : [{ | ||||
|         type: 'inside', | ||||
|         realtime: true, | ||||
|         zoomLock: true, | ||||
|         zoomOnMouseWheel: true, | ||||
|         moveOnMouseMove: true, | ||||
|         maxSpan: 100, | ||||
|         minSpan: 10, | ||||
|       }, { | ||||
|         showDetail: false, | ||||
|         show: true, | ||||
|         type: 'slider', | ||||
|         brushSelect: false, | ||||
|         realtime: true, | ||||
|         bottom: 0, | ||||
|         selectedDataBackground: { | ||||
|           lineStyle: { | ||||
|             color: '#fff', | ||||
|             opacity: 0.45, | ||||
|           }, | ||||
|           areaStyle: { | ||||
|           opacity: 0.25 | ||||
|         }, | ||||
|             opacity: 0, | ||||
|           } | ||||
|         }, | ||||
|       }], | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; | ||||
| import { EChartsOption } from 'echarts'; | ||||
| import { EChartsOption, graphic } from 'echarts'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { map, share, startWith, switchMap, tap } from 'rxjs/operators'; | ||||
| import { ApiService } from 'src/app/services/api.service'; | ||||
| @ -78,6 +78,13 @@ export class HashrateChartComponent implements OnInit { | ||||
| 
 | ||||
|   prepareChartOptions(data) { | ||||
|     this.chartOptions = { | ||||
|       color: new graphic.LinearGradient(0, 0, 0, 0.65, [ | ||||
|         { offset: 0, color: '#F4511E' }, | ||||
|         { offset: 0.25, color: '#FB8C00' }, | ||||
|         { offset: 0.5, color: '#FFB300' }, | ||||
|         { offset: 0.75, color: '#FDD835' }, | ||||
|         { offset: 1, color: '#7CB342' } | ||||
|       ]), | ||||
|       grid: { | ||||
|         right: this.right, | ||||
|         left: this.left, | ||||
| @ -92,6 +99,17 @@ export class HashrateChartComponent implements OnInit { | ||||
|       tooltip: { | ||||
|         show: true, | ||||
|         trigger: 'axis', | ||||
|         backgroundColor: 'rgba(17, 19, 31, 1)', | ||||
|         borderRadius: 4, | ||||
|         shadowColor: 'rgba(0, 0, 0, 0.5)', | ||||
|         textStyle: { | ||||
|           color: '#b1b1b1', | ||||
|         }, | ||||
|         borderColor: '#000', | ||||
|         formatter: params => { | ||||
|           return `<b style="color: white">${params[0].axisValueLabel}</b><br>
 | ||||
|             ${params[0].marker} ${formatNumber(params[0].value[1], this.locale, '1.0-0')} H/s` | ||||
|         } | ||||
|       }, | ||||
|       axisPointer: { | ||||
|         type: 'line', | ||||
| @ -125,9 +143,6 @@ export class HashrateChartComponent implements OnInit { | ||||
|         lineStyle: { | ||||
|           width: 2, | ||||
|         }, | ||||
|         areaStyle: { | ||||
|           opacity: 0.25 | ||||
|         }, | ||||
|       }, | ||||
|       dataZoom: this.widget ? null : [{ | ||||
|         type: 'inside', | ||||
|  | ||||
| @ -12,7 +12,10 @@ | ||||
|             <div class="fee-span"> | ||||
|               {{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span> | ||||
|             </div> | ||||
|             <div class="block-size" [innerHTML]="'‎' + (projectedBlock.blockSize | bytes: 2)"></div> | ||||
|             <div *ngIf="showMiningInfo" class="block-size"> | ||||
|               <app-amount [satoshis]="projectedBlock.totalFees + blockSubsidy * 100000000" digitsInfo="1.2-2" [noFiat]="true"></app-amount> | ||||
|             </div> | ||||
|             <div *ngIf="!showMiningInfo" class="block-size" [innerHTML]="'‎' + (projectedBlock.blockSize | bytes: 2)"></div> | ||||
|             <div class="transaction-count"> | ||||
|               <ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container> | ||||
|               <ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template> | ||||
|  | ||||
| @ -7,6 +7,7 @@ import { take, map, switchMap } from 'rxjs/operators'; | ||||
| import { feeLevels, mempoolFeeColors } from 'src/app/app.constants'; | ||||
| import { specialBlocks } from 'src/app/app.constants'; | ||||
| import { RelativeUrlPipe } from 'src/app/shared/pipes/relative-url/relative-url.pipe'; | ||||
| import { Location } from '@angular/common'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-mempool-blocks', | ||||
| @ -32,6 +33,8 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | ||||
|   networkSubscription: Subscription; | ||||
|   network = ''; | ||||
|   now = new Date().getTime(); | ||||
|   showMiningInfo = false; | ||||
|   blockSubsidy = 50; | ||||
| 
 | ||||
|   blockWidth = 125; | ||||
|   blockPadding = 30; | ||||
| @ -54,9 +57,20 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | ||||
|     public stateService: StateService, | ||||
|     private cd: ChangeDetectorRef, | ||||
|     private relativeUrlPipe: RelativeUrlPipe, | ||||
|     private location: Location | ||||
|   ) { } | ||||
| 
 | ||||
|   enabledMiningInfoIfNeeded(url) { | ||||
|     this.showMiningInfo = url === '/mining'; | ||||
|     this.cd.markForCheck(); // Need to update the view asap
 | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     if (['', 'testnet', 'signet'].includes(this.stateService.network)) { | ||||
|       this.enabledMiningInfoIfNeeded(this.location.path()); | ||||
|       this.location.onUrlChange((url) => this.enabledMiningInfoIfNeeded(url)); | ||||
|     } | ||||
| 
 | ||||
|     if (this.stateService.network === 'liquid' || this.stateService.network === 'liquidtestnet') { | ||||
|       this.feeRounding = '1.0-1'; | ||||
|     } | ||||
| @ -97,6 +111,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | ||||
|             if (this.stateService.network === '') { | ||||
|               block.blink = specialBlocks[block.height] ? true : false; | ||||
|             } | ||||
|             this.setBlockSubsidy(block.height); | ||||
|           }); | ||||
| 
 | ||||
|           const stringifiedBlocks = JSON.stringify(mempoolBlocks); | ||||
| @ -197,6 +212,18 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy { | ||||
|     return block.index; | ||||
|   } | ||||
| 
 | ||||
|   setBlockSubsidy(blockHeight) { | ||||
|     if (!['', 'testnet', 'signet'].includes(this.stateService.network)) { | ||||
|       return; | ||||
|     } | ||||
|     this.blockSubsidy = 50; | ||||
|     let halvenings = Math.floor(blockHeight / 210000); | ||||
|     while (halvenings > 0) { | ||||
|       this.blockSubsidy = this.blockSubsidy / 2; | ||||
|       halvenings--; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   reduceMempoolBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] { | ||||
|     const innerWidth = this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2; | ||||
|     const blocksAmount = Math.min(this.stateService.env.MEMPOOL_BLOCKS_AMOUNT, Math.floor(innerWidth / (this.blockWidth + this.blockPadding))); | ||||
|  | ||||
| @ -4,9 +4,9 @@ | ||||
| 
 | ||||
|     <!-- pool distribution --> | ||||
|     <div class="col"> | ||||
|       <div class="main-title" i18n="mining.pool-share">Mining Pools Share (1w)</div> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title" i18n="mining.pool-share">Mining Pools Share (1w)</h5> | ||||
|           <app-pool-ranking [widget]=true></app-pool-ranking> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/mining/pools' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
| @ -16,9 +16,9 @@ | ||||
| 
 | ||||
|     <!-- hashrate --> | ||||
|     <div class="col"> | ||||
|       <div class="main-title" i18n="mining.hashrate">Hashrate (1y)</div> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title" i18n="mining.hashrate">Hashrate (1y)</h5> | ||||
|           <app-hashrate-chart [widget]=true></app-hashrate-chart> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/mining/hashrate' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
| @ -28,9 +28,9 @@ | ||||
| 
 | ||||
|     <!-- difficulty --> | ||||
|     <div class="col"> | ||||
|       <div class="main-title" i18n="mining.difficulty">Difficulty (1y)</div> | ||||
|       <div class="card"> | ||||
|         <div class="card-body"> | ||||
|           <h5 class="card-title" i18n="ning.difficulty">Difficulty (1y)</h5> | ||||
|           <app-difficulty-chart [widget]=true></app-difficulty-chart> | ||||
|           <div class="text-center"><a href="" [routerLink]="['/mining/difficulty' | relativeUrl]" i18n="dashboard.view-more">View more | ||||
|               »</a></div> | ||||
|  | ||||
| @ -15,6 +15,11 @@ | ||||
|   height: 100%; | ||||
| } | ||||
| 
 | ||||
| .card-title { | ||||
|   color: #4a68b9; | ||||
|   font-size: 1rem; | ||||
| } | ||||
| 
 | ||||
| .card-wrapper { | ||||
|   .card { | ||||
|     height: auto !important; | ||||
|  | ||||
| @ -121,21 +121,24 @@ export class PoolRankingComponent implements OnInit { | ||||
|         value: pool.share, | ||||
|         name: pool.name + (this.isMobile() ? `` : ` (${pool.share}%)`), | ||||
|         label: { | ||||
|           color: '#FFFFFF', | ||||
|           color: '#b1b1b1', | ||||
|           overflow: 'break', | ||||
|         }, | ||||
|         tooltip: { | ||||
|           backgroundColor: '#282d47', | ||||
|           backgroundColor: 'rgba(17, 19, 31, 1)', | ||||
|           borderRadius: 4, | ||||
|           shadowColor: 'rgba(0, 0, 0, 0.5)', | ||||
|           textStyle: { | ||||
|             color: '#FFFFFF', | ||||
|             color: '#b1b1b1', | ||||
|           }, | ||||
|           borderColor: '#000', | ||||
|           formatter: () => { | ||||
|             if (this.poolsWindowPreference === '24h') { | ||||
|               return `<u><b>${pool.name} (${pool.share}%)</b></u><br>` + | ||||
|               return `<b style="color: white">${pool.name} (${pool.share}%)</b><br>` + | ||||
|                 pool.lastEstimatedHashrate.toString() + ' PH/s' + | ||||
|                 `<br>` + pool.blockCount.toString() + ` blocks`; | ||||
|             } else { | ||||
|               return `<u><b>${pool.name} (${pool.share}%)</b></u><br>` + | ||||
|               return `<b style="color: white">${pool.name} (${pool.share}%)</b><br>` + | ||||
|                 pool.blockCount.toString() + ` blocks`; | ||||
|             } | ||||
|           } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user