Merge branch 'master' into simon/configurable-main-module
# Conflicts: # frontend/src/app/components/api-docs/api-docs.component.ts # frontend/src/app/components/blockchain-blocks/blockchain-blocks.component.ts # frontend/src/app/components/master-page/master-page.component.html # frontend/src/app/components/mempool-blocks/mempool-blocks.component.html # frontend/src/app/components/mempool-blocks/mempool-blocks.component.ts # frontend/src/app/dashboard/dashboard.component.html
This commit is contained in:
@@ -43,7 +43,7 @@ export const languages: Language[] = [
|
||||
{ code: 'ar', name: 'العربية' }, // Arabic
|
||||
// { code: 'bg', name: 'Български' }, // Bulgarian
|
||||
// { code: 'bs', name: 'Bosanski' }, // Bosnian
|
||||
// { code: 'ca', name: 'Català' }, // Catalan
|
||||
{ code: 'ca', name: 'Català' }, // Catalan
|
||||
{ code: 'cs', name: 'Čeština' }, // Czech
|
||||
// { code: 'da', name: 'Dansk' }, // Danish
|
||||
{ code: 'de', name: 'Deutsch' }, // German
|
||||
|
||||
@@ -1,98 +1,102 @@
|
||||
<div class="container-xl">
|
||||
|
||||
<div class="title-block">
|
||||
<ng-template [ngIf]="!isLoading && !error">
|
||||
|
||||
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right">
|
||||
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
|
||||
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
|
||||
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]">
|
||||
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="bisqTx.id"></app-clipboard>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="box transaction-container">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
||||
<td>
|
||||
{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="lg-inline">
|
||||
<i class="symbol">(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.included-in-block|Transaction included in block">Included in block</td>
|
||||
<td>
|
||||
<a [routerLink]="['/block/' | relativeUrl, bisqTx.blockHash]" [state]="{ data: { blockHeight: bisqTx.blockHeight } }">{{ bisqTx.blockHeight }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
|
||||
<td>
|
||||
<app-tx-features *ngIf="tx; else loadingTx" [tx]="tx"></app-tx-features>
|
||||
<ng-template #loadingTx>
|
||||
<span class="skeleton-loader"></span>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width" i18n="BSQ burnt amount">Burnt amount</td>
|
||||
<td>
|
||||
{{ bisqTx.burntFee / 100 | number: '1.2-2' }} <span class="symbol">BSQ</span> <span class="fiat"><app-bsq-amount [bsq]="bisqTx.burntFee" [forceFiat]="true" [green]="true"></app-bsq-amount></span>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td *ngIf="!isLoadingTx; else loadingTxFee">
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span class="symbol">sat/vB</span>
|
||||
|
||||
<app-tx-fee-rating [tx]="tx"></app-tx-fee-rating>
|
||||
</td>
|
||||
<ng-template #loadingTxFee>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</ng-template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="title">
|
||||
<h1 i18n="shared.transaction">Transaction</h1>
|
||||
</div>
|
||||
|
||||
<div class="tx-link">
|
||||
<a [routerLink]="['/bisq-tx' | relativeUrl, bisqTx.id]">
|
||||
<span class="d-inline d-lg-none">{{ bisqTx.id | shortenString : 24 }}</span>
|
||||
<span class="d-none d-lg-inline">{{ bisqTx.id }}</span>
|
||||
</a>
|
||||
<app-clipboard [text]="bisqTx.id"></app-clipboard>
|
||||
</div>
|
||||
<div class="container-buttons">
|
||||
<button *ngIf="(latestBlock$ | async) as latestBlock" type="button" class="btn btn-sm btn-success float-right">
|
||||
<ng-container *ngTemplateOutlet="latestBlock.height - bisqTx.blockHeight + 1 == 1 ? confirmationSingular : confirmationPlural; context: {$implicit: latestBlock.height - bisqTx.blockHeight + 1}"></ng-container>
|
||||
<ng-template #confirmationSingular let-i i18n="shared.confirmation-count.singular|Transaction singular confirmation count">{{ i }} confirmation</ng-template>
|
||||
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="box transaction-container">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n="transaction.timestamp|Transaction Timestamp">Timestamp</td>
|
||||
<td>
|
||||
{{ bisqTx.time | date:'yyyy-MM-dd HH:mm' }}
|
||||
<div class="lg-inline">
|
||||
<i class="symbol">(<app-time-since [time]="bisqTx.time / 1000" [fastRender]="true"></app-time-since>)</i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.included-in-block|Transaction included in block">Included in block</td>
|
||||
<td>
|
||||
<a [routerLink]="['/block/' | relativeUrl, bisqTx.blockHash]" [state]="{ data: { blockHeight: bisqTx.blockHeight } }">{{ bisqTx.blockHeight }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="td-width" i18n="transaction.features|Transaction features">Features</td>
|
||||
<td>
|
||||
<app-tx-features *ngIf="tx; else loadingTx" [tx]="tx"></app-tx-features>
|
||||
<ng-template #loadingTx>
|
||||
<span class="skeleton-loader"></span>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<table class="table table-borderless table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="td-width" i18n="BSQ burnt amount">Burnt amount</td>
|
||||
<td>
|
||||
{{ bisqTx.burntFee / 100 | number: '1.2-2' }} <span class="symbol">BSQ</span> <span class="fiat"><app-bsq-amount [bsq]="bisqTx.burntFee" [forceFiat]="true" [green]="true"></app-bsq-amount></span>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.fee-per-vbyte|Transaction fee">Fee per vByte</td>
|
||||
<td *ngIf="!isLoadingTx; else loadingTxFee">
|
||||
{{ tx.fee / (tx.weight / 4) | number : '1.1-1' }} <span class="symbol">sat/vB</span>
|
||||
|
||||
<app-tx-fee-rating [tx]="tx"></app-tx-fee-rating>
|
||||
</td>
|
||||
<ng-template #loadingTxFee>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</ng-template>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
|
||||
|
||||
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
|
||||
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
||||
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.details">Details</h2>
|
||||
|
||||
|
||||
<app-bisq-transaction-details [tx]="bisqTx"></app-bisq-transaction-details>
|
||||
|
||||
<br>
|
||||
|
||||
<h2 i18n="transaction.inputs-and-outputs|Transaction inputs and outputs">Inputs & Outputs</h2>
|
||||
|
||||
<app-bisq-transfers [tx]="bisqTx"></app-bisq-transfers>
|
||||
|
||||
<br>
|
||||
|
||||
</ng-template>
|
||||
|
||||
<ng-template [ngIf="isLoading && !error">
|
||||
|
||||
@@ -1,126 +1 @@
|
||||
.adjust-btn-padding {
|
||||
padding: 0.55rem;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.mobile-bottomcol {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.td-width {
|
||||
width: 150px;
|
||||
@media (min-width: 768px) {
|
||||
width: 175px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.btn-small-height {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.arrow-green {
|
||||
color: #1a9436;
|
||||
}
|
||||
|
||||
.arrow-red {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
float: right;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.tx-link {
|
||||
display: block;
|
||||
width: auto;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 2px;
|
||||
margin-top: 40px;
|
||||
position: absolute;
|
||||
@media (min-width: 700px) {
|
||||
margin-top: 14px;
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
top: 14px;
|
||||
left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.container-xl {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.row{
|
||||
flex-direction: column;
|
||||
@media (min-width: 768px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.mobile-bottomcol {
|
||||
margin-top: 15px;
|
||||
}
|
||||
.details-table td:first-child {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.fiat {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
@media (min-width: 576px){
|
||||
display: inline-block;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 1.75rem;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0;
|
||||
float: left;
|
||||
padding-bottom: 40px;
|
||||
@media (min-width: 375px){
|
||||
margin-top: 0px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
@media (min-width: 768px){
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
.transaction-container {
|
||||
font-size: 14px;
|
||||
@media (min-width: 576px){
|
||||
font-size: 16px;
|
||||
}
|
||||
tr td {
|
||||
&:last-child{
|
||||
text-align: right;
|
||||
@media (min-width: 768px){
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@import "./../../components/transaction/transaction.component.scss";
|
||||
@@ -15,12 +15,6 @@ label {
|
||||
}
|
||||
}
|
||||
|
||||
h1{
|
||||
font-size: 1.5rem;
|
||||
@media(min-width: 375px){
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
.container-xl {
|
||||
padding-bottom: 60px;
|
||||
@media(min-width: 400px){
|
||||
|
||||
@@ -91,5 +91,7 @@ pre {
|
||||
padding: 30px;
|
||||
code{
|
||||
background-color: transparent;
|
||||
white-space: break-spaces;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="block.header">Block Header Hex</td>
|
||||
<td><a target="_blank" href="/api/block/{{block.id}}/header"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
<td><a target="_blank" href="{{ network === '' ? '' : '/' + network }}/api/block/{{block.id}}/header"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="blocks-container" *ngIf="loadingBlocks">
|
||||
<div class="blocks-container" *ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]">
|
||||
<a [routerLink]="['/block/' | relativeUrl, block.id]" [state]="{ data: { block: block } }" class="blockLink"> </a>
|
||||
@@ -25,11 +25,13 @@
|
||||
<div [hidden]="!arrowVisible" id="arrow-up" [style.transition]="transition" [ngStyle]="{'left': arrowLeftPx + 'px' }"></div>
|
||||
</div>
|
||||
|
||||
<div class="blocks-container" *ngIf="!loadingBlocks">
|
||||
<div class="flashing">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]"></div>
|
||||
<ng-template #loadingBlocksTemplate >
|
||||
<div class="blocks-container">
|
||||
<div class="flashing">
|
||||
<div *ngFor="let block of emptyBlocks; let i = index; trackBy: trackByBlocksFn" >
|
||||
<div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}" [ngStyle]="emptyBlockStyles[i]"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { Block } from 'src/app/interfaces/electrs.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Router } from '@angular/router';
|
||||
@@ -13,18 +13,19 @@ import { Router } from '@angular/router';
|
||||
export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
network = '';
|
||||
blocks: Block[] = this.mountEmptyBlocks();
|
||||
blocks: Block[] = [];
|
||||
emptyBlocks: Block[] = this.mountEmptyBlocks();
|
||||
markHeight: number;
|
||||
blocksSubscription: Subscription;
|
||||
networkSubscriotion: Subscription;
|
||||
networkSubscription: Subscription;
|
||||
tabHiddenSubscription: Subscription;
|
||||
markBlockSubscription: Subscription;
|
||||
loadingBlocks$: Observable<boolean>;
|
||||
blockStyles = [];
|
||||
emptyBlockStyles = [];
|
||||
interval: any;
|
||||
tabHidden = false;
|
||||
loadingBlocks = false;
|
||||
feeRounding = '1.0-0';
|
||||
|
||||
arrowVisible = false;
|
||||
arrowLeftPx = 30;
|
||||
blocksFilled = false;
|
||||
@@ -48,18 +49,16 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.blocks.forEach((b) => this.blockStyles.push(this.getStyleForBlock(b)));
|
||||
this.networkSubscriotion = this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
this.emptyBlocks.forEach((b) => this.emptyBlockStyles.push(this.getStyleForEmptyBlock(b)));
|
||||
this.loadingBlocks$ = this.stateService.isLoadingWebSocket$;
|
||||
this.networkSubscription = this.stateService.networkChanged$.subscribe((network) => this.network = network);
|
||||
this.tabHiddenSubscription = this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
|
||||
|
||||
this.blocksSubscription = this.stateService.blocks$
|
||||
.subscribe(([block, txConfirmed]) => {
|
||||
if (this.blocks.some((b) => b.height === block.height)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadingBlocks = true;
|
||||
|
||||
if (this.blocks.length && block.height !== this.blocks[0].height + 1) {
|
||||
this.blocks = [];
|
||||
this.blocksFilled = false;
|
||||
@@ -128,7 +127,7 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
ngOnDestroy() {
|
||||
this.blocksSubscription.unsubscribe();
|
||||
this.networkSubscriotion.unsubscribe();
|
||||
this.networkSubscription.unsubscribe();
|
||||
this.tabHiddenSubscription.unsubscribe();
|
||||
this.markBlockSubscription.unsubscribe();
|
||||
clearInterval(this.interval);
|
||||
@@ -187,6 +186,21 @@ export class BlockchainBlocksComponent implements OnInit, OnDestroy {
|
||||
)`,
|
||||
};
|
||||
}
|
||||
|
||||
getStyleForEmptyBlock(block: Block) {
|
||||
let addLeft = 0;
|
||||
|
||||
if (block.stage === 1) {
|
||||
block.stage = 2;
|
||||
addLeft = -205;
|
||||
}
|
||||
|
||||
return {
|
||||
left: addLeft + 155 * this.emptyBlocks.indexOf(block) + 'px',
|
||||
background: "#2d3348",
|
||||
};
|
||||
}
|
||||
|
||||
mountEmptyBlocks() {
|
||||
const emptyBlocks = [];
|
||||
for (let i = 0; i < this.stateService.env.KEEP_BLOCKS_AMOUNT; i++) {
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
<button ngbDropdownItem *ngIf="env.SIGNET_ENABLED" class="signet" [class.active]="network.val === 'signet'" routerLink="/signet"><img src="./resources/signet-logo.png" style="width: 30px;" class="mr-1"> Signet</button>
|
||||
<button ngbDropdownItem *ngIf="env.TESTNET_ENABLED" class="testnet" [class.active]="network.val === 'testnet'" routerLink="/testnet"><img src="./resources/testnet-logo.png" style="width: 30px;" class="mr-1"> Testnet</button>
|
||||
<h6 *ngIf="env.LIQUID_ENABLED || env.BISQ_ENABLED" class="dropdown-header" i18n="master-page.layer2-networks-header">Layer 2 Networks</h6>
|
||||
<a href="https://bisq.markets" ngbDropdownItem *ngIf="env.BISQ_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="mainnet"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="mainnet" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<a href="https://bisq.markets" ngbDropdownItem *ngIf="env.BISQ_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</a>
|
||||
<button ngbDropdownItem *ngIf="env.BISQ_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="bisq" [class.active]="network.val === 'bisq'" routerLink="/bisq"><img src="./resources/bisq-logo.png" style="width: 30px;" class="mr-1"> Bisq</button>
|
||||
<a href="https://liquid.network" ngbDropdownItem *ngIf="env.LIQUID_ENABLED && env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</a>
|
||||
<button ngbDropdownItem *ngIf="env.LIQUID_ENABLED && !env.OFFICIAL_MEMPOOL_SPACE" class="liquid" [class.active]="network.val === 'liquid'" routerLink="/liquid"><img src="./resources/liquid-logo.png" style="width: 30px;" class="mr-1"> Liquid</button>
|
||||
</div>
|
||||
|
||||
@@ -1,47 +1,51 @@
|
||||
<div class="mempool-blocks-container">
|
||||
<div class="flashing" *ngIf="(timeAvg$ | async) as timeAvg;">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="fee-span">
|
||||
{{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="block-size" [innerHTML]="projectedBlock.blockSize | bytes: 2">‎</div>
|
||||
<div class="transaction-count">
|
||||
<ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
|
||||
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||
</div>
|
||||
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= stateService.blockVSize; else mergedBlock">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
|
||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
<app-time-until [time]="(timeAvg * i) + now + timeAvg" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #mergedBlock>
|
||||
<div class="time-difference">
|
||||
<b>(<ng-container *ngTemplateOutlet="blocksPlural; context: {$implicit: projectedBlock.blockVSize / stateService.blockVSize | ceil }"></ng-container>)</b>
|
||||
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
|
||||
<ng-container *ngIf="(loadingBlocks$ | async) === false; else loadingBlocks">
|
||||
<div class="mempool-blocks-container" *ngIf="(timeAvg$ | async) as timeAvg;">
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks$ | async" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]">
|
||||
<a [routerLink]="['/mempool-block/' | relativeUrl, i]" class="blockLink"> </a>
|
||||
<div class="block-body">
|
||||
<div class="fees">
|
||||
~{{ projectedBlock.medianFee | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div class="fee-span">
|
||||
{{ projectedBlock.feeRange[0] | number:feeRounding }} - {{ projectedBlock.feeRange[projectedBlock.feeRange.length - 1] | number:feeRounding }} <span i18n="shared.sat-vbyte|sat/vB">sat/vB</span>
|
||||
</div>
|
||||
<div class="block-size" [innerHTML]="projectedBlock.blockSize | bytes: 2">‎</div>
|
||||
<div class="transaction-count">
|
||||
<ng-container *ngTemplateOutlet="projectedBlock.nTx === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: projectedBlock.nTx | number}"></ng-container>
|
||||
<ng-template #transactionsSingular let-i i18n="shared.transaction-count.singular">{{ i }} transaction</ng-template>
|
||||
<ng-template #transactionsPlural let-i i18n="shared.transaction-count.plural">{{ i }} transactions</ng-template>
|
||||
</div>
|
||||
<div class="time-difference" *ngIf="projectedBlock.blockVSize <= stateService.blockVSize; else mergedBlock">
|
||||
<ng-template [ngIf]="network === 'liquid'" [ngIfElse]="timeDiffMainnet">
|
||||
<app-time-until [time]="(1 * i) + now + 61000" [fastRender]="false" [fixedRender]="true"></app-time-until>
|
||||
</ng-template>
|
||||
<ng-template #timeDiffMainnet>
|
||||
<app-time-until [time]="(timeAvg * i) + now + timeAvg" [fastRender]="false" [fixedRender]="true" [forceFloorOnTimeIntervals]="['hour']"></app-time-until>
|
||||
</ng-template>
|
||||
</div>
|
||||
<ng-template #mergedBlock>
|
||||
<div class="time-difference">
|
||||
<b>(<ng-container *ngTemplateOutlet="blocksPlural; context: {$implicit: projectedBlock.blockVSize / stateService.blockVSize | ceil }"></ng-container>)</b>
|
||||
<ng-template #blocksPlural let-i i18n="shared.blocks">{{ i }} <span class="shared-block">blocks</span></ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<span class="animated-border"></span>
|
||||
</div>
|
||||
<span class="animated-border"></span>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
|
||||
</div>
|
||||
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="mempool-blocks-container" *ngIf="loadingMempoolBlocks === true">
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolBlocks" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolBlockStyles[i]"></div>
|
||||
</ng-template>
|
||||
<ng-template #loadingBlocks>
|
||||
<div class="mempool-blocks-container">
|
||||
<div class="flashing">
|
||||
<ng-template ngFor let-projectedBlock [ngForOf]="mempoolEmptyBlocks" let-i="index" [ngForTrackBy]="trackByFn">
|
||||
<div class="bitcoin-block text-center mempool-block" id="mempool-block-{{ i }}" [ngStyle]="mempoolEmptyBlockStyles[i]"></div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Subscription, Observable, fromEvent, merge, of, combineLatest, timer }
|
||||
import { MempoolBlock } from 'src/app/interfaces/websocket.interface';
|
||||
import { StateService } from 'src/app/services/state.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { take, map, switchMap } from 'rxjs/operators';
|
||||
import { take, map, switchMap, share } from 'rxjs/operators';
|
||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
|
||||
@Component({
|
||||
@@ -14,13 +14,17 @@ import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
|
||||
})
|
||||
export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
|
||||
mempoolBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||
mempoolBlocks: MempoolBlock[] = [];
|
||||
mempoolEmptyBlocks: MempoolBlock[] = this.mountEmptyBlocks();
|
||||
mempoolBlocks$: Observable<MempoolBlock[]>;
|
||||
timeAvg$: Observable<number>;
|
||||
loadingBlocks$: Observable<boolean>;
|
||||
|
||||
mempoolBlocksFull: MempoolBlock[] = this.mountEmptyBlocks();
|
||||
mempoolBlocksFull: MempoolBlock[] = [];
|
||||
mempoolBlockStyles = [];
|
||||
mempoolEmptyBlockStyles = [];
|
||||
markBlocksSubscription: Subscription;
|
||||
isLoadingWebsocketSubscription: Subscription;
|
||||
blockSubscription: Subscription;
|
||||
networkSubscription: Subscription;
|
||||
network = '';
|
||||
@@ -30,7 +34,6 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
blockPadding = 30;
|
||||
arrowVisible = false;
|
||||
tabHidden = false;
|
||||
loadingMempoolBlocks = true;
|
||||
feeRounding = '1.0-0';
|
||||
|
||||
rightPosition = 0;
|
||||
@@ -53,12 +56,18 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
if (this.stateService.network === 'liquid') {
|
||||
this.feeRounding = '1.0-1';
|
||||
}
|
||||
this.mempoolEmptyBlocks.forEach((b) => {
|
||||
this.mempoolEmptyBlockStyles.push(this.getStyleForMempoolEmptyBlock(b.index));
|
||||
});
|
||||
this.reduceMempoolBlocksToFitScreen(this.mempoolEmptyBlocks);
|
||||
|
||||
this.mempoolBlocks.map(() => {
|
||||
this.updateMempoolBlockStyles();
|
||||
this.calculateTransactionPosition();
|
||||
});
|
||||
this.reduceMempoolBlocksToFitScreen(this.mempoolBlocks);
|
||||
this.stateService.isTabHidden$.subscribe((tabHidden) => this.tabHidden = tabHidden);
|
||||
this.loadingBlocks$ = this.stateService.isLoadingWebSocket$;
|
||||
|
||||
this.mempoolBlocks$ = merge(
|
||||
of(true),
|
||||
@@ -109,8 +118,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
timeAvgMins += Math.abs(timeAvgDiff);
|
||||
}
|
||||
|
||||
this.loadingMempoolBlocks = false;
|
||||
|
||||
return timeAvgMins * 60 * 1000;
|
||||
})
|
||||
);
|
||||
@@ -234,6 +242,13 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
getStyleForMempoolEmptyBlock(index: number) {
|
||||
return {
|
||||
'right': 40 + index * 155 + 'px',
|
||||
'background': '#554b45',
|
||||
};
|
||||
}
|
||||
|
||||
calculateTransactionPosition() {
|
||||
if ((!this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
|
||||
this.arrowVisible = false;
|
||||
@@ -286,7 +301,7 @@ export class MempoolBlocksComponent implements OnInit, OnDestroy {
|
||||
blockSize: 0,
|
||||
blockVSize: 0,
|
||||
feeRange: [],
|
||||
index: 0,
|
||||
index: i,
|
||||
medianFee: 0,
|
||||
nTx: 0,
|
||||
totalFees: 0
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n="transaction.hex">Transaction Hex</td>
|
||||
<td><a target="_blank" href="/api/tx/{{ txId }}/hex"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
<td><a target="_blank" href="{{ network === '' ? '' : '/' + network }}/api/tx/{{ txId }}/hex"><fa-icon [icon]="['fas', 'external-link-alt']" [fixedWidth]="true"></fa-icon></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -16,15 +16,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h1{
|
||||
font-size: 1.75rem;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0;
|
||||
float: left;
|
||||
@media (min-width: 375px){
|
||||
margin-top: 0px;
|
||||
font-size: 2rem;
|
||||
margin-top: 2px;
|
||||
@media (min-width: 768px){
|
||||
margin-top: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
@@ -24,9 +27,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col" *ngIf="(network$ | async) !== 'liquid'">
|
||||
<ng-container *ngTemplateOutlet="difficultyEpoch"></ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #expanded>
|
||||
<div class="col card-wrapper" *ngIf="(network$ | async) !== 'liquid'">
|
||||
|
||||
@@ -25,6 +25,7 @@ export class WebsocketService {
|
||||
private goneOffline = false;
|
||||
private lastWant: string | null = null;
|
||||
private isTrackingTx = false;
|
||||
private trackingTxId: string;
|
||||
private latestGitCommit = '';
|
||||
private onlineCheckTimeout: number;
|
||||
private onlineCheckTimeoutTwo: number;
|
||||
@@ -97,6 +98,9 @@ export class WebsocketService {
|
||||
if (this.lastWant) {
|
||||
this.want(JSON.parse(this.lastWant), true);
|
||||
}
|
||||
if (this.isTrackingTx) {
|
||||
this.startMultiTrackTransaction(this.trackingTxId);
|
||||
}
|
||||
this.stateService.connectionState$.next(2);
|
||||
}
|
||||
|
||||
@@ -119,11 +123,13 @@ export class WebsocketService {
|
||||
}
|
||||
this.websocketSubject.next({ 'track-tx': txId });
|
||||
this.isTrackingTx = true;
|
||||
this.trackingTxId = txId;
|
||||
}
|
||||
|
||||
startMultiTrackTransaction(txId: string) {
|
||||
this.websocketSubject.next({ 'track-tx': txId, 'watch-mempool': true });
|
||||
this.isTrackingTx = true;
|
||||
this.trackingTxId = txId;
|
||||
}
|
||||
|
||||
stopTrackingTransaction() {
|
||||
|
||||
Reference in New Issue
Block a user