Refactored the "blockchain" into separate sub components.

This commit is contained in:
Simon Lindh
2019-07-23 18:13:36 +03:00
parent 58c8e3a131
commit e56b828ade
16 changed files with 437 additions and 279 deletions

View File

@@ -0,0 +1,45 @@
<div class="modal-header">
<h4 class="modal-title">Fee distribution for block <a href="https://www.blockstream.info/block-height/{{ block.height }}" target="_blank">#{{ block.height }}</a></h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div>
<table class="table table-borderless table-sm">
<tr>
<th>Median fee:</th>
<td>~{{ block.medianFee | ceil }} sat/vB <span *ngIf="conversions">(~<span class="green-color">{{ conversions.USD * (block.medianFee/100000000)*250 | currency:'USD':'symbol':'1.2-2' }}</span>/tx)</span></td>
<th>Block size:</th>
<td>{{ block.size | bytes: 2 }}</td>
</tr>
<tr>
<th>Fee span:</th>
<td class="yellow-color">{{ block.minFee | ceil }} - {{ block.maxFee | ceil }} sat/vB</td>
<th>Tx count:</th>
<td>{{ block.nTx }} transactions</td>
</tr>
<tr>
<th>Total fees:</th>
<td>{{ (block.fees - 12.5) | number: '1.2-2' }} BTC <span *ngIf="conversions">(<span class="green-color">{{ conversions.USD * (block.fees - 12.5) | currency:'USD':'symbol':'1.0-0' }}</span>)</span></td>
<th>Block reward + fees:</th>
<td>{{ block.fees | number: '1.2-2' }} BTC <span *ngIf="conversions">(<span class="green-color">{{ conversions.USD * block.fees | currency:'USD':'symbol':'1.0-0' }}</span>)</span></td>
</tr>
</table>
</div>
<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>
</div>

View File

@@ -0,0 +1,7 @@
.yellow-color {
color: #ffd800;
}
.green-color {
color: #3bcc49;
}

View File

@@ -0,0 +1,73 @@
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',
templateUrl: './block-modal.component.html',
styleUrls: ['./block-modal.component.scss']
})
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

@@ -0,0 +1,26 @@
<div class="blocks-container" *ngIf="blocks.length">
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
<div (click)="openBlockModal(block);" class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="getStyleForBlock(block)">
<div class="block-height">
<a href="https://www.blockstream.info/block-height/{{ block.height }}" target="_blank">#{{ block.height }}</a>
</div>
<div class="block-body">
<div class="fees">
~{{ block.medianFee | ceil }} sat/vB
<br/>
<span class="yellow-color">{{ block.minFee | ceil }} - {{ block.maxFee | ceil }} sat/vB</span>
</div>
<div class="block-size">{{ block.size | bytes: 2 }}</div>
<div class="transaction-count">{{ block.nTx }} transactions</div>
<br /><br />
<div class="time-difference">{{ getTimeSinceMined(block) }} ago</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,128 @@
.bitcoin-block {
width: 125px;
height: 125px;
cursor: pointer;
}
.mined-block {
position: absolute;
top: 0px;
transition: 1s;
}
.block-size {
font-size: 18px;
font-weight: bold;
}
.blocks-container {
position: absolute;
top: 0px;
left: 40px;
}
.projected-block {
position: absolute;
top: 0;
}
.block-body {
text-align: center;
}
@keyframes opacityPulse {
0% {opacity: 0.7;}
50% {opacity: 1.0;}
100% {opacity: 0.7;}
}
.time-difference {
position: absolute;
bottom: 10px;
text-align: center;
width: 100%;
font-size: 14px;
}
.fees {
font-size: 10px;
margin-top: 10px;
margin-bottom: 2px;
}
.btcblockmiddle {
height: 18px;
}
.breakRow {
height: 30px;
margin-top: 20px;
}
.yellow-color {
color: #ffd800;
}
.transaction-count {
font-size: 12px;
}
.block-height {
position: absolute;
font-size: 12px;
bottom: 160px;
width: 100%;
left: -12px;
text-shadow: 0px 32px 3px #111;
z-index: 100;
}
@media (max-width: 767.98px) {
.block-height {
bottom: 125px;
left: inherit;
text-shadow: inherit;
z-index: inherit;
}
}
@media (min-width: 768px) {
.bitcoin-block::after {
content: '';
width: 125px;
height: 24px;
position:absolute;
top: -24px;
left: -20px;
background-color: #232838;
transform:skew(40deg);
transform-origin:top;
}
.bitcoin-block::before {
content: '';
width: 20px;
height: 125px;
position: absolute;
top: -12px;
left: -20px;
background-color: #191c27;
transform: skewY(50deg);
transform-origin: top;
}
.projected-block.bitcoin-block::after {
background-color: #403834;
}
.projected-block.bitcoin-block::before {
background-color: #2d2825;
}
}
.black-background {
background-color: #11131f;
z-index: 100;
position: relative;
}

View File

@@ -0,0 +1,62 @@
import { Component, Input } from '@angular/core';
import { IBlock } from '../blockchain/interfaces';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { BlockModalComponent } from './block-modal/block-modal.component';
@Component({
selector: 'app-blockchain-blocks',
templateUrl: './blockchain-blocks.component.html',
styleUrls: ['./blockchain-blocks.component.scss']
})
export class BlockchainBlocksComponent {
@Input() blocks: IBlock[];
constructor(
private modalService: NgbModal,
) { }
getTimeSinceMined(block: IBlock): string {
const minutes = ((new Date().getTime()) - (new Date(block.time * 1000).getTime())) / 1000 / 60;
if (minutes >= 120) {
return Math.floor(minutes / 60) + ' hours';
}
if (minutes >= 60) {
return Math.floor(minutes / 60) + ' hour';
}
if (minutes <= 1) {
return '< 1 minute';
}
if (minutes === 1) {
return '1 minute';
}
return Math.round(minutes) + ' minutes';
}
trackByBlocksFn(index: number, item: IBlock) {
return item.height;
}
openBlockModal(block: IBlock) {
const modalRef = this.modalService.open(BlockModalComponent, { size: 'lg' });
modalRef.componentInstance.block = block;
}
getStyleForBlock(block: IBlock) {
const greenBackgroundHeight = 100 - (block.weight / 4000000) * 100;
if (window.innerWidth <= 768) {
return {
'top': 155 * this.blocks.indexOf(block) + 'px',
'background': `repeating-linear-gradient(#2d3348, #2d3348 ${greenBackgroundHeight}%,
#9339f4 ${Math.max(greenBackgroundHeight, 0)}%, #105fb0 100%)`,
};
} else {
return {
'left': 155 * this.blocks.indexOf(block) + 'px',
'background': `repeating-linear-gradient(#2d3348, #2d3348 ${greenBackgroundHeight}%,
#9339f4 ${Math.max(greenBackgroundHeight, 0)}%, #105fb0 100%)`,
};
}
}
}