mirror of
https://github.com/mempool/mempool.git
synced 2024-12-28 17:24:25 +01:00
Replacing menu button texts with icons.
Latest blocks is now a separate page.
This commit is contained in:
parent
2c3b02a682
commit
b67b025dc2
@ -13,6 +13,7 @@ import { AssetComponent } from './components/asset/asset.component';
|
|||||||
import { AssetsComponent } from './assets/assets.component';
|
import { AssetsComponent } from './assets/assets.component';
|
||||||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
|
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -41,6 +42,10 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'blocks',
|
||||||
|
component: LatestBlocksComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'graphs',
|
path: 'graphs',
|
||||||
component: StatisticsComponent,
|
component: StatisticsComponent,
|
||||||
@ -85,6 +90,10 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'blocks',
|
||||||
|
component: LatestBlocksComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'graphs',
|
path: 'graphs',
|
||||||
component: StatisticsComponent,
|
component: StatisticsComponent,
|
||||||
@ -150,6 +159,10 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'blocks',
|
||||||
|
component: LatestBlocksComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'graphs',
|
path: 'graphs',
|
||||||
component: StatisticsComponent,
|
component: StatisticsComponent,
|
||||||
|
@ -16,6 +16,7 @@ import { StateService } from './services/state.service';
|
|||||||
import { BlockComponent } from './components/block/block.component';
|
import { BlockComponent } from './components/block/block.component';
|
||||||
import { AddressComponent } from './components/address/address.component';
|
import { AddressComponent } from './components/address/address.component';
|
||||||
import { SearchFormComponent } from './components/search-form/search-form.component';
|
import { SearchFormComponent } from './components/search-form/search-form.component';
|
||||||
|
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
|
||||||
import { WebsocketService } from './services/websocket.service';
|
import { WebsocketService } from './services/websocket.service';
|
||||||
import { AddressLabelsComponent } from './components/address-labels/address-labels.component';
|
import { AddressLabelsComponent } from './components/address-labels/address-labels.component';
|
||||||
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
import { MempoolBlocksComponent } from './components/mempool-blocks/mempool-blocks.component';
|
||||||
@ -41,6 +42,8 @@ import { SharedModule } from './shared/shared.module';
|
|||||||
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
import { FeesBoxComponent } from './components/fees-box/fees-box.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
|
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||||
|
import { faChartArea, faCube, faDatabase, faInfo, faInfoCircle, faList, faQuestion, faQuestionCircle, faTachometerAlt, faThList, faTv } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -57,6 +60,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
|||||||
TransactionsListComponent,
|
TransactionsListComponent,
|
||||||
AddressComponent,
|
AddressComponent,
|
||||||
AmountComponent,
|
AmountComponent,
|
||||||
|
LatestBlocksComponent,
|
||||||
SearchFormComponent,
|
SearchFormComponent,
|
||||||
TimespanComponent,
|
TimespanComponent,
|
||||||
AddressLabelsComponent,
|
AddressLabelsComponent,
|
||||||
@ -80,6 +84,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
|||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
InfiniteScrollModule,
|
InfiniteScrollModule,
|
||||||
NgbTypeaheadModule,
|
NgbTypeaheadModule,
|
||||||
|
FontAwesomeModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
@ -91,4 +96,15 @@ import { DashboardComponent } from './dashboard/dashboard.component';
|
|||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule {
|
||||||
|
constructor(library: FaIconLibrary) {
|
||||||
|
library.addIcons(faInfoCircle);
|
||||||
|
library.addIcons(faChartArea);
|
||||||
|
library.addIcons(faTv);
|
||||||
|
library.addIcons(faCube);
|
||||||
|
library.addIcons(faThList);
|
||||||
|
library.addIcons(faList);
|
||||||
|
library.addIcons(faTachometerAlt);
|
||||||
|
library.addIcons(faDatabase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
<div class="container-xl">
|
||||||
|
<h1 style="float: left;">Blocks</h1>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
|
<table class="table table-borderless" [alwaysCallback]="true" [fromRoot]="true" [infiniteScrollContainer]="'body'" infiniteScroll [infiniteScrollDistance]="1.5" [infiniteScrollUpDistance]="1.5" [infiniteScrollThrottle]="50" (scrolled)="loadMore()">
|
||||||
|
<thead>
|
||||||
|
<th style="width: 15%;">Height</th>
|
||||||
|
<th class="d-none d-md-block" style="width: 20%;">Timestamp</th>
|
||||||
|
<th style="width: 20%;">Mined</th>
|
||||||
|
<th class="d-none d-lg-block" style="width: 15%;">Transactions</th>
|
||||||
|
<th style="width: 20%;">Filled</th>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||||
|
<td><a [routerLink]="['/block' | relativeUrl, block.id]" [state]="{ data: { block: block } }">{{ block.height }}</a></td>
|
||||||
|
<td class="d-none d-md-block">{{ block.timestamp * 1000 | date:'yyyy-MM-dd HH:mm' }}</td>
|
||||||
|
<td><app-time-since [time]="block.timestamp" [fastRender]="true"></app-time-since> ago</td>
|
||||||
|
<td class="d-none d-lg-block">{{ block.tx_count | number }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="progress position-relative">
|
||||||
|
<div class="progress-bar progress-mempool {{ network$ | async }}" role="progressbar" [ngStyle]="{'width': (block.weight / 4000000)*100 + '%' }"></div>
|
||||||
|
<div class="progress-text">{{ block.size | bytes: 2 }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<ng-template [ngIf]="isLoading">
|
||||||
|
<tr *ngFor="let item of [1,2,3,4,5,6,7,8,9,10]">
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
<td class="d-none d-md-block"><span class="skeleton-loader"></span></td>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
<td class="d-none d-lg-block"><span class="skeleton-loader"></span></td>
|
||||||
|
<td><span class="skeleton-loader"></span></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,14 @@
|
|||||||
|
.progress {
|
||||||
|
background-color: #2d3348;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.d-md-block {
|
||||||
|
display: table-cell !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.d-lg-block {
|
||||||
|
display: table-cell !important;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||||
|
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||||
|
import { StateService } from '../../services/state.service';
|
||||||
|
import { Block } from '../../interfaces/electrs.interface';
|
||||||
|
import { Subscription, Observable, merge, of } from 'rxjs';
|
||||||
|
import { SeoService } from '../../services/seo.service';
|
||||||
|
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-latest-blocks',
|
||||||
|
templateUrl: './latest-blocks.component.html',
|
||||||
|
styleUrls: ['./latest-blocks.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class LatestBlocksComponent implements OnInit, OnDestroy {
|
||||||
|
network$: Observable<string>;
|
||||||
|
|
||||||
|
blocks: any[] = [];
|
||||||
|
blockSubscription: Subscription;
|
||||||
|
isLoading = true;
|
||||||
|
interval: any;
|
||||||
|
|
||||||
|
latestBlockHeight: number;
|
||||||
|
|
||||||
|
heightOfPageUntilBlocks = 150;
|
||||||
|
heightOfBlocksTableChunk = 470;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private electrsApiService: ElectrsApiService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private seoService: SeoService,
|
||||||
|
private websocketService: WebsocketService,
|
||||||
|
private cd: ChangeDetectorRef,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.seoService.resetTitle();
|
||||||
|
this.websocketService.want(['blocks']);
|
||||||
|
|
||||||
|
this.network$ = merge(of(''), this.stateService.networkChanged$);
|
||||||
|
|
||||||
|
this.blockSubscription = this.stateService.blocks$
|
||||||
|
.subscribe(([block]) => {
|
||||||
|
if (block === null || !this.blocks.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.latestBlockHeight = block.height;
|
||||||
|
|
||||||
|
if (block.height === this.blocks[0].height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are out of sync, reload the blocks instead
|
||||||
|
if (block.height > this.blocks[0].height + 1) {
|
||||||
|
this.loadInitialBlocks();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.height <= this.blocks[0].height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.blocks.pop();
|
||||||
|
this.blocks.unshift(block);
|
||||||
|
this.cd.markForCheck();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loadInitialBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.blockSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadInitialBlocks() {
|
||||||
|
this.electrsApiService.listBlocks$()
|
||||||
|
.subscribe((blocks) => {
|
||||||
|
this.blocks = blocks;
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
this.latestBlockHeight = blocks[0].height;
|
||||||
|
|
||||||
|
const spaceForBlocks = window.innerHeight - this.heightOfPageUntilBlocks;
|
||||||
|
const chunks = Math.ceil(spaceForBlocks / this.heightOfBlocksTableChunk) - 1;
|
||||||
|
if (chunks > 0) {
|
||||||
|
this.loadMore(chunks);
|
||||||
|
}
|
||||||
|
this.cd.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore(chunks = 0) {
|
||||||
|
if (this.isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isLoading = true;
|
||||||
|
this.electrsApiService.listBlocks$(this.blocks[this.blocks.length - 1].height - 1)
|
||||||
|
.subscribe((blocks) => {
|
||||||
|
this.blocks = this.blocks.concat(blocks);
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
const chunksLeft = chunks - 1;
|
||||||
|
if (chunksLeft > 0) {
|
||||||
|
this.loadMore(chunksLeft);
|
||||||
|
}
|
||||||
|
this.cd.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trackByBlock(index: number, block: Block) {
|
||||||
|
return block.height;
|
||||||
|
}
|
||||||
|
}
|
@ -28,31 +28,37 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="navbar-collapse collapse" id="navbarCollapse" [ngClass]="{'show': navCollapsed}">
|
<div class="navbar-collapse collapse" id="navbarCollapse" [ngClass]="{'show': navCollapsed}">
|
||||||
<ul class="navbar-nav mr-auto {{ network.val }}">
|
<ul class="navbar-nav mr-auto pt-2 pb-2 pb-md-0 pt-md-0 {{ network.val }}">
|
||||||
<ng-template [ngIf]="network.val === 'bisq'" [ngIfElse]="notBisq">
|
<ng-template [ngIf]="network.val === 'bisq'" [ngIfElse]="notBisq">
|
||||||
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
||||||
<a class="nav-link" [routerLink]="['/bisq']" (click)="collapse()">Transactions</a>
|
<a class="nav-link" [routerLink]="['/bisq']" (click)="collapse()"><fa-icon [icon]="['fas', 'list']" size="lg" [fixedWidth]="true" title="Transactions"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" routerLinkActive="active">
|
<li class="nav-item" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/bisq/blocks']" (click)="collapse()">Blocks</a>
|
<a class="nav-link" [routerLink]="['/bisq/blocks']" (click)="collapse()"><fa-icon [icon]="['fas', 'th-list']" size="lg" [fixedWidth]="true" title="Blocks"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" routerLinkActive="active">
|
<li class="nav-item" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/bisq/stats']" (click)="collapse()">Stats</a>
|
<a class="nav-link" [routerLink]="['/bisq/stats']" (click)="collapse()"><fa-icon [icon]="['fas', 'tachometer-alt']" size="lg" [fixedWidth]="true" title="Stats"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<ng-template #notBisq>
|
<ng-template #notBisq>
|
||||||
<li class="nav-item" routerLinkActive="active">
|
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
|
||||||
<a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()">Graphs</a>
|
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cube']" size="lg" [fixedWidth]="true" title="Dashboard"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" routerLinkActive="active">
|
<li class="nav-item" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()">TV view <img src="./resources/expand.png" width="15"/></a>
|
<a class="nav-link" [routerLink]="['/blocks' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'th-list']" size="lg" [fixedWidth]="true" title="Blocks"></fa-icon></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active">
|
||||||
|
<a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'chart-area']" size="lg" [fixedWidth]="true" title="Graphs"></fa-icon></a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" routerLinkActive="active">
|
||||||
|
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'tv']" size="lg" [fixedWidth]="true" title="Full screen TV view"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<li *ngIf="network.val === 'liquid'" class="nav-item" routerLinkActive="active">
|
<li *ngIf="network.val === 'liquid'" class="nav-item" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()">Assets</a>
|
<a class="nav-link" [routerLink]="['/liquid/assets']" (click)="collapse()"><fa-icon [icon]="['fas', 'database']" size="lg" [fixedWidth]="true" title="Assets"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" routerLinkActive="active">
|
<li class="nav-item" routerLinkActive="active">
|
||||||
<a class="nav-link" [routerLink]="['/about' | relativeUrl]" (click)="collapse()">About</a>
|
<a class="nav-link" [routerLink]="['/about' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'info-circle']" size="lg" [fixedWidth]="true" title="About"></fa-icon></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>
|
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>
|
||||||
|
@ -24,6 +24,9 @@ li.nav-item a {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-nav {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
box-shadow: 0px 0px 15px 0px #000;
|
box-shadow: 0px 0px 15px 0px #000;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" class="mr-4" novalidate>
|
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
|
||||||
<div class="form-row">
|
<div class="d-flex">
|
||||||
<div style="width: 350px;" class="mr-2">
|
<div class="search-box-container mr-2">
|
||||||
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" placeholder="Transaction, block height, hash or address">
|
<input #instance="ngbTypeahead" [ngbTypeahead]="typeaheadSearch" (selectItem)="itemSelected()" (focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)" formControlName="searchText" type="text" class="form-control" placeholder="Transaction, block height, hash or address">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -3,4 +3,14 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 375px;
|
width: 375px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-box-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.search-box-container {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user