From 135fbfc4f3fa34effbd90c51cb95b7bf6ea60235 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 29 Sep 2022 22:40:46 +0000 Subject: [PATCH] Reversible mempool block visualization --- .../block-overview-graph.component.ts | 12 ++++++-- .../block-overview-graph/block-scene.ts | 18 ++++++++++++ .../mempool-block-overview.component.html | 2 +- .../mempool-block-overview.component.ts | 29 ++++++++++++++----- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts index f4980b088..14607f398 100644 --- a/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts +++ b/frontend/src/app/components/block-overview-graph/block-overview-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy } from '@angular/core'; +import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, NgZone, AfterViewInit, OnDestroy, OnChanges } from '@angular/core'; import { TransactionStripped } from '../../interfaces/websocket.interface'; import { FastVertexArray } from './fast-vertex-array'; import BlockScene from './block-scene'; @@ -11,7 +11,7 @@ import { Position } from './sprite-types'; templateUrl: './block-overview-graph.component.html', styleUrls: ['./block-overview-graph.component.scss'], }) -export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy { +export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, OnChanges { @Input() isLoading: boolean; @Input() resolution: number; @Input() blockLimit: number; @@ -57,6 +57,14 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy { this.resizeCanvas(); } + ngOnChanges(changes): void { + if (changes.orientation || changes.flip) { + if (this.scene) { + this.scene.setOrientation(this.orientation, this.flip); + } + } + } + ngOnDestroy(): void { if (this.animationFrameRequest) { cancelAnimationFrame(this.animationFrameRequest); diff --git a/frontend/src/app/components/block-overview-graph/block-scene.ts b/frontend/src/app/components/block-overview-graph/block-scene.ts index 20a219e75..af0ba56fa 100644 --- a/frontend/src/app/components/block-overview-graph/block-scene.ts +++ b/frontend/src/app/components/block-overview-graph/block-scene.ts @@ -42,6 +42,24 @@ export default class BlockScene { } } + setOrientation(orientation: string, flip: boolean): void { + this.orientation = orientation; + this.flip = flip; + this.dirty = true; + const startTime = performance.now(); + Object.values(this.txs).forEach(txView => { + this.saveGridToScreenPosition(txView); + this.applyTxUpdate(txView, { + display: { + position: txView.screenPosition, + color: txView.getColor() + }, + duration: 0 + }); + this.setTxOnScreen(txView, startTime, 0); + }); + } + // Destroy the current layout and clean up graphics sprites without any exit animation destroy(): void { Object.values(this.txs).forEach(tx => tx.destroy()); 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 304b2a7f9..3cb4ff3e8 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 @@ -3,7 +3,7 @@ [isLoading]="isLoading$ | async" [resolution]="75" [blockLimit]="stateService.blockVSize" - [orientation]="'left'" + [orientation]="timeLtr ? 'right' : 'left'" [flip]="true" (txClickEvent)="onTxClick($event)" > 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 b7fb65753..7a39e3536 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,5 +1,5 @@ import { Component, ComponentRef, ViewChild, HostListener, Input, Output, EventEmitter, - OnDestroy, OnChanges, ChangeDetectionStrategy, AfterViewInit } from '@angular/core'; + OnInit, OnDestroy, OnChanges, ChangeDetectionStrategy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; import { StateService } from '../../services/state.service'; import { MempoolBlockDelta, TransactionStripped } from '../../interfaces/websocket.interface'; import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component'; @@ -14,7 +14,7 @@ import { Router } from '@angular/router'; templateUrl: './mempool-block-overview.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, AfterViewInit { +export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { @Input() index: number; @Output() txPreviewEvent = new EventEmitter(); @@ -23,6 +23,10 @@ export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, Afte lastBlockHeight: number; blockIndex: number; isLoading$ = new BehaviorSubject(true); + timeLtrSubscription: Subscription; + timeLtr: boolean; + chainDirection: string = 'right'; + poolDirection: string = 'left'; blockSub: Subscription; deltaSub: Subscription; @@ -31,8 +35,18 @@ export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, Afte public stateService: StateService, private websocketService: WebsocketService, private router: Router, + private cd: ChangeDetectorRef, ) { } + ngOnInit(): void { + this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => { + this.timeLtr = !!ltr; + this.chainDirection = ltr ? 'left' : 'right'; + this.poolDirection = ltr ? 'right' : 'left'; + this.cd.markForCheck(); + }); + } + ngAfterViewInit(): void { this.blockSub = merge( of(true), @@ -50,7 +64,7 @@ export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, Afte ngOnChanges(changes): void { if (changes.index) { if (this.blockGraph) { - this.blockGraph.clear(changes.index.currentValue > changes.index.previousValue ? 'right' : 'left'); + this.blockGraph.clear(changes.index.currentValue > changes.index.previousValue ? this.chainDirection : this.poolDirection); } this.isLoading$.next(true); this.websocketService.startTrackMempoolBlock(changes.index.currentValue); @@ -60,16 +74,17 @@ export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, Afte ngOnDestroy(): void { this.blockSub.unsubscribe(); this.deltaSub.unsubscribe(); + this.timeLtrSubscription.unsubscribe(); this.websocketService.stopTrackMempoolBlock(); } replaceBlock(transactionsStripped: TransactionStripped[]): void { const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeight); 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) ? this.poolDirection : this.chainDirection; this.blockGraph.enter(transactionsStripped, direction); } else { - this.blockGraph.replace(transactionsStripped, blockMined ? 'right' : 'left'); + this.blockGraph.replace(transactionsStripped, blockMined ? this.chainDirection : this.poolDirection); } this.lastBlockHeight = this.stateService.latestBlockHeight; @@ -81,10 +96,10 @@ export class MempoolBlockOverviewComponent implements OnDestroy, OnChanges, Afte const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeight); 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) ? this.poolDirection : this.chainDirection; this.blockGraph.replace(delta.added, direction); } else { - this.blockGraph.update(delta.added, delta.removed, blockMined ? 'right' : 'left', blockMined); + this.blockGraph.update(delta.added, delta.removed, blockMined ? this.chainDirection : this.poolDirection, blockMined); } this.lastBlockHeight = this.stateService.latestBlockHeight;