Merge pull request #1004 from jorisvial/bug/improve-x-axis-intervals
Improve x-axis labels and graph data ticks
This commit is contained in:
		
						commit
						e67f552fbd
					
				| @ -267,8 +267,57 @@ class Statistics { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private getQueryForDays(div: number) { | ||||
|     return `SELECT id, added, unconfirmed_transactions,
 | ||||
|   private getQueryForDaysAvg(div: number, interval: string) { | ||||
|     return `SELECT id, UNIX_TIMESTAMP(added) as added,
 | ||||
|       CAST(avg(unconfirmed_transactions) as FLOAT) as unconfirmed_transactions, | ||||
|       CAST(avg(tx_per_second) as FLOAT) as tx_per_second, | ||||
|       CAST(avg(vbytes_per_second) as FLOAT) as vbytes_per_second, | ||||
|       CAST(avg(vsize_1) as FLOAT) as vsize_1, | ||||
|       CAST(avg(vsize_2) as FLOAT) as vsize_2, | ||||
|       CAST(avg(vsize_3) as FLOAT) as vsize_3, | ||||
|       CAST(avg(vsize_4) as FLOAT) as vsize_4, | ||||
|       CAST(avg(vsize_5) as FLOAT) as vsize_5, | ||||
|       CAST(avg(vsize_6) as FLOAT) as vsize_6, | ||||
|       CAST(avg(vsize_8) as FLOAT) as vsize_8, | ||||
|       CAST(avg(vsize_10) as FLOAT) as vsize_10, | ||||
|       CAST(avg(vsize_12) as FLOAT) as vsize_12, | ||||
|       CAST(avg(vsize_15) as FLOAT) as vsize_15, | ||||
|       CAST(avg(vsize_20) as FLOAT) as vsize_20, | ||||
|       CAST(avg(vsize_30) as FLOAT) as vsize_30, | ||||
|       CAST(avg(vsize_40) as FLOAT) as vsize_40, | ||||
|       CAST(avg(vsize_50) as FLOAT) as vsize_50, | ||||
|       CAST(avg(vsize_60) as FLOAT) as vsize_60, | ||||
|       CAST(avg(vsize_70) as FLOAT) as vsize_70, | ||||
|       CAST(avg(vsize_80) as FLOAT) as vsize_80, | ||||
|       CAST(avg(vsize_90) as FLOAT) as vsize_90, | ||||
|       CAST(avg(vsize_100) as FLOAT) as vsize_100, | ||||
|       CAST(avg(vsize_125) as FLOAT) as vsize_125, | ||||
|       CAST(avg(vsize_150) as FLOAT) as vsize_150, | ||||
|       CAST(avg(vsize_175) as FLOAT) as vsize_175, | ||||
|       CAST(avg(vsize_200) as FLOAT) as vsize_200, | ||||
|       CAST(avg(vsize_250) as FLOAT) as vsize_250, | ||||
|       CAST(avg(vsize_300) as FLOAT) as vsize_300, | ||||
|       CAST(avg(vsize_350) as FLOAT) as vsize_350, | ||||
|       CAST(avg(vsize_400) as FLOAT) as vsize_400, | ||||
|       CAST(avg(vsize_500) as FLOAT) as vsize_500, | ||||
|       CAST(avg(vsize_600) as FLOAT) as vsize_600, | ||||
|       CAST(avg(vsize_700) as FLOAT) as vsize_700, | ||||
|       CAST(avg(vsize_800) as FLOAT) as vsize_800, | ||||
|       CAST(avg(vsize_900) as FLOAT) as vsize_900, | ||||
|       CAST(avg(vsize_1000) as FLOAT) as vsize_1000, | ||||
|       CAST(avg(vsize_1200) as FLOAT) as vsize_1200, | ||||
|       CAST(avg(vsize_1400) as FLOAT) as vsize_1400, | ||||
|       CAST(avg(vsize_1600) as FLOAT) as vsize_1600, | ||||
|       CAST(avg(vsize_1800) as FLOAT) as vsize_1800, | ||||
|       CAST(avg(vsize_2000) as FLOAT) as vsize_2000 \ | ||||
|       FROM statistics \ | ||||
|       WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() \ | ||||
|       GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \ | ||||
|       ORDER BY id DESC;`;
 | ||||
|   } | ||||
| 
 | ||||
|   private getQueryForDays(div: number, interval: string) { | ||||
|     return `SELECT id, UNIX_TIMESTAMP(added) as added, unconfirmed_transactions,
 | ||||
|       tx_per_second, | ||||
|       vbytes_per_second, | ||||
|       vsize_1, | ||||
| @ -308,13 +357,17 @@ class Statistics { | ||||
|       vsize_1400, | ||||
|       vsize_1600, | ||||
|       vsize_1800, | ||||
|       vsize_2000 FROM statistics GROUP BY UNIX_TIMESTAMP(added) DIV ${div} ORDER BY id DESC LIMIT 480`;
 | ||||
|       vsize_2000 \ | ||||
|       FROM statistics \ | ||||
|       WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW() \ | ||||
|       GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \ | ||||
|       ORDER BY id DESC;`;
 | ||||
|   } | ||||
| 
 | ||||
|   public async $get(id: number): Promise<OptimizedStatistic | undefined> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = `SELECT * FROM statistics WHERE id = ?`; | ||||
|       const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics WHERE id = ?`; | ||||
|       const [rows] = await connection.query<any>(query, [id]); | ||||
|       connection.release(); | ||||
|       if (rows[0]) { | ||||
| @ -328,7 +381,7 @@ class Statistics { | ||||
|   public async $list2H(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = `SELECT * FROM statistics ORDER BY id DESC LIMIT 120`; | ||||
|       const query = `SELECT *, UNIX_TIMESTAMP(added) as added FROM statistics ORDER BY id DESC LIMIT 120`; | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -341,7 +394,7 @@ class Statistics { | ||||
|   public async $list24H(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(180); | ||||
|       const query = this.getQueryForDaysAvg(120, '1 DAY'); // 2m interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -354,7 +407,7 @@ class Statistics { | ||||
|   public async $list1W(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(1260); | ||||
|       const query = this.getQueryForDaysAvg(600, '1 WEEK'); // 10m interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -367,7 +420,7 @@ class Statistics { | ||||
|   public async $list1M(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(5040); | ||||
|       const query = this.getQueryForDaysAvg(3600, '1 MONTH'); // 1h interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -380,7 +433,7 @@ class Statistics { | ||||
|   public async $list3M(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(15120); | ||||
|       const query = this.getQueryForDaysAvg(14400, '3 MONTH'); // 4h interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -393,7 +446,7 @@ class Statistics { | ||||
|   public async $list6M(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(30240); | ||||
|       const query = this.getQueryForDaysAvg(21600, '6 MONTH'); // 6h interval 
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -406,7 +459,7 @@ class Statistics { | ||||
|   public async $list1Y(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(60480); | ||||
|       const query = this.getQueryForDays(43200, '1 YEAR'); // 12h interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -419,7 +472,7 @@ class Statistics { | ||||
|   public async $list2Y(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(120960); | ||||
|       const query = this.getQueryForDays(86400, "2 YEAR"); // 1d interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
| @ -432,7 +485,7 @@ class Statistics { | ||||
|   public async $list3Y(): Promise<OptimizedStatistic[]> { | ||||
|     try { | ||||
|       const connection = await DB.pool.getConnection(); | ||||
|       const query = this.getQueryForDays(181440); | ||||
|       const query = this.getQueryForDays(86400, "3 YEAR"); // 1d interval
 | ||||
|       const [rows] = await connection.query<any>({ sql: query, timeout: this.queryTimeout }); | ||||
|       connection.release(); | ||||
|       return this.mapStatisticToOptimizedStatistic(rows); | ||||
|  | ||||
| @ -32,7 +32,6 @@ import { BlockchainComponent } from './components/blockchain/blockchain.componen | ||||
| import { FooterComponent } from './components/footer/footer.component'; | ||||
| import { AudioService } from './services/audio.service'; | ||||
| import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component'; | ||||
| import { FeeDistributionGraphComponent } from './components/fee-distribution-graph/fee-distribution-graph.component'; | ||||
| import { IncomingTransactionsGraphComponent } from './components/incoming-transactions-graph/incoming-transactions-graph.component'; | ||||
| import { TimeSpanComponent } from './components/time-span/time-span.component'; | ||||
| import { SeoService } from './services/seo.service'; | ||||
| @ -84,7 +83,6 @@ import { PushTransactionComponent } from './components/push-transaction/push-tra | ||||
|     MempoolBlocksComponent, | ||||
|     FooterComponent, | ||||
|     MempoolBlockComponent, | ||||
|     FeeDistributionGraphComponent, | ||||
|     IncomingTransactionsGraphComponent, | ||||
|     MempoolGraphComponent, | ||||
|     LbtcPegsGraphComponent, | ||||
|  | ||||
| @ -1,9 +0,0 @@ | ||||
| <div class="fee-distribution-chart" *ngIf="mempoolVsizeFeesOptions; else loadingFees"> | ||||
|   <div echarts [initOpts]="mempoolVsizeFeesInitOptions" [options]="mempoolVsizeFeesOptions"></div> | ||||
| </div> | ||||
| 
 | ||||
| <ng-template #loadingFees> | ||||
|   <div class="text-center"> | ||||
|     <div class="spinner-border text-light"></div> | ||||
|   </div> | ||||
| </ng-template> | ||||
| @ -1,83 +0,0 @@ | ||||
| import { OnChanges } from '@angular/core'; | ||||
| import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-fee-distribution-graph', | ||||
|   templateUrl: './fee-distribution-graph.component.html', | ||||
|   changeDetection: ChangeDetectionStrategy.OnPush, | ||||
| }) | ||||
| export class FeeDistributionGraphComponent implements OnInit, OnChanges { | ||||
|   @Input() data: any; | ||||
|   @Input() height: number | string = 210; | ||||
|   @Input() top: number | string = 20; | ||||
|   @Input() right: number | string = 22; | ||||
|   @Input() left: number | string = 30; | ||||
| 
 | ||||
|   mempoolVsizeFeesOptions: any; | ||||
|   mempoolVsizeFeesInitOptions = { | ||||
|     renderer: 'svg' | ||||
|   }; | ||||
| 
 | ||||
|   constructor() { } | ||||
| 
 | ||||
|   ngOnInit() { | ||||
|     this.mountChart(); | ||||
|   } | ||||
| 
 | ||||
|   ngOnChanges() { | ||||
|     this.mountChart(); | ||||
|   } | ||||
| 
 | ||||
|   mountChart() { | ||||
|     this.mempoolVsizeFeesOptions = { | ||||
|       grid: { | ||||
|         height: '210', | ||||
|         right: '20', | ||||
|         top: '22', | ||||
|         left: '30', | ||||
|       }, | ||||
|       xAxis: { | ||||
|         type: 'category', | ||||
|         boundaryGap: false, | ||||
|       }, | ||||
|       yAxis: { | ||||
|         type: 'value', | ||||
|         splitLine: { | ||||
|           lineStyle: { | ||||
|             type: 'dotted', | ||||
|             color: '#ffffff66', | ||||
|             opacity: 0.25, | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|       series: [{ | ||||
|         data: this.data, | ||||
|         type: 'line', | ||||
|         label: { | ||||
|           show: true, | ||||
|           position: 'top', | ||||
|           color: '#ffffff', | ||||
|           textShadowBlur: 0, | ||||
|           formatter: (label: any) => { | ||||
|             return Math.floor(label.data); | ||||
|           }, | ||||
|         }, | ||||
|         smooth: true, | ||||
|         lineStyle: { | ||||
|           color: '#D81B60', | ||||
|           width: 4, | ||||
|         }, | ||||
|         itemStyle: { | ||||
|           color: '#b71c1c', | ||||
|           borderWidth: 10, | ||||
|           borderMiterLimit: 10, | ||||
|           opacity: 1, | ||||
|         }, | ||||
|         areaStyle: { | ||||
|           color: '#D81B60', | ||||
|           opacity: 1, | ||||
|         } | ||||
|       }] | ||||
|     }; | ||||
|   } | ||||
| } | ||||
| @ -1,8 +1,8 @@ | ||||
| import { Component, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnInit } from '@angular/core'; | ||||
| import { formatDate } from '@angular/common'; | ||||
| import { EChartsOption } from 'echarts'; | ||||
| import { OnChanges } from '@angular/core'; | ||||
| import { StorageService } from 'src/app/services/storage.service'; | ||||
| import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-incoming-transactions-graph', | ||||
| @ -25,6 +25,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { | ||||
|   @Input() top: number | string = '20'; | ||||
|   @Input() left: number | string = '0'; | ||||
|   @Input() template: ('widget' | 'advanced') = 'widget'; | ||||
|   @Input() windowPreferenceOverride: string; | ||||
| 
 | ||||
|   isLoading = true; | ||||
|   mempoolStatsChartOption: EChartsOption = {}; | ||||
| @ -46,7 +47,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { | ||||
|     if (!this.data) { | ||||
|       return; | ||||
|     } | ||||
|     this.windowPreference = this.storageService.getValue('graphWindowPreference'); | ||||
|     this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); | ||||
|     this.mountChart(); | ||||
|   } | ||||
| 
 | ||||
| @ -73,10 +74,12 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { | ||||
|         maxSpan: 100, | ||||
|         minSpan: 10, | ||||
|       }, { | ||||
|         showDetail: false, | ||||
|         show: (this.template === 'advanced') ? true : false, | ||||
|         type: 'slider', | ||||
|         brushSelect: false, | ||||
|         realtime: true, | ||||
|         bottom: 0, | ||||
|         selectedDataBackground: { | ||||
|           lineStyle: { | ||||
|             color: '#fff', | ||||
| @ -85,7 +88,7 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { | ||||
|           areaStyle: { | ||||
|             opacity: 0, | ||||
|           } | ||||
|         } | ||||
|         }, | ||||
|       }], | ||||
|       tooltip: { | ||||
|         trigger: 'axis', | ||||
| @ -102,29 +105,39 @@ export class IncomingTransactionsGraphComponent implements OnInit, OnChanges { | ||||
|           type: 'line', | ||||
|         }, | ||||
|         formatter: (params: any) => { | ||||
|           const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue);          | ||||
|           const colorSpan = (color: string) => `<span class="indicator" style="background-color: ` + color + `"></span>`; | ||||
|           let itemFormatted = '<div class="title">' + params[0].axisValue + '</div>'; | ||||
|           let itemFormatted = '<div class="title">' + axisValueLabel + '</div>'; | ||||
|           params.map((item: any, index: number) => { | ||||
|             if (index < 26) { | ||||
|               itemFormatted += `<div class="item">
 | ||||
|                 <div class="indicator-container">${colorSpan(item.color)}</div> | ||||
|                 <div class="grow"></div> | ||||
|                 <div class="value">${item.value} <span class="symbol">vB/s</span></div> | ||||
|                 <div class="value">${item.value[1]} <span class="symbol">vB/s</span></div> | ||||
|               </div>`;
 | ||||
|             } | ||||
|           }); | ||||
|           return `<div class="tx-wrapper-tooltip-chart ${(this.template === 'advanced') ? 'tx-wrapper-tooltip-chart-advanced' : ''}">${itemFormatted}</div>`; | ||||
|         } | ||||
|       }, | ||||
|       xAxis: { | ||||
|         type: 'category', | ||||
|         axisLabel: { | ||||
|           align: 'center', | ||||
|           fontSize: 11, | ||||
|           lineHeight: 12 | ||||
|         }, | ||||
|         data: this.data.labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`), | ||||
|       }, | ||||
|       xAxis: [ | ||||
|         { | ||||
|           name: formatterXAxisLabel(this.locale, this.windowPreference), | ||||
|           nameLocation: 'middle', | ||||
|           nameTextStyle: { | ||||
|             padding: [20, 0, 0, 0], | ||||
|           }, | ||||
|           type: 'time', | ||||
|           axisLabel: { | ||||
|             margin: 20, | ||||
|             align: 'center', | ||||
|             fontSize: 11, | ||||
|             lineHeight: 12, | ||||
|             hideOverlap: true, | ||||
|             padding: [0, 5], | ||||
|           }, | ||||
|         } | ||||
|       ], | ||||
|       yAxis: { | ||||
|         type: 'value', | ||||
|         axisLabel: { | ||||
|  | ||||
| @ -40,9 +40,6 @@ | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|       <div class="col-md chart-container"> | ||||
|         <app-fee-distribution-graph [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|  | ||||
| @ -1,13 +1,12 @@ | ||||
| import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; | ||||
| import { formatDate } from '@angular/common'; | ||||
| import { VbytesPipe } from 'src/app/shared/pipes/bytes-pipe/vbytes.pipe'; | ||||
| import { formatNumber } from "@angular/common"; | ||||
| 
 | ||||
| import { OptimizedMempoolStats } from 'src/app/interfaces/node-api.interface'; | ||||
| import { StateService } from 'src/app/services/state.service'; | ||||
| import { StorageService } from 'src/app/services/storage.service'; | ||||
| import { EChartsOption } from 'echarts'; | ||||
| import { feeLevels, chartColors } from 'src/app/app.constants'; | ||||
| import { formatterXAxis, formatterXAxisLabel } from 'src/app/shared/graphs.utils'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-mempool-graph', | ||||
| @ -32,6 +31,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|   @Input() left: number | string = 75; | ||||
|   @Input() template: ('widget' | 'advanced') = 'widget'; | ||||
|   @Input() showZoom = true; | ||||
|   @Input() windowPreferenceOverride: string; | ||||
| 
 | ||||
|   isLoading = true; | ||||
|   mempoolVsizeFeesData: any; | ||||
| @ -62,7 +62,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|     if (!this.data) { | ||||
|       return; | ||||
|     } | ||||
|     this.windowPreference = this.storageService.getValue('graphWindowPreference'); | ||||
|     this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); | ||||
|     this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); | ||||
|     this.mountFeeChart(); | ||||
|   } | ||||
| @ -97,13 +97,13 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|   } | ||||
| 
 | ||||
