Merge pull request #3951 from mempool/mononaut/tx-highlight
Highlight searched transactions in the block visualizations
This commit is contained in:
		
						commit
						f15f0570d4
					
				| @ -6,6 +6,8 @@ import TxSprite from './tx-sprite'; | ||||
| import TxView from './tx-view'; | ||||
| import { Position } from './sprite-types'; | ||||
| import { Price } from '../../services/price.service'; | ||||
| import { StateService } from '../../services/state.service'; | ||||
| import { Subscription } from 'rxjs'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-block-overview-graph', | ||||
| @ -44,16 +46,25 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|   scene: BlockScene; | ||||
|   hoverTx: TxView | void; | ||||
|   selectedTx: TxView | void; | ||||
|   highlightTx: TxView | void; | ||||
|   mirrorTx: TxView | void; | ||||
|   tooltipPosition: Position; | ||||
| 
 | ||||
|   readyNextFrame = false; | ||||
| 
 | ||||
|   searchText: string; | ||||
|   searchSubscription: Subscription; | ||||
| 
 | ||||
|   constructor( | ||||
|     readonly ngZone: NgZone, | ||||
|     readonly elRef: ElementRef, | ||||
|     private stateService: StateService, | ||||
|   ) { | ||||
|     this.vertexArray = new FastVertexArray(512, TxSprite.dataSize); | ||||
|     this.searchSubscription = this.stateService.searchText$.subscribe((text) => { | ||||
|       this.searchText = text; | ||||
|       this.updateSearchHighlight(); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   ngAfterViewInit(): void { | ||||
| @ -109,6 +120,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|       this.scene.setup(transactions); | ||||
|       this.readyNextFrame = true; | ||||
|       this.start(); | ||||
|       this.updateSearchHighlight(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -116,6 +128,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     if (this.scene) { | ||||
|       this.scene.enter(transactions, direction); | ||||
|       this.start(); | ||||
|       this.updateSearchHighlight(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -123,6 +136,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     if (this.scene) { | ||||
|       this.scene.exit(direction); | ||||
|       this.start(); | ||||
|       this.updateSearchHighlight(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -130,6 +144,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     if (this.scene) { | ||||
|       this.scene.replace(transactions || [], direction, sort); | ||||
|       this.start(); | ||||
|       this.updateSearchHighlight(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -137,6 +152,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     if (this.scene) { | ||||
|       this.scene.update(add, remove, change, direction, resetLayout); | ||||
|       this.start(); | ||||
|       this.updateSearchHighlight(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -406,6 +422,19 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   updateSearchHighlight(): void { | ||||
|     if (this.highlightTx && this.highlightTx.txid !== this.searchText && this.scene) { | ||||
|       this.scene.setHighlight(this.highlightTx, false); | ||||
|       this.start(); | ||||
|     } else if (this.scene?.txs && this.searchText && this.searchText.length === 64) { | ||||
|       this.highlightTx = this.scene.txs[this.searchText]; | ||||
|       if (this.highlightTx) { | ||||
|         this.scene.setHighlight(this.highlightTx, true); | ||||
|         this.start(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   setHighlightingEnabled(enabled: boolean): void { | ||||
|     if (this.scene) { | ||||
|       this.scene.setHighlighting(enabled); | ||||
|  | ||||
| @ -215,6 +215,10 @@ export default class BlockScene { | ||||
|     this.animateUntil = Math.max(this.animateUntil, tx.setHover(value)); | ||||
|   } | ||||
| 
 | ||||
|   setHighlight(tx: TxView, value: boolean): void { | ||||
|     this.animateUntil = Math.max(this.animateUntil, tx.setHighlight(value)); | ||||
|   } | ||||
| 
 | ||||
|   private init({ width, height, resolution, blockLimit, orientation, flip, vertexArray, highlighting, pixelAlign }: | ||||
|       { width: number, height: number, resolution: number, blockLimit: number, | ||||
|         orientation: string, flip: boolean, vertexArray: FastVertexArray, highlighting: boolean, pixelAlign: boolean } | ||||
|  | ||||
| @ -7,6 +7,7 @@ import BlockScene from './block-scene'; | ||||
| 
 | ||||
| const hoverTransitionTime = 300; | ||||
| const defaultHoverColor = hexToColor('1bd8f4'); | ||||
| const defaultHighlightColor = hexToColor('800080'); | ||||
| 
 | ||||
| const feeColors = mempoolFeeColors.map(hexToColor); | ||||
| const auditFeeColors = feeColors.map((color) => darken(desaturate(color, 0.3), 0.9)); | ||||
| @ -44,8 +45,10 @@ export default class TxView implements TransactionStripped { | ||||
|   initialised: boolean; | ||||
|   vertexArray: FastVertexArray; | ||||
|   hover: boolean; | ||||
|   highlight: boolean; | ||||
|   sprite: TxSprite; | ||||
|   hoverColor: Color | void; | ||||
|   highlightColor: Color | void; | ||||
| 
 | ||||
|   screenPosition: Square; | ||||
|   gridPosition: Square | void; | ||||
| @ -150,8 +153,40 @@ export default class TxView implements TransactionStripped { | ||||
|     } else { | ||||
|       this.hover = false; | ||||
|       this.hoverColor = null; | ||||
|       if (this.sprite) { | ||||
|         this.sprite.resume(hoverTransitionTime); | ||||
|       if (this.highlight) { | ||||
|         this.setHighlight(true, this.highlightColor); | ||||
|       } else { | ||||
|         if (this.sprite) { | ||||
|           this.sprite.resume(hoverTransitionTime); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     this.dirty = false; | ||||
|     return performance.now() + hoverTransitionTime; | ||||
|   } | ||||
| 
 | ||||
|   // Temporarily override the tx color
 | ||||
|   // returns minimum transition end time
 | ||||
|   setHighlight(highlightOn: boolean, color: Color | void = defaultHighlightColor): number { | ||||
|     if (highlightOn) { | ||||
|       this.highlight = true; | ||||
|       this.highlightColor = color; | ||||
| 
 | ||||
|       this.sprite.update({ | ||||
|         ...this.highlightColor, | ||||
|         duration: hoverTransitionTime, | ||||
|         adjust: false, | ||||
|         temp: true | ||||
|       }); | ||||
|     } else { | ||||
|       this.highlight = false; | ||||
|       this.highlightColor = null; | ||||
|       if (this.hover) { | ||||
|         this.setHover(true, this.hoverColor); | ||||
|       } else { | ||||
|         if (this.sprite) { | ||||
|           this.sprite.resume(hoverTransitionTime); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     this.dirty = false; | ||||
|  | ||||
| @ -80,6 +80,9 @@ export class SearchFormComponent implements OnInit { | ||||
|         } | ||||
|         return text.trim(); | ||||
|       }), | ||||
|       tap((text) => { | ||||
|         this.stateService.searchText$.next(text); | ||||
|       }), | ||||
|       distinctUntilChanged(), | ||||
|     ); | ||||
| 
 | ||||
|  | ||||
| @ -129,6 +129,7 @@ export class StateService { | ||||
| 
 | ||||
|   markBlock$ = new BehaviorSubject<MarkBlockState>({}); | ||||
|   keyNavigation$ = new Subject<KeyboardEvent>(); | ||||
|   searchText$ = new BehaviorSubject<string>(''); | ||||
| 
 | ||||
|   blockScrolling$: Subject<boolean> = new Subject<boolean>(); | ||||
|   resetScroll$: Subject<boolean> = new Subject<boolean>(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user