diff --git a/frontend/src/app/components/mempool-block-overview/block-scene.ts b/frontend/src/app/components/mempool-block-overview/block-scene.ts index 825569100..5e781e429 100644 --- a/frontend/src/app/components/mempool-block-overview/block-scene.ts +++ b/frontend/src/app/components/mempool-block-overview/block-scene.ts @@ -90,7 +90,7 @@ export default class BlockScene { 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 removed = this.removeBatch(remove, startTime, direction) @@ -101,17 +101,25 @@ export default class BlockScene { }) }, 1000) - // try to insert new txs directly - 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() + if (resetLayout) { + add.forEach(tx => { + if (!this.txs[tx.txid]) this.txs[tx.txid] = new TxView(tx, this.vertexArray) + }) + 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) + }) + } else { + // try to insert new txs directly + 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) } diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html index 275cc8833..6951120d7 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.html @@ -1,3 +1,6 @@
+
+
+
diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.scss b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.scss index d14af382f..8c3c271d1 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.scss +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.scss @@ -3,6 +3,9 @@ width: 100%; padding-bottom: 100%; background: #181b2d; + display: flex; + justify-content: center; + align-items: center; } .block-overview { @@ -13,3 +16,20 @@ bottom: 0; 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; + } +} diff --git a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts index 4edbbb49e..e75a78896 100644 --- a/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts +++ b/frontend/src/app/components/mempool-block-overview/mempool-block-overview.component.ts @@ -1,7 +1,7 @@ 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 { 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 { FastVertexArray } from './fast-vertex-array'; import BlockScene from './block-scene'; @@ -33,6 +33,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang selectedTx: TxView | void; lastBlockHeight: number; blockIndex: number; + isLoading$ = new BehaviorSubject(true); blockSub: Subscription; deltaSub: Subscription; @@ -65,6 +66,8 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang ngOnChanges(changes): void { if (changes.index) { + this.clearBlock(changes.index.currentValue > changes.index.previousValue ? 'right' : 'left') + this.isLoading$.next(true) this.websocketService.startTrackMempoolBlock(changes.index.currentValue); } } @@ -75,6 +78,15 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang 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 { if (!this.scene) { 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) if (this.blockIndex != this.index) { 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) } else { 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.blockIndex = this.index + this.isLoading$.next(false) } 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.enter(delta.added, direction) } 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.blockIndex = this.index + this.isLoading$.next(false) } initCanvas (): void { diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.html b/frontend/src/app/components/mempool-block/mempool-block.component.html index 9e8502ee5..da1e91f1b 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.html +++ b/frontend/src/app/components/mempool-block/mempool-block.component.html @@ -68,10 +68,11 @@ - +
- + +
diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index bab26eb2e..e20c0b67d 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -19,13 +19,16 @@ export class MempoolBlockComponent implements OnInit, OnDestroy { mempoolBlock$: Observable; ordinal$: BehaviorSubject = new BehaviorSubject(''); previewTx: TransactionStripped | void; + webGlEnabled: boolean; constructor( private route: ActivatedRoute, public stateService: StateService, private seoService: SeoService, private websocketService: WebsocketService, - ) { } + ) { + this.webGlEnabled = detectWebGL(); + } ngOnInit(): void { this.websocketService.want(['blocks', 'mempool-blocks']); @@ -81,3 +84,9 @@ export class MempoolBlockComponent implements OnInit, OnDestroy { this.previewTx = event } } + +function detectWebGL () { + const canvas = document.createElement("canvas"); + const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); + return (gl && gl instanceof WebGLRenderingContext) +}