|   generateArray(mempoolStats: OptimizedMempoolStats[]) { | ||||
|     const finalArray: number[][] = []; | ||||
|     let feesArray: number[] = []; | ||||
|     const finalArray: number[][][] = []; | ||||
|     let feesArray: number[][] = []; | ||||
|     let limitFeesTemplate = this.template === 'advanced' ? 26 : 20; | ||||
|     for (let index = limitFeesTemplate; index > -1; index--) { | ||||
|       feesArray = []; | ||||
|       mempoolStats.forEach((stats) => { | ||||
|         feesArray.push(stats.vsizes[index] ? stats.vsizes[index] : 0); | ||||
|         feesArray.push([stats.added * 1000, stats.vsizes[index] ? stats.vsizes[index] : 0]); | ||||
|       }); | ||||
|       finalArray.push(feesArray); | ||||
|     } | ||||
| @ -113,7 +113,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
| 
 | ||||
|   mountFeeChart() { | ||||
|     this.orderLevels(); | ||||
|     const { labels, series } = this.mempoolVsizeFeesData; | ||||
|     const { series } = this.mempoolVsizeFeesData; | ||||
| 
 | ||||
|     const seriesGraph = []; | ||||
|     const newColors = []; | ||||
| @ -186,14 +186,15 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|           type: 'line', | ||||
|         }, | ||||
|         formatter: (params: any) => { | ||||
|           const axisValueLabel: string = formatterXAxis(this.locale, this.windowPreference, params[0].axisValue);          | ||||
|           const { totalValue, totalValueArray } = this.getTotalValues(params); | ||||
|           const itemFormatted = []; | ||||
|           let totalParcial = 0; | ||||
|           let progressPercentageText = ''; | ||||
|           const items = this.inverted ? [...params].reverse() : params; | ||||
|           items.map((item: any, index: number) => { | ||||
|             totalParcial += item.value; | ||||
|             const progressPercentage = (item.value / totalValue) * 100; | ||||
|             totalParcial += item.value[1]; | ||||
|             const progressPercentage = (item.value[1] / totalValue) * 100; | ||||
|             const progressPercentageSum = (totalValueArray[index] / totalValue) * 100; | ||||
|             let activeItemClass = ''; | ||||
|             let hoverActive = 0; | ||||
| @ -233,7 +234,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|               </td> | ||||
|               <td class="total-progress-sum"> | ||||
|                 <span> | ||||
|                   ${this.vbytesPipe.transform(item.value, 2, 'vB', 'MvB', false)} | ||||
|                   ${this.vbytesPipe.transform(item.value[1], 2, 'vB', 'MvB', false)} | ||||
|                 </span> | ||||
|               </td> | ||||
|               <td class="total-progress-sum"> | ||||
| @ -257,7 +258,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|           const titleSum = $localize`Sum`; | ||||
|           return `<div class="fees-wrapper-tooltip-chart ${classActive}">
 | ||||
|             <div class="title"> | ||||
|               ${params[0].axisValue} | ||||
|               ${axisValueLabel} | ||||
|               <span class="total-value"> | ||||
|                 ${this.vbytesPipe.transform(totalValue, 2, 'vB', 'MvB', false)} | ||||
|               </span> | ||||
| @ -288,6 +289,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|         maxSpan: 100, | ||||
|         minSpan: 10, | ||||
|       }, { | ||||
|         showDetail: false, | ||||
|         show: (this.template === 'advanced' && this.showZoom) ? true : false, | ||||
|         type: 'slider', | ||||
|         brushSelect: false, | ||||
| @ -312,15 +314,22 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|       }, | ||||
|       xAxis: [ | ||||
|         { | ||||
|           type: 'category', | ||||
|           name: formatterXAxisLabel(this.locale, this.windowPreference), | ||||
|           nameLocation: 'middle', | ||||
|           nameTextStyle: { | ||||
|             padding: [20, 0, 0, 0], | ||||
|           }, | ||||
|           type: 'time', | ||||
|           boundaryGap: false, | ||||
|           axisLine: { onZero: true }, | ||||
|           axisLabel: { | ||||
|             margin: 20, | ||||
|             align: 'center', | ||||
|             fontSize: 11, | ||||
|             lineHeight: 12, | ||||
|             hideOverlap: true, | ||||
|             padding: [0, 5], | ||||
|           }, | ||||
|           data: labels.map((value: any) => `${formatDate(value, 'M/d', this.locale)}\n${formatDate(value, 'H:mm', this.locale)}`), | ||||
|         } | ||||
|       ], | ||||
|       yAxis: { | ||||
| @ -346,7 +355,7 @@ export class MempoolGraphComponent implements OnInit, OnChanges { | ||||
|     const totalValueArray = []; | ||||
|     const valuesInverted = this.inverted ? values : [...values].reverse(); | ||||
|     for (const item of valuesInverted) { | ||||
|       totalValueTemp += item.value; | ||||
|       totalValueTemp += item.value[1]; | ||||
|       totalValueArray.push(totalValueTemp); | ||||
|     } | ||||
|     return { | ||||
|  | ||||
| @ -134,7 +134,7 @@ export class StatisticsComponent implements OnInit { | ||||
| 
 | ||||
|     this.mempoolTransactionsWeightPerSecondData = { | ||||
|       labels: labels, | ||||
|       series: [mempoolStats.map((stats) => stats.vbytes_per_second)], | ||||
|       series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])], | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -54,6 +54,7 @@ | ||||
|                 [limitFee]="150" | ||||
|                 [limitFilterFee]="1" | ||||
|                 [data]="mempoolStats.value?.mempool" | ||||
|                 [windowPreferenceOverride]="'2h'" | ||||
|                 ></app-mempool-graph> | ||||
|               </div> | ||||
|             </ng-container> | ||||
| @ -73,6 +74,7 @@ | ||||
|                 <app-incoming-transactions-graph | ||||
|                   [left]="50" | ||||
|                   [data]="mempoolStats.value?.weightPerSecond" | ||||
|                   [windowPreferenceOverride]="'2h'" | ||||
|                   ></app-incoming-transactions-graph> | ||||
|               </div> | ||||
|             </ng-template> | ||||
|  | ||||
| @ -254,7 +254,6 @@ export class DashboardComponent implements OnInit { | ||||
|           ); | ||||
|         }), | ||||
|         map((mempoolStats) => { | ||||
|           const data = this.handleNewMempoolData(mempoolStats.concat([])); | ||||
|           return { | ||||
|             mempool: mempoolStats, | ||||
|             weightPerSecond: this.handleNewMempoolData(mempoolStats.concat([])), | ||||
| @ -286,7 +285,7 @@ export class DashboardComponent implements OnInit { | ||||
| 
 | ||||
|     return { | ||||
|       labels: labels, | ||||
|       series: [mempoolStats.map((stats) => stats.vbytes_per_second)], | ||||
|       series: [mempoolStats.map((stats) => [stats.added * 1000, stats.vbytes_per_second])], | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| export interface OptimizedMempoolStats { | ||||
|   id: number; | ||||
|   added: string; | ||||
|   added: number; | ||||
|   unconfirmed_transactions: number; | ||||
|   tx_per_second: number; | ||||
|   vbytes_per_second: number; | ||||
|  | ||||
| @ -1,9 +1,25 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { Router, ActivatedRoute } from '@angular/router'; | ||||
| 
 | ||||
| @Injectable({ | ||||
|   providedIn: 'root' | ||||
| }) | ||||
| export class StorageService { | ||||
|   constructor(private router: Router, private route: ActivatedRoute) { | ||||
|     let graphWindowPreference: string = this.getValue('graphWindowPreference'); | ||||
|     if (graphWindowPreference === null) { // First visit to mempool.space
 | ||||
|       if (this.router.url.includes("graphs")) { | ||||
|         this.setValue('graphWindowPreference', this.route.snapshot.fragment ? this.route.snapshot.fragment : "2h"); | ||||
|       } else { | ||||
|         this.setValue('graphWindowPreference', "2h"); | ||||
|       } | ||||
|     } else if (this.router.url.includes("graphs")) { // Visit a different graphs#fragment from last visit
 | ||||
|         if (this.route.snapshot.fragment !== null && graphWindowPreference !== this.route.snapshot.fragment) { | ||||
|           this.setValue('graphWindowPreference', this.route.snapshot.fragment); | ||||
|         } | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   getValue(key: string): string { | ||||
|     try { | ||||
|       return localStorage.getItem(key); | ||||
|  | ||||
							
								
								
									
										50
									
								
								frontend/src/app/shared/graphs.utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								frontend/src/app/shared/graphs.utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| export const formatterXAxis = ( | ||||
|   locale: string, | ||||
|   windowPreference: string, | ||||
|   value: string | ||||
| ) => { | ||||
| 
 | ||||
|   if(value.length === 0){ | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
|   const date = new Date(value); | ||||
|   switch (windowPreference) { | ||||
|     case '2h': | ||||
|       return date.toLocaleTimeString(locale, { hour: 'numeric', minute: 'numeric' }); | ||||
|     case '24h': | ||||
|       return date.toLocaleTimeString(locale, { weekday: 'short', hour: 'numeric', minute: 'numeric' }); | ||||
|     case '1w': | ||||
|       return date.toLocaleTimeString(locale, { month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' }); | ||||
|     case '1m': | ||||
|     case '3m': | ||||
|     case '6m': | ||||
|     case '1y': | ||||
|       return date.toLocaleTimeString(locale, { month: 'short', day: 'numeric', hour: 'numeric' }); | ||||
|     case '2y': | ||||
|     case '3y': | ||||
|       return date.toLocaleDateString(locale, { year: 'numeric', month: 'short', day: 'numeric' }); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| export const formatterXAxisLabel = ( | ||||
|   locale: string, | ||||
|   windowPreference: string, | ||||
| ) => { | ||||
|   const date = new Date(); | ||||
|   switch (windowPreference) { | ||||
|     case '2h': | ||||
|     case '24h': | ||||
|       return date.toLocaleDateString(locale, { year: 'numeric', month: 'short', day: 'numeric' }); | ||||
|     case '1w': | ||||
|       return date.toLocaleDateString(locale, { year: 'numeric', month: 'long' }); | ||||
|     case '1m': | ||||
|     case '3m': | ||||
|     case '6m': | ||||
|       return date.toLocaleDateString(locale, { year: 'numeric' }); | ||||
|     case '1y': | ||||
|     case '2y': | ||||
|     case '3y': | ||||
|       return null; | ||||
|   } | ||||
| }; | ||||
| @ -555,7 +555,7 @@ html:lang(ru) .card-title { | ||||
| } | ||||
| 
 | ||||
| .tx-wrapper-tooltip-chart-advanced { | ||||
|   width: 115px; | ||||
|   width: 140px; | ||||
|   .indicator-container { | ||||
|     .indicator { | ||||
|       margin-right: 5px; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user