Added fee distribution Pie Chart to the block inspector.

This commit is contained in:
Simon Lindh 2019-08-18 17:22:16 +03:00
parent bd19b88f11
commit 9b287336d0
9 changed files with 169 additions and 119 deletions

View File

@ -19,6 +19,7 @@ import { BlockchainBlocksComponent } from './blockchain-blocks/blockchain-blocks
import { BlockchainProjectedBlocksComponent } from './blockchain-projected-blocks/blockchain-projected-blocks.component';
import { ApiService } from './services/api.service';
import { MasterPageComponent } from './master-page/master-page.component';
import { FeeDistributionGraphComponent } from './fee-distribution-graph/fee-distribution-graph.component';
@NgModule({
declarations: [
@ -34,6 +35,7 @@ import { MasterPageComponent } from './master-page/master-page.component';
BlockchainBlocksComponent,
BlockchainProjectedBlocksComponent,
MasterPageComponent,
FeeDistributionGraphComponent,
],
imports: [
ReactiveFormsModule,

View File

@ -30,16 +30,5 @@
<hr>
<div style="height: 400px;" *ngIf="mempoolVsizeFeesData; else loadingFees">
<app-chartist
[data]="mempoolVsizeFeesData"
[type]="'Bar'"
[options]="mempoolVsizeFeesOptions">
</app-chartist>
</div>
<ng-template #loadingFees>
<div class="text-center">
<div class="spinner-border text-light"></div>
</div>
</ng-template>
<app-fee-distribution-graph [blockHeight]="block.height"></app-fee-distribution-graph>
</div>

View File

@ -1,9 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ApiService } from '../../services/api.service';
import { IBlock } from '../../blockchain/interfaces';
import { MemPoolService } from '../../services/mem-pool.service';
import * as Chartist from 'chartist';
@Component({
selector: 'app-block-modal',
@ -13,61 +11,17 @@ import * as Chartist from 'chartist';
export class BlockModalComponent implements OnInit {
@Input() block: IBlock;
mempoolVsizeFeesData: any;
mempoolVsizeFeesOptions: any;
conversions: any;
constructor(
public activeModal: NgbActiveModal,
private apiService: ApiService,
private memPoolService: MemPoolService,
) { }
ngOnInit() {
this.mempoolVsizeFeesOptions = {
showArea: false,
showLine: false,
fullWidth: false,
showPoint: false,
low: 0,
axisX: {
position: 'start',
showLabel: false,
offset: 0,
showGrid: false,
},
axisY: {
position: 'end',
scaleMinSpace: 40,
showGrid: false,
},
plugins: [
Chartist.plugins.tooltip({
tooltipOffset: {
x: 15,
y: 250
},
transformTooltipTextFnc: (value: number): any => {
return Math.ceil(value) + ' sat/vB';
},
anchorToPoint: false,
})
]
};
this.memPoolService.conversions$
.subscribe((conversions) => {
this.conversions = conversions;
});
this.apiService.listTransactionsForBlock$(this.block.height)
.subscribe((data) => {
this.mempoolVsizeFeesData = {
labels: data.map((x, i) => i),
series: [data.map((tx) => tx.fpv)]
};
});
}
}

View File

@ -26,16 +26,5 @@
<hr>
<div style="height: 400px;" *ngIf="mempoolVsizeFeesData; else loadingFees">
<app-chartist
[data]="mempoolVsizeFeesData"
[type]="'Bar'"
[options]="mempoolVsizeFeesOptions">
</app-chartist>
</div>
<ng-template #loadingFees>
<div class="text-center">
<div class="spinner-border text-light"></div>
</div>
</ng-template>
<app-fee-distribution-graph [projectedBlockIndex]="index"></app-fee-distribution-graph>
</div>

View File

@ -1,9 +1,7 @@
import { Component, OnInit, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ApiService } from '../../services/api.service';
import { IBlock } from '../../blockchain/interfaces';
import { MemPoolService } from '../../services/mem-pool.service';
import * as Chartist from 'chartist';
import { IBlock } from 'src/app/blockchain/interfaces';
@Component({
selector: 'app-projected-block-modal',
@ -14,61 +12,18 @@ export class ProjectedBlockModalComponent implements OnInit {
@Input() block: IBlock;
@Input() index: number;
mempoolVsizeFeesData: any;
mempoolVsizeFeesOptions: any;
conversions: any;
constructor(
public activeModal: NgbActiveModal,
private apiService: ApiService,
private memPoolService: MemPoolService,
) { }
ngOnInit() {
this.mempoolVsizeFeesOptions = {
showArea: false,
showLine: false,
fullWidth: false,
showPoint: false,
low: 0,
axisX: {
position: 'start',
showLabel: false,
offset: 0,
showGrid: false,
},
axisY: {
position: 'end',
scaleMinSpace: 40,
showGrid: false,
},
plugins: [
Chartist.plugins.tooltip({
tooltipOffset: {
x: 15,
y: 250
},
transformTooltipTextFnc: (value: number): any => {
return Math.ceil(value) + ' sat/vB';
},
anchorToPoint: false,
})
]
};
this.memPoolService.conversions$
.subscribe((conversions) => {
this.conversions = conversions;
});
this.apiService.listTransactionsForProjectedBlock$(this.index)
.subscribe((data) => {
this.mempoolVsizeFeesData = {
labels: data.map((x, i) => i),
series: [data.map((tx) => tx.fpv)]
};
});
}
}

View File

@ -0,0 +1,30 @@
<div style="height: 400px;" *ngIf="mempoolVsizeFeesData; else loadingFees">
<form [formGroup]="radioGroupForm" style="position: absolute;">
<div class="btn-group btn-group-toggle" ngbRadioGroup name="radioBasic" formControlName="model">
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" value="line"> Line
</label>
<label ngbButtonLabel class="btn-primary btn-sm">
<input ngbButton type="radio" value="pie"> Pie
</label>
</div>
</form>
<app-chartist
*ngIf="radioGroupForm.get('model')?.value === 'pie'"
[data]="mempoolVsizeFeesPieData"
[type]="'Pie'"
[options]="mempoolVsizeFeesPieOptions">
</app-chartist>
<app-chartist
*ngIf="radioGroupForm.get('model')?.value === 'line'"
[data]="mempoolVsizeFeesData"
[type]="'Bar'"
[options]="mempoolVsizeFeesOptions">
</app-chartist>
</div>
<ng-template #loadingFees>
<div class="text-center">
<div class="spinner-border text-light"></div>
</div>
</ng-template>

View File

@ -0,0 +1,131 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import * as Chartist from 'chartist';
import { ApiService } from '../services/api.service';
@Component({
selector: 'app-fee-distribution-graph',
templateUrl: './fee-distribution-graph.component.html',
styleUrls: ['./fee-distribution-graph.component.scss']
})
export class FeeDistributionGraphComponent implements OnInit {
@Input() projectedBlockIndex: number;
@Input() blockHeight: number;
mempoolVsizeFeesData: any;
mempoolVsizeFeesOptions: any;
mempoolVsizeFeesPieData: any;
mempoolVsizeFeesPieOptions: any;
feeLevels = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
250, 300, 350, 400, 500];
radioGroupForm: FormGroup;
constructor(
private formBuilder: FormBuilder,
private apiService: ApiService,
) { }
ngOnInit() {
this.radioGroupForm = this.formBuilder.group({
model: ['line'],
});
this.mempoolVsizeFeesOptions = {
showArea: false,
showLine: false,
fullWidth: false,
showPoint: false,
low: 0,
axisX: {
position: 'start',
showLabel: false,
offset: 0,
showGrid: false,
},
axisY: {
position: 'end',
scaleMinSpace: 40,
showGrid: false,
},
plugins: [
Chartist.plugins.tooltip({
tooltipOffset: {
x: 15,
y: 250
},
transformTooltipTextFnc: (value: number): any => {
return Math.ceil(value) + ' sat/vB';
},
anchorToPoint: false,
})
]
};
this.mempoolVsizeFeesPieOptions = {
showLabel: false,
plugins: [
Chartist.plugins.tooltip({
tooltipOffset: {
x: 15,
y: 250
},
transformTooltipTextFnc: (value: string, seriesName: string): any => {
const index = parseInt(seriesName.split(' ')[2].split('-')[1], 10);
const intValue = parseInt(value, 10);
const result = Math.ceil(intValue) + ' tx @ ' + this.feeLevels[index] +
(this.feeLevels[index + 1] ? '-' + this.feeLevels[index + 1] : '+' ) + ' sat/vB';
return result;
},
anchorToPoint: false,
})
]
};
let sub;
if (this.blockHeight) {
sub = this.apiService.listTransactionsForBlock$(this.blockHeight);
} else {
sub = this.apiService.listTransactionsForProjectedBlock$(this.projectedBlockIndex);
}
sub.subscribe((data) => {
const fees = data.map((tx) => tx.fpv);
const series = [];
for (let i = 0; i < this.feeLevels.length; i++) {
let total = 0;
for (let j = 0; j < fees.length; j++) {
if (i === this.feeLevels.length - 1) {
if (fees[j] >= this.feeLevels[i]) {
total += 1;
}
} else if (fees[j] >= this.feeLevels[i] && fees[j] < this.feeLevels[i + 1]) {
total += 1;
}
}
series.push(total);
}
this.mempoolVsizeFeesPieData = {
series: series.map((d, index: number) => {
return {
value: d,
className: 'ct-series-' + Chartist.alphaNumerate(index) + ' index-' + index
};
}),
labels: data.map((x, i) => i),
};
this.mempoolVsizeFeesData = {
labels: data.map((x, i) => i),
series: [fees]
};
});
}
}

View File

@ -496,9 +496,9 @@ Chartist.plugins.tooltip = function (options: any) {
return function tooltip(chart: any) {
let tooltipSelector = options.pointClass;
if (chart.constructor.name === Chartist.Bar.prototype.constructor.name) {
if (chart instanceof Chartist.Bar) {
tooltipSelector = 'ct-bar';
} else if (chart.constructor.name === Chartist.Pie.prototype.constructor.name) {
} else if (chart instanceof Chartist.Pie) {
// Added support for donut graph
if (chart.options.donut) {
tooltipSelector = 'ct-slice-donut';
@ -542,7 +542,7 @@ Chartist.plugins.tooltip = function (options: any) {
let value = $point.getAttribute('ct:value');
if (options.transformTooltipTextFnc && typeof options.transformTooltipTextFnc === 'function') {
value = options.transformTooltipTextFnc(value);
value = options.transformTooltipTextFnc(value, $point.parentNode.getAttribute('class'));
}
if (options.tooltipFnc && typeof options.tooltipFnc === 'function') {