Address page with QR code
This commit is contained in:
parent
309e851ead
commit
480016ef07
@ -13,4 +13,7 @@ export interface AbstractBitcoinApi {
|
|||||||
getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock>;
|
getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock>;
|
||||||
getBlocks(): Promise<string>;
|
getBlocks(): Promise<string>;
|
||||||
getBlocksFromHeight(height: number): Promise<string>;
|
getBlocksFromHeight(height: number): Promise<string>;
|
||||||
|
getAddress(address: string): Promise<IBlock>;
|
||||||
|
getAddressTransactions(address: string): Promise<IBlock>;
|
||||||
|
getAddressTransactionsFromLastSeenTxid(address: string, lastSeenTxid: string): Promise<IBlock>;
|
||||||
}
|
}
|
||||||
|
@ -84,22 +84,27 @@ class BitcoindApi implements AbstractBitcoinApi {
|
|||||||
getBlock(hash: string): Promise<IBlock> {
|
getBlock(hash: string): Promise<IBlock> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlocks(): Promise<string> {
|
getBlocks(): Promise<string> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlocksFromHeight(height: number): Promise<string> {
|
getBlocksFromHeight(height: number): Promise<string> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlockTransactions(hash: string): Promise<IBlock> {
|
getBlockTransactions(hash: string): Promise<IBlock> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock> {
|
getBlockTransactionsFromIndex(hash: string, index: number): Promise<IBlock> {
|
||||||
throw new Error('Method not implemented.');
|
throw new Error('Method not implemented.');
|
||||||
}
|
}
|
||||||
|
getAddress(address: string): Promise<IBlock> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
getAddressTransactions(address: string): Promise<IBlock> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
getAddressTransactionsFromLastSeenTxid(address: string, lastSeenTxid: string): Promise<IBlock> {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BitcoindApi;
|
export default BitcoindApi;
|
||||||
|
@ -149,6 +149,39 @@ class EsploraApi implements AbstractBitcoinApi {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAddress(address: string): Promise<IBlock> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const blockInfo: AxiosResponse = await this.client.get('/address/' + address);
|
||||||
|
resolve(blockInfo.data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddressTransactions(address: string): Promise<IBlock> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const blockInfo: AxiosResponse = await this.client.get('/address/' + address + '/txs');
|
||||||
|
resolve(blockInfo.data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddressTransactionsFromLastSeenTxid(address: string, lastSeenTxid: string): Promise<IBlock> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const blockInfo: AxiosResponse = await this.client.get('/address/' + address + '/txs/chain/' + lastSeenTxid);
|
||||||
|
resolve(blockInfo.data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EsploraApi;
|
export default EsploraApi;
|
||||||
|
@ -272,6 +272,9 @@ class MempoolSpace {
|
|||||||
.get(config.API_ENDPOINT + 'explorer/block/:hash', routes.getBlock)
|
.get(config.API_ENDPOINT + 'explorer/block/:hash', routes.getBlock)
|
||||||
.get(config.API_ENDPOINT + 'explorer/block/:hash/tx', routes.getBlockTransactions)
|
.get(config.API_ENDPOINT + 'explorer/block/:hash/tx', routes.getBlockTransactions)
|
||||||
.get(config.API_ENDPOINT + 'explorer/block/:hash/tx/:index', routes.getBlockTransactionsFromIndex)
|
.get(config.API_ENDPOINT + 'explorer/block/:hash/tx/:index', routes.getBlockTransactionsFromIndex)
|
||||||
|
.get(config.API_ENDPOINT + 'explorer/address/:address', routes.getAddress)
|
||||||
|
.get(config.API_ENDPOINT + 'explorer/address/:address/tx', routes.getAddressTransactions)
|
||||||
|
.get(config.API_ENDPOINT + 'explorer/address/:address/tx/chain/:txid', routes.getAddressTransactionsFromTxid)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,33 @@ class Routes {
|
|||||||
res.status(500).send(e.message);
|
res.status(500).send(e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getAddress(req, res) {
|
||||||
|
try {
|
||||||
|
const result = await bitcoinApi.getAddress(req.params.address);
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAddressTransactions(req, res) {
|
||||||
|
try {
|
||||||
|
const result = await bitcoinApi.getAddressTransactions(req.params.address);
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAddressTransactionsFromTxid(req, res) {
|
||||||
|
try {
|
||||||
|
const result = await bitcoinApi.getAddressTransactionsFromLastSeenTxid(req.params.address, req.params.txid);
|
||||||
|
res.send(result);
|
||||||
|
} catch (e) {
|
||||||
|
res.status(500).send(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Routes();
|
export default new Routes();
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "^8.2.11",
|
"@angular/platform-browser-dynamic": "^8.2.11",
|
||||||
"@angular/router": "^8.2.11",
|
"@angular/router": "^8.2.11",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^5.1.1",
|
"@ng-bootstrap/ng-bootstrap": "^5.1.1",
|
||||||
|
"angularx-qrcode": "^1.7.0-beta.5",
|
||||||
"bootstrap": "^4.3.1",
|
"bootstrap": "^4.3.1",
|
||||||
"chartist": "^0.11.2",
|
"chartist": "^0.11.2",
|
||||||
"core-js": "^3.3.3",
|
"core-js": "^3.3.3",
|
||||||
|
@ -1 +1,81 @@
|
|||||||
<p>address works!</p>
|
<div class="container">
|
||||||
|
<h1>Address</h1>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="!isLoadingAddress && !error">
|
||||||
|
|
||||||
|
<table class="table table-borderless">
|
||||||
|
<thead>
|
||||||
|
<tr class="header-bg">
|
||||||
|
<th>
|
||||||
|
<a [routerLink]="['/explorer/address/', address.address]">{{ address.address }}</a>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Number of transactions</td>
|
||||||
|
<td>{{ address.chain_stats.tx_count + address.mempool_stats.tx_count }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Total received</td>
|
||||||
|
<td>{{ (address.chain_stats.funded_txo_sum + address.mempool_stats.funded_txo_sum) / 100000000 | number: '1.2-2' }} BTC</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Total sent</td>
|
||||||
|
<td>{{ (address.chain_stats.spent_txo_sum + address.mempool_stats.spent_txo_sum) / 100000000 | number: '1.2-2' }} BTC</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="col text-center">
|
||||||
|
<div class="qr-wrapper">
|
||||||
|
<qrcode id="qrCode" [qrdata]="address.address" [size]="128" [level]="'M'"></qrcode>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<h2>{{ transactions?.length || '?' }} of {{ address.chain_stats.tx_count + address.mempool_stats.tx_count }} transactions</h2>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<app-transactions-list [transactions]="transactions" [showConfirmations]="true"></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 && transactions?.length !== (address.chain_stats.tx_count + address.mempool_stats.tx_count)" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="isLoadingAddress && !error">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border text-light"></div>
|
||||||
|
<br><br>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="error">
|
||||||
|
<div class="text-center">
|
||||||
|
Error loading address data.
|
||||||
|
<br>
|
||||||
|
<i>{{ error.message }}</i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
@ -0,0 +1,14 @@
|
|||||||
|
.header-bg {
|
||||||
|
background-color:#653b9c;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.header-bg a {
|
||||||
|
color: #FFF;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-wrapper {
|
||||||
|
background-color: #FFF;
|
||||||
|
padding: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
@ -1,4 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||||
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
import { ApiService } from 'src/app/services/api.service';
|
||||||
|
import { switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-address',
|
selector: 'app-address',
|
||||||
@ -6,10 +9,54 @@ import { Component, OnInit } from '@angular/core';
|
|||||||
styleUrls: ['./address.component.scss']
|
styleUrls: ['./address.component.scss']
|
||||||
})
|
})
|
||||||
export class AddressComponent implements OnInit {
|
export class AddressComponent implements OnInit {
|
||||||
|
address: any;
|
||||||
|
isLoadingAddress = true;
|
||||||
|
latestBlockHeight: number;
|
||||||
|
transactions: any[];
|
||||||
|
isLoadingTransactions = true;
|
||||||
|
error: any;
|
||||||
|
|
||||||
constructor() { }
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private ref: ChangeDetectorRef,
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.route.paramMap.pipe(
|
||||||
|
switchMap((params: ParamMap) => {
|
||||||
|
this.error = undefined;
|
||||||
|
this.isLoadingAddress = true;
|
||||||
|
const address: string = params.get('id') || '';
|
||||||
|
return this.apiService.getAddress$(address);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((address) => {
|
||||||
|
this.address = address;
|
||||||
|
this.isLoadingAddress = false;
|
||||||
|
this.getAddressTransactions(address.address);
|
||||||
|
this.ref.markForCheck();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.error = error;
|
||||||
|
this.isLoadingAddress = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAddressTransactions(address: string) {
|
||||||
|
this.apiService.getAddressTransactions$(address)
|
||||||
|
.subscribe((transactions: any) => {
|
||||||
|
this.transactions = transactions;
|
||||||
|
this.isLoadingTransactions = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore() {
|
||||||
|
this.isLoadingTransactions = true;
|
||||||
|
this.apiService.getAddressTransactionsFromHash$(this.address.id, this.transactions[this.transactions.length - 1].txid)
|
||||||
|
.subscribe((transactions) => {
|
||||||
|
this.transactions = this.transactions.concat(transactions);
|
||||||
|
this.isLoadingTransactions = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Block <ng-template [ngIf]="!isLoadingBlock"><a [routerLink]="['/explorer/block/', block.id]">#{{ block.height }}</a></ng-template></h1>
|
<h1>Block <ng-template [ngIf]="!isLoadingBlock"><a [routerLink]="['/explorer/block/', block.id]">#{{ block.height }}</a></ng-template></h1>
|
||||||
|
|
||||||
<ng-template [ngIf]="!isLoadingBlock" [ngIfElse]="loadingBlock">
|
<ng-template [ngIf]="!isLoadingBlock && !error">
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Status</td>
|
<td>Status</td>
|
||||||
<td>{{ latestBlockHeight - block.height + 1 }} confirmations</td>
|
<td>{{ (latestBlockHeight - block.height + 1) }} confirmation{{ (latestBlockHeight - block.height + 1) === 1 ? '' : 's' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -61,17 +61,26 @@
|
|||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
<br><br>
|
<br><br>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<button *ngIf="transactions?.length" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
|
<button *ngIf="transactions?.length && transactions?.length !== block.tx_count" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #loadingBlock>
|
|
||||||
|
<ng-template [ngIf]="isLoadingBlock && !error">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
<br><br>
|
<br><br>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="error">
|
||||||
|
<div class="text-center">
|
||||||
|
Error loading block data.
|
||||||
|
<br>
|
||||||
|
<i>{{ error.message }}</i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
@ -16,6 +16,7 @@ export class BlockComponent implements OnInit {
|
|||||||
latestBlockHeight: number;
|
latestBlockHeight: number;
|
||||||
transactions: any[];
|
transactions: any[];
|
||||||
isLoadingTransactions = true;
|
isLoadingTransactions = true;
|
||||||
|
error: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -27,16 +28,21 @@ export class BlockComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.paramMap.pipe(
|
this.route.paramMap.pipe(
|
||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
|
this.error = undefined;
|
||||||
this.isLoadingBlock = true;
|
this.isLoadingBlock = true;
|
||||||
const blockHash: string = params.get('id') || '';
|
const blockHash: string = params.get('id') || '';
|
||||||
this.getBlockTransactions(blockHash);
|
|
||||||
return this.apiService.getBlock$(blockHash);
|
return this.apiService.getBlock$(blockHash);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe((block) => {
|
.subscribe((block) => {
|
||||||
this.block = block;
|
this.block = block;
|
||||||
this.isLoadingBlock = false;
|
this.isLoadingBlock = false;
|
||||||
|
this.getBlockTransactions(block.id);
|
||||||
this.ref.markForCheck();
|
this.ref.markForCheck();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.error = error;
|
||||||
|
this.isLoadingBlock = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.memPoolService.blocks$
|
this.memPoolService.blocks$
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { QRCodeModule } from 'angularx-qrcode';
|
||||||
import { ExplorerComponent } from './explorer/explorer.component';
|
import { ExplorerComponent } from './explorer/explorer.component';
|
||||||
import { TransactionComponent } from './transaction/transaction.component';
|
import { TransactionComponent } from './transaction/transaction.component';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
@ -33,6 +34,7 @@ const routes: Routes = [
|
|||||||
SharedModule,
|
SharedModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
|
QRCodeModule,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ExplorerModule { }
|
export class ExplorerModule { }
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<ng-template [ngIf]="!isLoadingTx" [ngIfElse]="loadingTx">
|
<ng-template [ngIf]="!isLoadingTx && !error">
|
||||||
|
|
||||||
<app-transactions-list [transactions]="[tx]" [showConfirmations]="true"></app-transactions-list>
|
<app-transactions-list [transactions]="[tx]" [showConfirmations]="true"></app-transactions-list>
|
||||||
|
|
||||||
@ -49,11 +49,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #loadingTx>
|
|
||||||
|
<ng-template [ngIf="isLoadingTx && !error">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
<br><br>
|
<br><br>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template [ngIf]="error">
|
||||||
|
<div class="text-center">
|
||||||
|
Error loading transaction data.
|
||||||
|
<br>
|
||||||
|
<i>{{ error.message }}</i>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
@ -13,6 +13,7 @@ export class TransactionComponent implements OnInit {
|
|||||||
tx: any;
|
tx: any;
|
||||||
isLoadingTx = true;
|
isLoadingTx = true;
|
||||||
conversions: any;
|
conversions: any;
|
||||||
|
error: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -23,6 +24,7 @@ export class TransactionComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.paramMap.pipe(
|
this.route.paramMap.pipe(
|
||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
|
this.error = undefined;
|
||||||
const txId: string = params.get('id') || '';
|
const txId: string = params.get('id') || '';
|
||||||
return this.apiService.getTransaction$(txId);
|
return this.apiService.getTransaction$(txId);
|
||||||
})
|
})
|
||||||
@ -30,6 +32,10 @@ export class TransactionComponent implements OnInit {
|
|||||||
.subscribe((tx) => {
|
.subscribe((tx) => {
|
||||||
this.tx = tx;
|
this.tx = tx;
|
||||||
this.isLoadingTx = false;
|
this.isLoadingTx = false;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
this.error = error;
|
||||||
|
this.isLoadingTx = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.memPoolService.conversions$
|
this.memPoolService.conversions$
|
||||||
|
@ -1,93 +1,93 @@
|
|||||||
<ng-template ngFor let-tx [ngForOf]="transactions">
|
<ng-template ngFor let-tx [ngForOf]="transactions">
|
||||||
|
|
||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="header-bg">
|
<tr class="header-bg">
|
||||||
<th>
|
<th>
|
||||||
<a [routerLink]="['/explorer/tx/', tx.txid]">{{ tx.txid }}</a>
|
<a [routerLink]="['/explorer/tx/', tx.txid]">{{ tx.txid }}</a>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<table class="table table-borderless smaller-text">
|
<table class="table table-borderless smaller-text">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<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>
|
<td>
|
||||||
<div *ngFor="let vout of tx.vout">
|
<div *ngFor="let vin of tx.vin">
|
||||||
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
|
<ng-template [ngIf]="vin.is_coinbase" [ngIfElse]="regularVin">
|
||||||
<ng-template #scriptpubkey_type>
|
Coinbase
|
||||||
{{ vout.scriptpubkey_type | uppercase }}
|
</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>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<div *ngFor="let vout of tx.vout">
|
<div *ngFor="let vin of tx.vin">
|
||||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
|
<ng-template [ngIf]="vin.prevout">
|
||||||
<span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVin">
|
||||||
</ng-template>
|
<span class="green-color">{{ conversions.USD * (vin.prevout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||||
<ng-template #viewFiatVout>
|
</ng-template>
|
||||||
{{ vout.value / 100000000 }} BTC
|
<ng-template #viewFiatVin>
|
||||||
|
{{ vin.prevout.value / 100000000 }} BTC
|
||||||
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<table class="table table-borderless smaller-text">
|
||||||
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-right" colspan="4">
|
<td>
|
||||||
<ng-template [ngIf]="showConfirmations">
|
<div *ngFor="let vout of tx.vout">
|
||||||
<button *ngIf="tx.status.confirmed; else unconfirmedButton" type="button" class="btn btn-success">{{ latestBlockHeight - tx.status.block_height + 1 }} confirmations</button>
|
<a *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/explorer/address/', vout.scriptpubkey_address]">{{ vout.scriptpubkey_address }}</a>
|
||||||
<ng-template #unconfirmedButton>
|
<ng-template #scriptpubkey_type>
|
||||||
<button type="button" class="btn btn-danger">Unconfirmed</button>
|
{{ vout.scriptpubkey_type | uppercase }}
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<button type="button" class="btn btn-primary" (click)="viewFiat = !viewFiat">
|
</div>
|
||||||
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatButton">
|
</td>
|
||||||
<span *ngIf="conversions">{{ conversions.USD * (getTotalTxOutput(tx) / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
<td class="text-right">
|
||||||
</ng-template>
|
<div *ngFor="let vout of tx.vout">
|
||||||
<ng-template #viewFiatButton>
|
<ng-template [ngIf]="viewFiat" [ngIfElse]="viewFiatVout">
|
||||||
{{ getTotalTxOutput(tx) / 100000000 }} BTC
|
<span class="green-color">{{ conversions.USD * (vout.value / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</button>
|
<ng-template #viewFiatVout>
|
||||||
</td>
|
{{ vout.value / 100000000 }} BTC
|
||||||
</tr>
|
</ng-template>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
</td>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
.header-bg {
|
.header-bg {
|
||||||
background-color: #181b2d !important;
|
background-color:#653b9c;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-bg a {
|
||||||
|
color: #FFF;
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
@ -170,11 +170,23 @@ export class ApiService {
|
|||||||
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/tx/' + txId);
|
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/tx/' + txId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlock$(hash: string): Observable<any[]> {
|
getBlock$(hash: string): Observable<any> {
|
||||||
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/block/' + hash);
|
return this.httpClient.get<any>(API_BASE_URL + '/explorer/block/' + hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlockTransactions$(hash: string, index?: number): Observable<any[]> {
|
getBlockTransactions$(hash: string, index?: number): Observable<any[]> {
|
||||||
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/block/' + hash + '/tx/' + (index || ''));
|
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/block/' + hash + '/tx/' + (index || ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAddress$(address: string): Observable<any> {
|
||||||
|
return this.httpClient.get<any>(API_BASE_URL + '/explorer/address/' + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddressTransactions$(address: string): Observable<any[]> {
|
||||||
|
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/address/' + address+ '/tx/');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddressTransactionsFromHash$(address: string, txid: string): Observable<any[]> {
|
||||||
|
return this.httpClient.get<any[]>(API_BASE_URL + '/explorer/address/' + address + '/tx/chain/' + txid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7884
frontend/yarn.lock
Normal file
7884
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user