Replacing menu button texts with icons.

Latest blocks is now a separate page.
This commit is contained in:
softsimon 2020-09-22 05:51:34 +07:00
parent 2c3b02a682
commit b67b025dc2
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
9 changed files with 231 additions and 14 deletions

View File

@ -13,6 +13,7 @@ import { AssetComponent } from './components/asset/asset.component';
import { AssetsComponent } from './assets/assets.component';
import { StatusViewComponent } from './components/status-view/status-view.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
const routes: Routes = [
{
@ -41,6 +42,10 @@ const routes: Routes = [
},
],
},
{
path: 'blocks',
component: LatestBlocksComponent,
},
{
path: 'graphs',
component: StatisticsComponent,
@ -85,6 +90,10 @@ const routes: Routes = [
},
],
},
{
path: 'blocks',
component: LatestBlocksComponent,
},
{
path: 'graphs',
component: StatisticsComponent,
@ -150,6 +159,10 @@ const routes: Routes = [
},
],
},
{
path: 'blocks',
component: LatestBlocksComponent,
},
{
path: 'graphs',
component: StatisticsComponent,

View File

@ -16,6 +16,7 @@ import { StateService } from './services/state.service';
import { BlockComponent } from './components/block/block.component';
import { AddressComponent } from './components/address/address.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 { AddressLabelsComponent } from './components/address-labels/address-labels.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 { FeesBoxComponent } from './components/fees-box/fees-box.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({
declarations: [
@ -57,6 +60,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
TransactionsListComponent,
AddressComponent,
AmountComponent,
LatestBlocksComponent,
SearchFormComponent,
TimespanComponent,
AddressLabelsComponent,
@ -80,6 +84,7 @@ import { DashboardComponent } from './dashboard/dashboard.component';
BrowserAnimationsModule,
InfiniteScrollModule,
NgbTypeaheadModule,
FontAwesomeModule,
SharedModule,
],
providers: [
@ -91,4 +96,15 @@ import { DashboardComponent } from './dashboard/dashboard.component';
],
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);
}
}

View File

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

View File

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

View File

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

View File

@ -28,31 +28,37 @@
</button>
<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">
<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 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 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>
</ng-template>
<ng-template #notBisq>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/graphs' | relativeUrl]" (click)="collapse()">Graphs</a>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a class="nav-link" [routerLink]="['/' | relativeUrl]" (click)="collapse()"><fa-icon [icon]="['fas', 'cube']" size="lg" [fixedWidth]="true" title="Dashboard"></fa-icon></a>
</li>
<li class="nav-item" routerLinkActive="active">
<a class="nav-link" [routerLink]="['/tv' | relativeUrl]" (click)="collapse()">TV view &nbsp;<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>
</ng-template>
<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 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>
</ul>
<app-search-form location="top" (searchTriggered)="collapse()"></app-search-form>

View File

@ -24,6 +24,9 @@ li.nav-item a {
color: #ffffff;
}
.navbar-nav {
flex-direction: row;
}
nav {
box-shadow: 0px 0px 15px 0px #000;

View File

@ -1,6 +1,6 @@
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" class="mr-4" novalidate>
<div class="form-row">
<div style="width: 350px;" class="mr-2">
<form [formGroup]="searchForm" (submit)="searchForm.valid && search()" novalidate>
<div class="d-flex">
<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">
</div>
<div>

View File

@ -4,3 +4,13 @@
width: 375px;
text-overflow: ellipsis;
}
.search-box-container {
width: 100%;
}
@media (min-width: 992px) {
.search-box-container {
width: 350px;
}
}