Projected block loading spinner & WebGL detection
This commit is contained in:
parent
3ffc4956f4
commit
6cd8c448b4
@ -90,7 +90,7 @@ export default class BlockScene {
|
|||||||
this.updateAll(startTime, direction)
|
this.updateAll(startTime, direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
update (add: TransactionStripped[], remove: string[], direction: string = 'left'): void {
|
update (add: TransactionStripped[], remove: string[], direction: string = 'left', resetLayout: boolean = false): void {
|
||||||
const startTime = performance.now()
|
const startTime = performance.now()
|
||||||
const removed = this.removeBatch(remove, startTime, direction)
|
const removed = this.removeBatch(remove, startTime, direction)
|
||||||
|
|
||||||
@ -101,17 +101,25 @@ export default class BlockScene {
|
|||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
// try to insert new txs directly
|
if (resetLayout) {
|
||||||
const remaining = []
|
add.forEach(tx => {
|
||||||
add.map(tx => new TxView(tx, this.vertexArray)).sort((a,b) => { return b.feerate - a.feerate }).forEach(tx => {
|
if (!this.txs[tx.txid]) this.txs[tx.txid] = new TxView(tx, this.vertexArray)
|
||||||
if (!this.tryInsertByFee(tx)) {
|
})
|
||||||
remaining.push(tx)
|
this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight })
|
||||||
}
|
Object.values(this.txs).sort((a,b) => { return b.feerate - a.feerate }).forEach(tx => {
|
||||||
})
|
this.place(tx)
|
||||||
|
})
|
||||||
this.placeBatch(remaining)
|
} else {
|
||||||
|
// try to insert new txs directly
|
||||||
this.layout.applyGravity()
|
const remaining = []
|
||||||
|
add.map(tx => new TxView(tx, this.vertexArray)).sort((a,b) => { return b.feerate - a.feerate }).forEach(tx => {
|
||||||
|
if (!this.tryInsertByFee(tx)) {
|
||||||
|
remaining.push(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.placeBatch(remaining)
|
||||||
|
this.layout.applyGravity()
|
||||||
|
}
|
||||||
|
|
||||||
this.updateAll(startTime, direction)
|
this.updateAll(startTime, direction)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
<div class="mempool-block-overview">
|
<div class="mempool-block-overview">
|
||||||
<canvas class="block-overview" #blockCanvas></canvas>
|
<canvas class="block-overview" #blockCanvas></canvas>
|
||||||
|
<div class="loader-wrapper" [class.hidden]="!(isLoading$ | async)">
|
||||||
|
<div class="spinner-border ml-3 loading" role="status"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding-bottom: 100%;
|
padding-bottom: 100%;
|
||||||
background: #181b2d;
|
background: #181b2d;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-overview {
|
.block-overview {
|
||||||
@ -13,3 +16,20 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loader-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: opacity 500ms 500ms;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 500ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, NgZone } from '@angular/core';
|
import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, NgZone } from '@angular/core';
|
||||||
import { StateService } from 'src/app/services/state.service';
|
import { StateService } from 'src/app/services/state.service';
|
||||||
import { MempoolBlockWithTransactions, MempoolBlockDelta, TransactionStripped } from 'src/app/interfaces/websocket.interface';
|
import { MempoolBlockWithTransactions, MempoolBlockDelta, TransactionStripped } from 'src/app/interfaces/websocket.interface';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription, BehaviorSubject } from 'rxjs';
|
||||||
import { WebsocketService } from 'src/app/services/websocket.service';
|
import { WebsocketService } from 'src/app/services/websocket.service';
|
||||||
import { FastVertexArray } from './fast-vertex-array';
|
import { FastVertexArray } from './fast-vertex-array';
|
||||||
import BlockScene from './block-scene';
|
import BlockScene from './block-scene';
|
||||||
@ -33,6 +33,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
selectedTx: TxView | void;
|
selectedTx: TxView | void;
|
||||||
lastBlockHeight: number;
|
lastBlockHeight: number;
|
||||||
blockIndex: number;
|
blockIndex: number;
|
||||||
|
isLoading$ = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
blockSub: Subscription;
|
blockSub: Subscription;
|
||||||
deltaSub: Subscription;
|
deltaSub: Subscription;
|
||||||
@ -65,6 +66,8 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
|
|
||||||
ngOnChanges(changes): void {
|
ngOnChanges(changes): void {
|
||||||
if (changes.index) {
|
if (changes.index) {
|
||||||
|
this.clearBlock(changes.index.currentValue > changes.index.previousValue ? 'right' : 'left')
|
||||||
|
this.isLoading$.next(true)
|
||||||
this.websocketService.startTrackMempoolBlock(changes.index.currentValue);
|
this.websocketService.startTrackMempoolBlock(changes.index.currentValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +78,15 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
this.websocketService.stopTrackMempoolBlock();
|
this.websocketService.stopTrackMempoolBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearBlock(direction): void {
|
||||||
|
if (this.scene) {
|
||||||
|
this.scene.exit(direction)
|
||||||
|
}
|
||||||
|
this.hoverTx = null
|
||||||
|
this.selectedTx = null
|
||||||
|
this.txPreviewEvent.emit(null)
|
||||||
|
}
|
||||||
|
|
||||||
replaceBlock(block: MempoolBlockWithTransactions): void {
|
replaceBlock(block: MempoolBlockWithTransactions): void {
|
||||||
if (!this.scene) {
|
if (!this.scene) {
|
||||||
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
|
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
|
||||||
@ -82,8 +94,6 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeight)
|
const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeight)
|
||||||
if (this.blockIndex != this.index) {
|
if (this.blockIndex != this.index) {
|
||||||
const direction = (this.blockIndex == null || this.index < this.blockIndex) ? 'left' : 'right'
|
const direction = (this.blockIndex == null || this.index < this.blockIndex) ? 'left' : 'right'
|
||||||
this.scene.exit(direction)
|
|
||||||
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
|
|
||||||
this.scene.enter(block.transactions, direction)
|
this.scene.enter(block.transactions, direction)
|
||||||
} else {
|
} else {
|
||||||
this.scene.replace(block.transactions, blockMined ? 'right' : 'left')
|
this.scene.replace(block.transactions, blockMined ? 'right' : 'left')
|
||||||
@ -91,6 +101,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
|
|
||||||
this.lastBlockHeight = this.stateService.latestBlockHeight
|
this.lastBlockHeight = this.stateService.latestBlockHeight
|
||||||
this.blockIndex = this.index
|
this.blockIndex = this.index
|
||||||
|
this.isLoading$.next(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBlock(delta: MempoolBlockDelta): void {
|
updateBlock(delta: MempoolBlockDelta): void {
|
||||||
@ -105,11 +116,12 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
|||||||
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
|
this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
|
||||||
this.scene.enter(delta.added, direction)
|
this.scene.enter(delta.added, direction)
|
||||||
} else {
|
} else {
|
||||||
this.scene.update(delta.added, delta.removed, blockMined ? 'right' : 'left')
|
this.scene.update(delta.added, delta.removed, blockMined ? 'right' : 'left', blockMined)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastBlockHeight = this.stateService.latestBlockHeight
|
this.lastBlockHeight = this.stateService.latestBlockHeight
|
||||||
this.blockIndex = this.index
|
this.blockIndex = this.index
|
||||||
|
this.isLoading$.next(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
initCanvas (): void {
|
initCanvas (): void {
|
||||||
|
@ -68,10 +68,11 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<app-fee-distribution-graph [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
|
<app-fee-distribution-graph *ngIf="webGlEnabled" [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md chart-container">
|
<div class="col-md chart-container">
|
||||||
<app-mempool-block-overview [index]="mempoolBlockIndex" (txPreviewEvent)="setTxPreview($event)"></app-mempool-block-overview>
|
<app-mempool-block-overview *ngIf="webGlEnabled" [index]="mempoolBlockIndex" (txPreviewEvent)="setTxPreview($event)"></app-mempool-block-overview>
|
||||||
|
<app-fee-distribution-graph *ngIf="!webGlEnabled" [data]="mempoolBlock.feeRange" ></app-fee-distribution-graph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,13 +19,16 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
|
|||||||
mempoolBlock$: Observable<MempoolBlock>;
|
mempoolBlock$: Observable<MempoolBlock>;
|
||||||
ordinal$: BehaviorSubject<string> = new BehaviorSubject('');
|
ordinal$: BehaviorSubject<string> = new BehaviorSubject('');
|
||||||
previewTx: TransactionStripped | void;
|
previewTx: TransactionStripped | void;
|
||||||
|
webGlEnabled: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
) { }
|
) {
|
||||||
|
this.webGlEnabled = detectWebGL();
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||||
@ -81,3 +84,9 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
|
|||||||
this.previewTx = event
|
this.previewTx = event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function detectWebGL () {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
|
||||||
|
return (gl && gl instanceof WebGLRenderingContext)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user