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 { 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,
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
  
 | 
			
		||||
  <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  <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>
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,9 @@ li.nav-item a {
 | 
			
		||||
  color: #ffffff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navbar-nav {
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nav {
 | 
			
		||||
  box-shadow: 0px 0px 15px 0px #000;
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -3,4 +3,14 @@
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  width: 375px;
 | 
			
		||||
  text-overflow: ellipsis;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-box-container {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 992px) {
 | 
			
		||||
  .search-box-container {
 | 
			
		||||
    width: 350px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user