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,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) | ||||
|   } | ||||
|  | ||||
| @ -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