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)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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,6 +101,15 @@ export default class BlockScene {
 | 
			
		||||
      })
 | 
			
		||||
    }, 1000)
 | 
			
		||||
 | 
			
		||||
    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 => {
 | 
			
		||||
@ -108,10 +117,9 @@ export default class BlockScene {
 | 
			
		||||
          remaining.push(tx)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      this.placeBatch(remaining)
 | 
			
		||||
 | 
			
		||||
      this.layout.applyGravity()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.updateAll(startTime, direction)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,6 @@
 | 
			
		||||
<div class="mempool-block-overview">
 | 
			
		||||
  <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>
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<boolean>(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 {
 | 
			
		||||
 | 
			
		||||
@ -68,10 +68,11 @@
 | 
			
		||||
              </ng-container>
 | 
			
		||||
          </tbody>
 | 
			
		||||
        </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 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>
 | 
			
		||||
 | 
			
		||||
@ -19,13 +19,16 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
 | 
			
		||||
  mempoolBlock$: Observable<MempoolBlock>;
 | 
			
		||||
  ordinal$: BehaviorSubject<string> = 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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user