New base code for mempool blockchain explorerer
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<table class="table table-borderless">
|
||||
<thead>
|
||||
<th style="width: 120px;">Height</th>
|
||||
<th class="d-none d-md-block" style="width: 300px;">Timestamp</th>
|
||||
<th style="width: 200px;">Mined</th>
|
||||
<th style="width: 150px;">Transactions</th>
|
||||
<th style="width: 175px;">Size</th>
|
||||
<th class="d-none d-md-block">Filled</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let block of blocks; let i= index; trackBy: trackByBlock">
|
||||
<td><a [routerLink]="['./block', 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>{{ block.timestamp | timeSince : trigger }} ago</td>
|
||||
<td>{{ block.tx_count }}</td>
|
||||
<td>{{ block.size | bytes: 2 }}</td>
|
||||
<td class="d-none d-md-block">
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" [ngStyle]="{'width': (block.weight / 4000000)*100 + '%' }"></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><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="text-center">
|
||||
<ng-template [ngIf]="isLoading">
|
||||
<div class="spinner-border"></div>
|
||||
<br><br>
|
||||
</ng-template>
|
||||
<br>
|
||||
<button *ngIf="blocks.length" [disabled]="isLoading" type="button" class="btn btn-primary" (click)="loadMore()">Load more</button>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LatestBlocksComponent } from './latest-blocks.component';
|
||||
|
||||
describe('LatestBlocksComponent', () => {
|
||||
let component: LatestBlocksComponent;
|
||||
let fixture: ComponentFixture<LatestBlocksComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ LatestBlocksComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LatestBlocksComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { ElectrsApiService } from '../../services/electrs-api.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Block } from '../../interfaces/electrs.interface';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-latest-blocks',
|
||||
templateUrl: './latest-blocks.component.html',
|
||||
styleUrls: ['./latest-blocks.component.scss'],
|
||||
})
|
||||
export class LatestBlocksComponent implements OnInit, OnDestroy {
|
||||
blocks: any[] = [];
|
||||
blockSubscription: Subscription;
|
||||
isLoading = true;
|
||||
interval: any;
|
||||
trigger = 0;
|
||||
|
||||
constructor(
|
||||
private electrsApiService: ElectrsApiService,
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.blockSubscription = this.stateService.blocks$
|
||||
.subscribe((block) => {
|
||||
if (block === null || !this.blocks.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
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.loadInitialBlocks();
|
||||
this.interval = window.setInterval(() => this.trigger++, 1000 * 60);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.interval);
|
||||
this.blockSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
loadInitialBlocks() {
|
||||
this.electrsApiService.listBlocks$()
|
||||
.subscribe((blocks) => {
|
||||
this.blocks = blocks;
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
trackByBlock(index: number, block: Block) {
|
||||
return block.height;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user