Block view.
This commit is contained in:
@@ -1 +1,77 @@
|
||||
<p>block works!</p>
|
||||
<div class="container">
|
||||
<h1>Block <ng-template [ngIf]="!isLoadingBlock"><a [routerLink]="['/explorer/block/', block.id]">#{{ block.height }}</a></ng-template></h1>
|
||||
|
||||
<ng-template [ngIf]="!isLoadingBlock" [ngIfElse]="loadingBlock">
|
||||
|
||||
<br>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Timestamp</td>
|
||||
<td>{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }} ({{ block.timestamp | timeSince }} ago)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Number of transactions</td>
|
||||
<td>{{ block.tx_count }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>{{ block.size | bytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td>{{ block.weight | wuBytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>{{ latestBlockHeight - block.height + 1 }} confirmations</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hash</td>
|
||||
<td><a [routerLink]="['/explorer/block/', block.id]">{{ block.id | shortenString : 36 }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Previous Block</td>
|
||||
<td><a [routerLink]="['/explorer/block/', block.previousblockhash]">{{ block.previousblockhash | shortenString : 36 }}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2>{{ transactions?.length || '?' }} of {{ block.tx_count }} transactions</h2>
|
||||
|
||||
<br>
|
||||
|
||||
<app-transactions-list [transactions]="transactions"></app-transactions-list>
|
||||
|
||||
<div class="text-center">
|
||||
<ng-template [ngIf]="isLoadingTransactions">
|
||||
<div class="spinner-border text-light"></div>
|
||||
<br><br>
|
||||
</ng-template>
|
||||
<button *ngIf="transactions?.length" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
<ng-template #loadingBlock>
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-light"></div>
|
||||
<br><br>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { ApiService } from 'src/app/services/api.service';
|
||||
import { MemPoolService } from 'src/app/services/mem-pool.service';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
@@ -6,10 +11,55 @@ import { Component, OnInit } from '@angular/core';
|
||||
styleUrls: ['./block.component.scss']
|
||||
})
|
||||
export class BlockComponent implements OnInit {
|
||||
block: any;
|
||||
isLoadingBlock = true;
|
||||
latestBlockHeight: number;
|
||||
transactions: any[];
|
||||
isLoadingTransactions = true;
|
||||
|
||||
constructor() { }
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private apiService: ApiService,
|
||||
private memPoolService: MemPoolService,
|
||||
private ref: ChangeDetectorRef,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.route.paramMap.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
this.isLoadingBlock = true;
|
||||
const blockHash: string = params.get('id') || '';
|
||||
this.getBlockTransactions(blockHash);
|
||||
return this.apiService.getBlock$(blockHash);
|
||||
})
|
||||
)
|
||||
.subscribe((block) => {
|
||||
this.block = block;
|
||||
this.isLoadingBlock = false;
|
||||
this.ref.markForCheck();
|
||||
});
|
||||
|
||||
this.memPoolService.blocks$
|
||||
.subscribe((block) => {
|
||||
this.latestBlockHeight = block.height;
|
||||
});
|
||||
}
|
||||
|
||||
getBlockTransactions(hash: string) {
|
||||
this.apiService.getBlockTransactions$(hash)
|
||||
.subscribe((transactions: any) => {
|
||||
this.transactions = transactions;
|
||||
this.isLoadingTransactions = false;
|
||||
});
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
this.isLoadingTransactions = true;
|
||||
this.apiService.getBlockTransactions$(this.block.id, this.transactions.length)
|
||||
.subscribe((transactions) => {
|
||||
this.transactions = this.transactions.concat(transactions);
|
||||
this.isLoadingTransactions = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { BlockComponent } from './block/block.component';
|
||||
import { AddressComponent } from './address/address.component';
|
||||
import { TransactionsListComponent } from './transactions-list/transactions-list.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@@ -27,7 +28,7 @@ const routes: Routes = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [ExplorerComponent, TransactionComponent, BlockComponent, AddressComponent],
|
||||
declarations: [ExplorerComponent, TransactionComponent, BlockComponent, AddressComponent, TransactionsListComponent],
|
||||
imports: [
|
||||
SharedModule,
|
||||
CommonModule,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<th>Timestamp</th>
|
||||
<th>Mined</th>
|
||||
<th>Transactions</th>
|
||||
<th>Size (kB)</th>
|
||||
<th>Weight (kWU)</th>
|
||||
<th>Size</th>
|
||||
<th>Weight</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let block of blocks; let i= index;">
|
||||
@@ -17,7 +17,7 @@
|
||||
<td>{{ block.timestamp | timeSince }} ago </td>
|
||||
<td>{{ block.tx_count }}</td>
|
||||
<td>{{ block.size | bytes: 2 }}</td>
|
||||
<td>{{ block.weight | bytes: 2 }}</td>
|
||||
<td>{{ block.weight | wuBytes: 2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,96 +1,12 @@
|
||||
<div class="container">
|
||||
<h1>Transaction</h1>
|
||||
|
||||
<br>
|
||||
|
||||
<ng-template [ngIf]="!isLoadingTx" [ngIfElse]="loadingTx">
|
||||
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<tr class="header-bg">
|
||||
<th>
|
||||
{{ tx.txid }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<app-transactions-list [transactions]="[tx]" [showConfirmations]="true"></app-transactions-list>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
<table class="table table-borderless smaller-text">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div *ngFor="let vin of tx.vin">
|
||||
<ng-template [ngIf]="vin.is_coinbase" [ngIfElse]="regularVin">
|
||||
Coinbase
|
||||
</ng-template>
|
||||
<ng-template #regularVin>
|
||||
<a [routerLink]="['/explorer/address/', vin.prevout.scriptpubkey_address]">{{ vin.prevout.scriptpubkey_address }}</a>
|
||||
(<a [routerLink]="['/explorer/tx/', vin.txid]">tx</a>)
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div *ngFor="let vin of tx.vin">
|
||||
<ng-template [ngIf]="vin.prevout">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVin">
|
||||
<span class="green-color">{{ conversions.USD * (vin.prevout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatVin>
|
||||
{{ vin.prevout.value / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div *ngFor="let vout of tx.vout">
|
||||
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
|
||||
<ng-template #scriptpubkey_type>
|
||||
{{ vout.scriptpubkey_type | uppercase }}
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div *ngFor="let vout of tx.vout">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
|
||||
<span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatVout>
|
||||
{{ vout.value / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-right" colspan="4">
|
||||
<button *ngIf="tx.status.confirmed" type="button" class="btn btn-success">{{ latestBlockHeight - tx.status.block_height + 1 }} confirmations</button>
|
||||
<ng-template #unconfirmedButton>
|
||||
<button type="button" class="btn btn-danger">Unconfirmed</button>
|
||||
</ng-template>
|
||||
|
||||
<button type="button" class="btn btn-primary" (click)="viewFiat = !viewFiat">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatButton">
|
||||
<span *ngIf="conversions">{{ conversions.USD * (totalOutput / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatButton>
|
||||
{{ totalOutput / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<h2>Details</h2>
|
||||
@@ -103,11 +19,11 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Size</td>
|
||||
<td>{{ tx.size | bytes }}</td>
|
||||
<td>{{ tx.size | bytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td>{{ tx.weight }} WU</td>
|
||||
<td>{{ tx.weight | wuBytes: 2 }}</td>
|
||||
</tr>
|
||||
<tr *ngIf="tx.status.confirmed">
|
||||
<td>Included in block</td>
|
||||
@@ -117,7 +33,7 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="col">
|
||||
<table class="table table-borderless table-striped" *ngIf="tx.fee">
|
||||
<table class="table table-borderless table-striped" *ngIf="tx.fee">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Fees</td>
|
||||
|
||||
@@ -13,10 +13,6 @@ export class TransactionComponent implements OnInit {
|
||||
tx: any;
|
||||
isLoadingTx = true;
|
||||
conversions: any;
|
||||
totalOutput: number;
|
||||
|
||||
viewFiat = false;
|
||||
latestBlockHeight: number;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
@@ -33,7 +29,6 @@ export class TransactionComponent implements OnInit {
|
||||
)
|
||||
.subscribe((tx) => {
|
||||
this.tx = tx;
|
||||
this.totalOutput = this.tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b);
|
||||
this.isLoadingTx = false;
|
||||
});
|
||||
|
||||
@@ -41,10 +36,5 @@ export class TransactionComponent implements OnInit {
|
||||
.subscribe((conversions) => {
|
||||
this.conversions = conversions;
|
||||
});
|
||||
|
||||
this.memPoolService.blocks$
|
||||
.subscribe((block) => {
|
||||
this.latestBlockHeight = block.height;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<ng-template ngFor let-tx [ngForOf]="transactions">
|
||||
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<tr class="header-bg">
|
||||
<th>
|
||||
<a [routerLink]="['/explorer/tx/', tx.txid]">{{ tx.txid }}</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div *ngFor="let vin of tx.vin">
|
||||
<ng-template [ngIf]="vin.is_coinbase" [ngIfElse]="regularVin">
|
||||
Coinbase
|
||||
</ng-template>
|
||||
<ng-template #regularVin>
|
||||
<a [routerLink]="['/explorer/address/', vin.prevout.scriptpubkey_address]">{{ vin.prevout.scriptpubkey_address }}</a>
|
||||
(<a [routerLink]="['/explorer/tx/', vin.txid]">tx</a>)
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div *ngFor="let vin of tx.vin">
|
||||
<ng-template [ngIf]="vin.prevout">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVin">
|
||||
<span class="green-color">{{ conversions.USD * (vin.prevout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatVin>
|
||||
{{ vin.prevout.value / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
<table class="table table-borderless smaller-text">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div *ngFor="let vout of tx.vout">
|
||||
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
|
||||
<ng-template #scriptpubkey_type>
|
||||
{{ vout.scriptpubkey_type | uppercase }}
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div *ngFor="let vout of tx.vout">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
|
||||
<span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatVout>
|
||||
{{ vout.value / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-right" colspan="4">
|
||||
<ng-template [ngIf]="showConfirmations">
|
||||
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-success">{{ latestBlockHeight - tx.status.block_height + 1 }} confirmations</button>
|
||||
<ng-template #unconfirmedButton>
|
||||
<button type="button" class="btn btn-danger">Unconfirmed</button>
|
||||
</ng-template>
|
||||
|
||||
</ng-template>
|
||||
<button type="button" class="btn btn-primary" (click)="viewFiat = !viewFiat">
|
||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatButton">
|
||||
<span *ngIf="conversions">{{ conversions.USD * (getTotalTxOutput(tx) / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
</ng-template>
|
||||
<ng-template #viewFiatButton>
|
||||
{{ getTotalTxOutput(tx) / 100000000 }} BTC
|
||||
</ng-template>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.header-bg {
|
||||
background-color: #181b2d !important;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { MemPoolService } from 'src/app/services/mem-pool.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transactions-list',
|
||||
templateUrl: './transactions-list.component.html',
|
||||
styleUrls: ['./transactions-list.component.scss']
|
||||
})
|
||||
export class TransactionsListComponent implements OnInit {
|
||||
@Input() transactions: any[];
|
||||
@Input() showConfirmations = false;
|
||||
latestBlockHeight: number;
|
||||
|
||||
viewFiat = false;
|
||||
conversions: any;
|
||||
|
||||
constructor(
|
||||
private memPoolService: MemPoolService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.memPoolService.conversions$
|
||||
.subscribe((conversions) => {
|
||||
this.conversions = conversions;
|
||||
});
|
||||
|
||||
this.memPoolService.blocks$
|
||||
.subscribe((block) => {
|
||||
this.latestBlockHeight = block.height;
|
||||
});
|
||||
}
|
||||
|
||||
getTotalTxOutput(tx: any) {
|
||||
return tx.vout.map((v: any) => v.value || 0).reduce((a: number, b: number) => a + b);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user