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:
softsimon
2021-08-13 17:16:14 +03:00
56 changed files with 1307 additions and 466 deletions

View File

@@ -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

View File

@@ -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>
&nbsp;
<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>
&nbsp;
<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">

View File

@@ -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";

View File

@@ -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){

View File

@@ -91,5 +91,7 @@ pre {
padding: 30px;
code{
background-color: transparent;
white-space: break-spaces;
word-break: break-all;
}
}

View File

@@ -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>

View File

@@ -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">&nbsp;</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>

View File

@@ -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++) {

View File

@@ -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>

View File

@@ -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">&nbsp;</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">&lrm;</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">&nbsp;</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">&lrm;</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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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'">

View File

@@ -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() {