Merge branch 'master' into nymkappa/feature/align-dashboards
This commit is contained in:
@@ -352,7 +352,7 @@
|
||||
|
||||
<div class="copyright">
|
||||
<div class="title">
|
||||
Copyright © 2019-2022<br>
|
||||
Copyright © 2019-2023<br>
|
||||
The Mempool Open Source Project
|
||||
</div>
|
||||
<p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="block-titles">
|
||||
<h1 class="title">
|
||||
<ng-template [ngIf]="blockHeight === 0"><ng-container i18n="@@2303359202781425764">Genesis</ng-container></ng-template>
|
||||
<ng-template [ngIf]="blockHeight" i18n="shared.block-title">{{ blockHeight }}</ng-template>
|
||||
<ng-template [ngIf]="blockHeight">{{ blockHeight }}</ng-template>
|
||||
</h1>
|
||||
<div class="blockhash" *ngIf="blockHash">
|
||||
<h2 class="truncate right">{{ blockHash.slice(0,32) }}</h2>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="blocks-container blockchain-blocks-container" [class.time-ltr]="timeLtr"
|
||||
[style.left]="static ? (offset || 0) + 'px' : null"
|
||||
*ngIf="(loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
*ngIf="static || (loadingBlocks$ | async) === false; else loadingBlocksTemplate">
|
||||
<div *ngFor="let block of blocks; let i = index; trackBy: trackByBlocksFn">
|
||||
<ng-container *ngIf="block && !block.loading && !block.placeholder; else placeholderBlock">
|
||||
<ng-container *ngIf="connected && block && !block.loading && !block.placeholder; else placeholderBlock">
|
||||
<div [attr.data-cy]="'bitcoin-block-offset-' + offset + '-index-' + i"
|
||||
class="text-center bitcoin-block mined-block blockchain-blocks-offset-{{ offset }}-index-{{ i }}"
|
||||
id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]"
|
||||
@@ -43,10 +43,8 @@
|
||||
<div [attr.data-cy]="'bitcoin-block-' + i + '-transactions'" class="transaction-count">
|
||||
<ng-container
|
||||
*ngTemplateOutlet="block.tx_count === 1 ? transactionsSingular : transactionsPlural; context: {$implicit: block.tx_count | 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>
|
||||
<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 [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i + '-time'" class="time-difference">
|
||||
<app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since></div>
|
||||
@@ -59,19 +57,19 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #placeholderBlock>
|
||||
<ng-container *ngIf="block && block.placeholder; else loadingBlock">
|
||||
<ng-container *ngIf="block && block.placeholder && connected && !loadingTip; else loadingBlock">
|
||||
<div [attr.data-cy]="'bitcoin-block-' + offset + '-index-' + i"
|
||||
class="text-center bitcoin-block mined-block placeholder-block blockchain-blocks-{{ i }}"
|
||||
id="bitcoin-block-{{ block.height }}" [ngStyle]="blockStyles[i]">
|
||||
|
||||
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
<ng-template #loadingBlock>
|
||||
<ng-container *ngIf="block && block.loading">
|
||||
<div class="flashing">
|
||||
<ng-container *ngIf="!connected || loadingTip || (block && block.loading)">
|
||||
<div class="flashing loading">
|
||||
<div class="text-center bitcoin-block mined-block" id="bitcoin-block-{{ block.height }}"
|
||||
[ngStyle]="blockStyles[i]"></div>
|
||||
[ngStyle]="convertStyleForLoadingBlock(blockStyles[i])"></div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
@@ -137,6 +137,10 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.loading .bitcoin-block.mined-block {
|
||||
background: #2d3348;
|
||||
}
|
||||
|
||||
@keyframes opacityPulse {
|
||||
0% {opacity: 0.7;}
|
||||
50% {opacity: 1.0;}
|
||||
|
||||
@@ -22,6 +22,8 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@Input() offset: number = 0;
|
||||
@Input() height: number = 0;
|
||||
@Input() count: number = 8;
|
||||
@Input() loadingTip: boolean = false;
|
||||
@Input() connected: boolean = true;
|
||||
|
||||
specialBlocks = specialBlocks;
|
||||
network = '';
|
||||
@@ -288,6 +290,13 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
convertStyleForLoadingBlock(style) {
|
||||
return {
|
||||
...style,
|
||||
background: "#2d3348",
|
||||
};
|
||||
}
|
||||
|
||||
getStyleForLoadingBlock(index: number, animateEnterFrom: number = 0) {
|
||||
const addLeft = animateEnterFrom || 0;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<app-mempool-blocks [hidden]="pageIndex > 0"></app-mempool-blocks>
|
||||
<app-blockchain-blocks [hidden]="pageIndex > 0"></app-blockchain-blocks>
|
||||
<ng-container *ngFor="let page of pages; trackBy: trackByPageFn">
|
||||
<app-blockchain-blocks [static]="true" [offset]="page.offset" [height]="page.height" [count]="blocksPerPage"></app-blockchain-blocks>
|
||||
<app-blockchain-blocks [static]="true" [offset]="page.offset" [height]="page.height" [count]="blocksPerPage" [loadingTip]="loadingTip" [connected]="connected"></app-blockchain-blocks>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div id="divider" [hidden]="pageIndex > 0">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { firstValueFrom, Subscription } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
|
||||
@Component({
|
||||
@@ -18,6 +18,9 @@ export class BlockchainComponent implements OnInit, OnDestroy {
|
||||
timeLtrSubscription: Subscription;
|
||||
timeLtr: boolean = this.stateService.timeLtr.value;
|
||||
ltrTransitionEnabled = false;
|
||||
connectionStateSubscription: Subscription;
|
||||
loadingTip: boolean = true;
|
||||
connected: boolean = true;
|
||||
|
||||
constructor(
|
||||
public stateService: StateService,
|
||||
@@ -28,10 +31,17 @@ export class BlockchainComponent implements OnInit, OnDestroy {
|
||||
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
|
||||
this.timeLtr = !!ltr;
|
||||
});
|
||||
this.connectionStateSubscription = this.stateService.connectionState$.subscribe(state => {
|
||||
this.connected = (state === 2);
|
||||
})
|
||||
firstValueFrom(this.stateService.chainTip$).then(tip => {
|
||||
this.loadingTip = false;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.timeLtrSubscription.unsubscribe();
|
||||
this.connectionStateSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
trackByPageFn(index: number, item: { index: number }) {
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
<div class="dropdown-menu show" *ngIf="results" [hidden]="!results.hashQuickMatch && !results.addresses.length && !results.nodes.length && !results.channels.length">
|
||||
<ng-template [ngIf]="results.blockHeight">
|
||||
<div class="card-title">Bitcoin Block Height</div>
|
||||
<div class="card-title" i18n="search.bitcoin-block-height">Bitcoin Block Height</div>
|
||||
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
Go to "{{ results.searchText }}"
|
||||
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText }"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.txId">
|
||||
<div class="card-title">Bitcoin Transaction</div>
|
||||
<div class="card-title" i18n="search.bitcoin-transaction">Bitcoin Transaction</div>
|
||||
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
Go to "{{ results.searchText | shortenString : 13 }}"
|
||||
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : 13 }"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.address">
|
||||
<div class="card-title">Bitcoin Address</div>
|
||||
<div class="card-title" i18n="search.bitcoin-address">Bitcoin Address</div>
|
||||
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
Go to "{{ results.searchText | shortenString : isMobile ? 20 : 30 }}"
|
||||
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : isMobile ? 20 : 30 }"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.blockHash">
|
||||
<div class="card-title">Bitcoin Block</div>
|
||||
<div class="card-title" i18n="search.bitcoin-block">Bitcoin Block</div>
|
||||
<button (click)="clickItem(0)" [class.active]="0 === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
Go to "{{ results.searchText | shortenString : 13 }}"
|
||||
<ng-container *ngTemplateOutlet="goTo; context: { $implicit: results.searchText | shortenString : 13 }"></ng-container>
|
||||
</button>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.addresses.length">
|
||||
<div class="card-title">Bitcoin Addresses</div>
|
||||
<div class="card-title" i18n="search.bitcoin-addresses">Bitcoin Addresses</div>
|
||||
<ng-template ngFor [ngForOf]="results.addresses" let-address let-i="index">
|
||||
<button (click)="clickItem(results.hashQuickMatch + i)" [class.active]="(results.hashQuickMatch + i) === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
<ngb-highlight [result]="address | shortenString : isMobile ? 25 : 36" [term]="results.searchText"></ngb-highlight>
|
||||
@@ -32,7 +32,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.nodes.length">
|
||||
<div class="card-title">Lightning Nodes</div>
|
||||
<div class="card-title" i18n="search.lightning-nodes">Lightning Nodes</div>
|
||||
<ng-template ngFor [ngForOf]="results.nodes" let-node let-i="index">
|
||||
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + i)" [class.inactive]="node.status === 0" [class.active]="results.hashQuickMatch + results.addresses.length + i === activeIdx" [routerLink]="['/lightning/node' | relativeUrl, node.public_key]" type="button" role="option" class="dropdown-item">
|
||||
<ngb-highlight [result]="node.alias" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ node.public_key | shortenString : 10 }}</span>
|
||||
@@ -40,7 +40,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template [ngIf]="results.channels.length">
|
||||
<div class="card-title">Lightning Channels</div>
|
||||
<div class="card-title" i18n="search.lightning-channels">Lightning Channels</div>
|
||||
<ng-template ngFor [ngForOf]="results.channels" let-channel let-i="index">
|
||||
<button (click)="clickItem(results.hashQuickMatch + results.addresses.length + results.nodes.length + i)" [class.inactive]="channel.status === 2" [class.active]="results.hashQuickMatch + results.addresses.length + results.nodes.length + i === activeIdx" type="button" role="option" class="dropdown-item">
|
||||
<ngb-highlight [result]="channel.short_id" [term]="results.searchText"></ngb-highlight> <span class="symbol">{{ channel.id }}</span>
|
||||
@@ -48,3 +48,5 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<ng-template #goTo let-x i18n="search.go-to">Go to "{{ x }}"</ng-template>
|
||||
|
||||
@@ -21,6 +21,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||
timeLtr: boolean = this.stateService.timeLtr.value;
|
||||
chainTipSubscription: Subscription;
|
||||
chainTip: number = -1;
|
||||
tipIsSet: boolean = false;
|
||||
markBlockSubscription: Subscription;
|
||||
blockCounterSubscription: Subscription;
|
||||
@ViewChild('blockchainContainer') blockchainContainer: ElementRef;
|
||||
@@ -58,6 +59,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
this.chainTipSubscription = this.stateService.chainTip$.subscribe((height) => {
|
||||
this.chainTip = height;
|
||||
this.tipIsSet = true;
|
||||
this.updatePages();
|
||||
if (this.pendingMark != null) {
|
||||
this.scrollToBlock(this.pendingMark);
|
||||
@@ -66,7 +68,7 @@ export class StartComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
this.markBlockSubscription = this.stateService.markBlock$.subscribe((mark) => {
|
||||
if (mark?.blockHeight != null) {
|
||||
if (this.chainTip >=0) {
|
||||
if (this.tipIsSet) {
|
||||
if (!this.blockInViewport(mark.blockHeight)) {
|
||||
this.scrollToBlock(mark.blockHeight);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="doc-content">
|
||||
|
||||
<div id="disclaimer">
|
||||
<table><tr><td><svg viewBox="0 0 304 304" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" style="fill:#ffc107;fill-opacity:1"><path d="M135.3 34.474c-15.62 27.306-54.206 95.63-85.21 150.534L9.075 257.583C5.382 264.08 6.76 269.217 7.908 271.7c2.326 5.028 7.29 7.537 11.155 8.215l.78.133 264.698.006-.554-.02c4.152.255 9.664-1.24 12.677-6.194 1.926-3.18 3.31-8.589-1.073-16.278L213.637 114.37l-45.351-79.205c-5.681-9.932-12.272-12.022-16.8-12.022-4.42 0-10.818 1.964-16.181 11.331h-.006zm-69.072 159.94c30.997-54.885 69.563-123.184 85.16-150.446l.186-.297c.2.303.393.582.618.981l45.363 79.22s72.377 126.47 78.569 137.283l-247.618-.007 37.719-66.734" style="fill:#ffc107;fill-opacity:1"/><path d="M152.597 247.445c8.02 0 14.518-6.728 14.518-15.025 0-8.29-6.499-15.018-14.518-15.018-8.031 0-14.529 6.728-14.529 15.018 0 8.297 6.498 15.025 14.53 15.025m-.001-147.18c11.586 0 22.23 10.958 20.977 21.7l-9.922 75.564c-.966 6.601-4.95 11.433-11.055 11.433s-10.102-4.832-11.056-11.433l-9.927-75.564c-1.26-10.742 9.39-21.7 20.983-21.7" style="fill:#ffc107;fill-opacity:1"/></g></svg></td><td><p><b>mempool.space merely provides data about the Bitcoin network.</b> It cannot help you with retrieving funds, confirming your transaction quicker, etc.</p><p>For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).</p></td></tr></table>
|
||||
<table><tr><td><svg viewBox="0 0 304 304" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd" style="fill:#ffc107;fill-opacity:1"><path d="M135.3 34.474c-15.62 27.306-54.206 95.63-85.21 150.534L9.075 257.583C5.382 264.08 6.76 269.217 7.908 271.7c2.326 5.028 7.29 7.537 11.155 8.215l.78.133 264.698.006-.554-.02c4.152.255 9.664-1.24 12.677-6.194 1.926-3.18 3.31-8.589-1.073-16.278L213.637 114.37l-45.351-79.205c-5.681-9.932-12.272-12.022-16.8-12.022-4.42 0-10.818 1.964-16.181 11.331h-.006zm-69.072 159.94c30.997-54.885 69.563-123.184 85.16-150.446l.186-.297c.2.303.393.582.618.981l45.363 79.22s72.377 126.47 78.569 137.283l-247.618-.007 37.719-66.734" style="fill:#ffc107;fill-opacity:1"/><path d="M152.597 247.445c8.02 0 14.518-6.728 14.518-15.025 0-8.29-6.499-15.018-14.518-15.018-8.031 0-14.529 6.728-14.529 15.018 0 8.297 6.498 15.025 14.53 15.025m-.001-147.18c11.586 0 22.23 10.958 20.977 21.7l-9.922 75.564c-.966 6.601-4.95 11.433-11.055 11.433s-10.102-4.832-11.056-11.433l-9.927-75.564c-1.26-10.742 9.39-21.7 20.983-21.7" style="fill:#ffc107;fill-opacity:1"/></g></svg></td><td><p i18n="faq.big-disclaimer"><b>mempool.space merely provides data about the Bitcoin network.</b> It cannot help you with retrieving funds, confirming your transaction quicker, etc.</p><p>For any such requests, you need to get in touch with the entity that helped make the transaction (wallet software, exchange company, etc).</p></td></tr></table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,19 +17,19 @@ export class ClosingTypeComponent implements OnChanges {
|
||||
getLabelFromType(type: number): { label: string; class: string } {
|
||||
switch (type) {
|
||||
case 1: return {
|
||||
label: 'Mutually closed',
|
||||
label: $localize`Mutually closed`,
|
||||
class: 'success',
|
||||
};
|
||||
case 2: return {
|
||||
label: 'Force closed',
|
||||
label: $localize`Force closed`,
|
||||
class: 'warning',
|
||||
};
|
||||
case 3: return {
|
||||
label: 'Force closed with penalty',
|
||||
label: $localize`Force closed with penalty`,
|
||||
class: 'danger',
|
||||
};
|
||||
default: return {
|
||||
label: 'Unknown',
|
||||
label: $localize`:@@e5d8bb389c702588877f039d72178f219453a72d:Unknown`,
|
||||
class: 'secondary',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div class="widget-toggler">
|
||||
<a href="" (click)="switchMode('avg')" class="toggler-option"
|
||||
[ngClass]="{'inactive': mode === 'avg'}"><small>avg</small></a>
|
||||
[ngClass]="{'inactive': mode === 'avg'}"><small i18n="statistics.average-small">avg</small></a>
|
||||
<span style="color: #ffffff66; font-size: 8px"> | </span>
|
||||
<a href="" (click)="switchMode('med')" class="toggler-option"
|
||||
[ngClass]="{'inactive': mode === 'med'}"><small>med</small></a>
|
||||
[ngClass]="{'inactive': mode === 'med'}"><small i18n="statistics.median-small">med</small></a>
|
||||
</div>
|
||||
|
||||
<div class="fee-estimation-wrapper" *ngIf="statistics$ | async as statistics; else loadingReward">
|
||||
|
||||
@@ -167,7 +167,7 @@ export class NodeFeeChartComponent implements OnInit {
|
||||
padding: 10,
|
||||
data: [
|
||||
{
|
||||
name: 'Outgoing Fees',
|
||||
name: $localize`Outgoing Fees`,
|
||||
inactiveColor: 'rgb(110, 112, 121)',
|
||||
textStyle: {
|
||||
color: 'white',
|
||||
@@ -175,7 +175,7 @@ export class NodeFeeChartComponent implements OnInit {
|
||||
icon: 'roundRect',
|
||||
},
|
||||
{
|
||||
name: 'Incoming Fees',
|
||||
name: $localize`Incoming Fees`,
|
||||
inactiveColor: 'rgb(110, 112, 121)',
|
||||
textStyle: {
|
||||
color: 'white',
|
||||
@@ -205,7 +205,7 @@ export class NodeFeeChartComponent implements OnInit {
|
||||
series: outgoingData.length === 0 ? undefined : [
|
||||
{
|
||||
zlevel: 0,
|
||||
name: 'Outgoing Fees',
|
||||
name: $localize`Outgoing Fees`,
|
||||
data: outgoingData.map(bucket => ({
|
||||
value: bucket.capacity,
|
||||
label: bucket.label,
|
||||
@@ -219,7 +219,7 @@ export class NodeFeeChartComponent implements OnInit {
|
||||
},
|
||||
{
|
||||
zlevel: 0,
|
||||
name: 'Incoming Fees',
|
||||
name: $localize`Incoming Fees`,
|
||||
data: incomingData.map(bucket => ({
|
||||
value: -bucket.capacity,
|
||||
label: bucket.label,
|
||||
|
||||
@@ -62,7 +62,12 @@ export class CacheService {
|
||||
for (let i = 0; i < chunkSize; i++) {
|
||||
this.blockLoading[maxHeight - i] = true;
|
||||
}
|
||||
const result = await firstValueFrom(this.apiService.getBlocks$(maxHeight));
|
||||
let result;
|
||||
try {
|
||||
result = await firstValueFrom(this.apiService.getBlocks$(maxHeight));
|
||||
} catch (e) {
|
||||
console.log("failed to load blocks: ", e.message);
|
||||
}
|
||||
for (let i = 0; i < chunkSize; i++) {
|
||||
delete this.blockLoading[maxHeight - i];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user