import { Component, OnInit, Input, Inject, LOCALE_ID, ChangeDetectionStrategy, OnChanges } from '@angular/core'; 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', templateUrl: './mempool-graph.component.html', styles: [` .loadingGraphs { position: absolute; top: 50%; left: calc(50% - 16px); z-index: 100; } `], changeDetection: ChangeDetectionStrategy.OnPush, }) export class MempoolGraphComponent implements OnInit, OnChanges { @Input() data: any[]; @Input() limitFee = 350; @Input() limitFilterFee = 1; @Input() height: number | string = 200; @Input() top: number | string = 20; @Input() right: number | string = 10; @Input() left: number | string = 75; @Input() template: ('widget' | 'advanced') = 'widget'; @Input() showZoom = true; @Input() windowPreferenceOverride: string; isLoading = true; mempoolVsizeFeesData: any; mempoolVsizeFeesOptions: EChartsOption; mempoolVsizeFeesInitOptions = { renderer: 'svg', }; windowPreference: string; hoverIndexSerie = 0; feeLimitIndex: number; feeLevelsOrdered = []; chartColorsOrdered = chartColors; inverted: boolean; constructor( private vbytesPipe: VbytesPipe, private stateService: StateService, private storageService: StorageService, @Inject(LOCALE_ID) private locale: string, ) { } ngOnInit(): void { this.isLoading = true; this.inverted = this.storageService.getValue('inverted-graph') === 'true'; } ngOnChanges() { if (!this.data) { return; } this.windowPreference = this.windowPreferenceOverride ? this.windowPreferenceOverride : this.storageService.getValue('graphWindowPreference'); this.mempoolVsizeFeesData = this.handleNewMempoolData(this.data.concat([])); this.mountFeeChart(); } rendered() { if (!this.data) { return; } this.isLoading = false; } onChartReady(myChart: any) { myChart.getZr().on('mousemove', (e: any) => { if (e.target !== undefined && e.target.parent !== undefined && e.target.parent.parent !== null && e.target.parent.parent.__ecComponentInfo !== undefined) { this.hoverIndexSerie = e.target.parent.parent.__ecComponentInfo.index; } }); } handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) { mempoolStats.reverse(); const labels = mempoolStats.map(stats => stats.added); const finalArrayVByte = this.generateArray(mempoolStats); return { labels: labels, series: finalArrayVByte }; } generateArray(mempoolStats: OptimizedMempoolStats[]) { 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.added * 1000, stats.vsizes[index] ? stats.vsizes[index] : 0]); }); finalArray.push(feesArray); } finalArray.reverse(); return finalArray; } mountFeeChart() { this.orderLevels(); const { series } = this.mempoolVsizeFeesData; const seriesGraph = []; const newColors = []; for (let index = 0; index < series.length; index++) { const value = series[index]; if (index >= this.feeLimitIndex) { newColors.push(this.chartColorsOrdered[index]); seriesGraph.push({ name: this.feeLevelsOrdered[index], type: 'line', stack: 'fees', smooth: false, markPoint: { symbol: 'rect', }, lineStyle: { width: 0, opacity: 0, }, symbol: 'none', emphasis: { focus: 'none', areaStyle: { opacity: 0.85, }, }, markLine: { silent: true, symbol: 'none', lineStyle: { color: '#fff', opacity: 1, width: this.inverted ? 2 : 0, }, data: [{ yAxis: '1000000', label: { show: false, color: '#ffffff', } }], }, areaStyle: { color: this.chartColorsOrdered[index], opacity: 1, }, data: value }); } } this.mempoolVsizeFeesOptions = { series: this.inverted ? [...seriesGraph].reverse() : seriesGraph, hover: true, color: this.inverted ? [...newColors].reverse() : newColors, tooltip: { show: !this.isMobile(), trigger: 'axis', alwaysShowContent: false, position: (pos, params, el, elRect, size) => { const positions = { top: (this.template === 'advanced') ? 0 : -30 }; positions[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 60; return positions; }, extraCssText: `width: ${(this.template === 'advanced') ? '275px' : '200px'}; background: transparent; border: none; box-shadow: none;`, axisPointer: { 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[1]; const progressPercentage = (item.value[1] / totalValue) * 100; const progressPercentageSum = (totalValueArray[index] / totalValue) * 100; let activeItemClass = ''; let hoverActive = 0; if (this.inverted) { hoverActive = Math.abs(this.feeLevelsOrdered.length - item.seriesIndex - this.feeLevelsOrdered.length); } else { hoverActive = item.seriesIndex; } if (this.hoverIndexSerie === hoverActive) { progressPercentageText = `
${titleRange} | ${titleSize} | ${titleSum} |
---|