Minor refactor for projected block transactions
This commit is contained in:
		
							parent
							
								
									ddab579111
								
							
						
					
					
						commit
						f0a2ddf57b
					
				@ -27,7 +27,7 @@ class MempoolBlocks {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getMempoolBlockDeltas(): MempoolBlockDelta[] {
 | 
			
		||||
    return this.mempoolBlockDeltas
 | 
			
		||||
    return this.mempoolBlockDeltas;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public updateMempoolBlocks(memPool: { [txid: string]: TransactionExtended }): void {
 | 
			
		||||
@ -72,11 +72,12 @@ class MempoolBlocks {
 | 
			
		||||
    logger.debug('Mempool blocks calculated in ' + time / 1000 + ' seconds');
 | 
			
		||||
 | 
			
		||||
    const { blocks, deltas } = this.calculateMempoolBlocks(memPoolArray, this.mempoolBlocks);
 | 
			
		||||
    this.mempoolBlocks = blocks
 | 
			
		||||
    this.mempoolBlockDeltas = deltas
 | 
			
		||||
    this.mempoolBlocks = blocks;
 | 
			
		||||
    this.mempoolBlockDeltas = deltas;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private calculateMempoolBlocks(transactionsSorted: TransactionExtended[], prevBlocks: MempoolBlockWithTransactions[]): { blocks: MempoolBlockWithTransactions[], deltas: MempoolBlockDelta[] } {
 | 
			
		||||
  private calculateMempoolBlocks(transactionsSorted: TransactionExtended[], prevBlocks: MempoolBlockWithTransactions[]):
 | 
			
		||||
    { blocks: MempoolBlockWithTransactions[], deltas: MempoolBlockDelta[] } {
 | 
			
		||||
    const mempoolBlocks: MempoolBlockWithTransactions[] = [];
 | 
			
		||||
    const mempoolBlockDeltas: MempoolBlockDelta[] = [];
 | 
			
		||||
    let blockWeight = 0;
 | 
			
		||||
@ -100,37 +101,41 @@ class MempoolBlocks {
 | 
			
		||||
    }
 | 
			
		||||
    // Calculate change from previous block states
 | 
			
		||||
    for (let i = 0; i < Math.max(mempoolBlocks.length, prevBlocks.length); i++) {
 | 
			
		||||
      let added: TransactionStripped[] = []
 | 
			
		||||
      let removed: string[] = []
 | 
			
		||||
      let added: TransactionStripped[] = [];
 | 
			
		||||
      let removed: string[] = [];
 | 
			
		||||
      if (mempoolBlocks[i] && !prevBlocks[i]) {
 | 
			
		||||
        added = mempoolBlocks[i].transactions
 | 
			
		||||
        added = mempoolBlocks[i].transactions;
 | 
			
		||||
      } else if (!mempoolBlocks[i] && prevBlocks[i]) {
 | 
			
		||||
        removed = prevBlocks[i].transactions.map(tx => tx.txid)
 | 
			
		||||
        removed = prevBlocks[i].transactions.map(tx => tx.txid);
 | 
			
		||||
      } else if (mempoolBlocks[i] && prevBlocks[i]) {
 | 
			
		||||
        const prevIds = {}
 | 
			
		||||
        const newIds = {}
 | 
			
		||||
        const prevIds = {};
 | 
			
		||||
        const newIds = {};
 | 
			
		||||
        prevBlocks[i].transactions.forEach(tx => {
 | 
			
		||||
          prevIds[tx.txid] = true
 | 
			
		||||
        })
 | 
			
		||||
          prevIds[tx.txid] = true;
 | 
			
		||||
        });
 | 
			
		||||
        mempoolBlocks[i].transactions.forEach(tx => {
 | 
			
		||||
          newIds[tx.txid] = true
 | 
			
		||||
        })
 | 
			
		||||
          newIds[tx.txid] = true;
 | 
			
		||||
        });
 | 
			
		||||
        prevBlocks[i].transactions.forEach(tx => {
 | 
			
		||||
          if (!newIds[tx.txid]) removed.push(tx.txid)
 | 
			
		||||
        })
 | 
			
		||||
          if (!newIds[tx.txid]) {
 | 
			
		||||
            removed.push(tx.txid);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        mempoolBlocks[i].transactions.forEach(tx => {
 | 
			
		||||
          if (!prevIds[tx.txid]) added.push(tx)
 | 
			
		||||
        })
 | 
			
		||||
          if (!prevIds[tx.txid]) {
 | 
			
		||||
            added.push(tx);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      mempoolBlockDeltas.push({
 | 
			
		||||
        added,
 | 
			
		||||
        removed
 | 
			
		||||
      })
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      blocks: mempoolBlocks,
 | 
			
		||||
      deltas: mempoolBlockDeltas
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private dataToMempoolBlocks(transactions: TransactionExtended[],
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import logger from '../logger';
 | 
			
		||||
import * as WebSocket from 'ws';
 | 
			
		||||
import { BlockExtended, TransactionExtended, WebsocketResponse, MempoolBlock, MempoolBlockDelta, OptimizedStatistic, ILoadingIndicators, IConversionRates } from '../mempool.interfaces';
 | 
			
		||||
import { BlockExtended, TransactionExtended, WebsocketResponse, MempoolBlock, MempoolBlockDelta,
 | 
			
		||||
  OptimizedStatistic, ILoadingIndicators, IConversionRates } from '../mempool.interfaces';
 | 
			
		||||
import blocks from './blocks';
 | 
			
		||||
import memPool from './mempool';
 | 
			
		||||
import backendInfo from './backend-info';
 | 
			
		||||
@ -110,15 +111,15 @@ class WebsocketHandler {
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (parsedMessage && parsedMessage['track-mempool-block'] != null) {
 | 
			
		||||
          if (parsedMessage && parsedMessage['track-mempool-block'] !== undefined) {
 | 
			
		||||
            if (Number.isInteger(parsedMessage['track-mempool-block']) && parsedMessage['track-mempool-block'] >= 0) {
 | 
			
		||||
              const index = parsedMessage['track-mempool-block'];
 | 
			
		||||
              client['track-mempool-block'] = index;
 | 
			
		||||
              const mBlocksWithTransactions = mempoolBlocks.getMempoolBlocksWithTransactions();
 | 
			
		||||
              if (mBlocksWithTransactions[index]) {
 | 
			
		||||
                response['projected-mempool-block'] = {
 | 
			
		||||
                response['projected-block-transactions'] = {
 | 
			
		||||
                  index: index,
 | 
			
		||||
                  block: mBlocksWithTransactions[index],
 | 
			
		||||
                  blockTransactions: mBlocksWithTransactions[index].transactions
 | 
			
		||||
                };
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
@ -389,7 +390,7 @@ class WebsocketHandler {
 | 
			
		||||
      if (client['track-mempool-block'] >= 0) {
 | 
			
		||||
        const index = client['track-mempool-block'];
 | 
			
		||||
        if (mBlockDeltas[index]) {
 | 
			
		||||
          response['projected-mempool-block'] = {
 | 
			
		||||
          response['projected-block-transactions'] = {
 | 
			
		||||
            index: index,
 | 
			
		||||
            delta: mBlockDeltas[index],
 | 
			
		||||
          };
 | 
			
		||||
@ -526,7 +527,7 @@ class WebsocketHandler {
 | 
			
		||||
      if (client['track-mempool-block'] >= 0) {
 | 
			
		||||
        const index = client['track-mempool-block'];
 | 
			
		||||
        if (mBlockDeltas && mBlockDeltas[index]) {
 | 
			
		||||
          response['projected-mempool-block'] = {
 | 
			
		||||
          response['projected-block-transactions'] = {
 | 
			
		||||
            index: index,
 | 
			
		||||
            delta: mBlockDeltas[index],
 | 
			
		||||
          };
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array'
 | 
			
		||||
import TxSprite from './tx-sprite'
 | 
			
		||||
import TxView from './tx-view'
 | 
			
		||||
import { TransactionStripped } from 'src/app/interfaces/websocket.interface';
 | 
			
		||||
import { Position, Square } from './sprite-types'
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
  or compacting into a smaller Float32Array when there's space to do so.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
import TxSprite from './tx-sprite'
 | 
			
		||||
import TxSprite from './tx-sprite';
 | 
			
		||||
 | 
			
		||||
export class FastVertexArray {
 | 
			
		||||
  length: number;
 | 
			
		||||
@ -19,85 +19,87 @@ export class FastVertexArray {
 | 
			
		||||
  freeSlots: number[];
 | 
			
		||||
  lastSlot: number;
 | 
			
		||||
 | 
			
		||||
  constructor (length, stride) {
 | 
			
		||||
    this.length = length
 | 
			
		||||
    this.count = 0
 | 
			
		||||
    this.stride = stride
 | 
			
		||||
    this.sprites = []
 | 
			
		||||
    this.data = new Float32Array(this.length * this.stride)
 | 
			
		||||
    this.freeSlots = []
 | 
			
		||||
    this.lastSlot = 0
 | 
			
		||||
  constructor(length, stride) {
 | 
			
		||||
    this.length = length;
 | 
			
		||||
    this.count = 0;
 | 
			
		||||
    this.stride = stride;
 | 
			
		||||
    this.sprites = [];
 | 
			
		||||
    this.data = new Float32Array(this.length * this.stride);
 | 
			
		||||
    this.freeSlots = [];
 | 
			
		||||
    this.lastSlot = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  insert (sprite: TxSprite): number {
 | 
			
		||||
    this.count++
 | 
			
		||||
  insert(sprite: TxSprite): number {
 | 
			
		||||
    this.count++;
 | 
			
		||||
 | 
			
		||||
    let position
 | 
			
		||||
    let position;
 | 
			
		||||
    if (this.freeSlots.length) {
 | 
			
		||||
      position = this.freeSlots.shift()
 | 
			
		||||
      position = this.freeSlots.shift();
 | 
			
		||||
    } else {
 | 
			
		||||
      position = this.lastSlot
 | 
			
		||||
      this.lastSlot++
 | 
			
		||||
      position = this.lastSlot;
 | 
			
		||||
      this.lastSlot++;
 | 
			
		||||
      if (this.lastSlot > this.length) {
 | 
			
		||||
        this.expand()
 | 
			
		||||
        this.expand();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.sprites[position] = sprite
 | 
			
		||||
    return position
 | 
			
		||||
    this.sprites[position] = sprite;
 | 
			
		||||
    return position;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  remove (index: number): void {
 | 
			
		||||
    this.count--
 | 
			
		||||
    this.clearData(index)
 | 
			
		||||
    this.freeSlots.push(index)
 | 
			
		||||
    this.sprites[index] = null
 | 
			
		||||
    if (this.length > 2048 && this.count < (this.length * 0.4)) this.compact()
 | 
			
		||||
  remove(index: number): void {
 | 
			
		||||
    this.count--;
 | 
			
		||||
    this.clearData(index);
 | 
			
		||||
    this.freeSlots.push(index);
 | 
			
		||||
    this.sprites[index] = null;
 | 
			
		||||
    if (this.length > 2048 && this.count < (this.length * 0.4)) {
 | 
			
		||||
      this.compact();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setData (index: number, dataChunk: number[]): void {
 | 
			
		||||
    this.data.set(dataChunk, (index * this.stride))
 | 
			
		||||
  setData(index: number, dataChunk: number[]): void {
 | 
			
		||||
    this.data.set(dataChunk, (index * this.stride));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearData (index: number): void {
 | 
			
		||||
    this.data.fill(0, (index * this.stride), ((index+1) * this.stride))
 | 
			
		||||
  clearData(index: number): void {
 | 
			
		||||
    this.data.fill(0, (index * this.stride), ((index + 1) * this.stride));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getData (index: number): Float32Array {
 | 
			
		||||
    return this.data.subarray(index, this.stride)
 | 
			
		||||
  getData(index: number): Float32Array {
 | 
			
		||||
    return this.data.subarray(index, this.stride);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  expand (): void {
 | 
			
		||||
    this.length *= 2
 | 
			
		||||
    const newData = new Float32Array(this.length * this.stride)
 | 
			
		||||
    newData.set(this.data)
 | 
			
		||||
    this.data = newData
 | 
			
		||||
  expand(): void {
 | 
			
		||||
    this.length *= 2;
 | 
			
		||||
    const newData = new Float32Array(this.length * this.stride);
 | 
			
		||||
    newData.set(this.data);
 | 
			
		||||
    this.data = newData;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  compact (): void {
 | 
			
		||||
  compact(): void {
 | 
			
		||||
    // New array length is the smallest power of 2 larger than the sprite count (but no smaller than 512)
 | 
			
		||||
    const newLength = Math.max(512, Math.pow(2, Math.ceil(Math.log2(this.count))))
 | 
			
		||||
    if (newLength != this.length) {
 | 
			
		||||
      this.length = newLength
 | 
			
		||||
      this.data = new Float32Array(this.length * this.stride)
 | 
			
		||||
      let sprite
 | 
			
		||||
      const newSprites = []
 | 
			
		||||
      let i = 0
 | 
			
		||||
      for (var index in this.sprites) {
 | 
			
		||||
        sprite = this.sprites[index]
 | 
			
		||||
    const newLength = Math.max(512, Math.pow(2, Math.ceil(Math.log2(this.count))));
 | 
			
		||||
    if (newLength !== this.length) {
 | 
			
		||||
      this.length = newLength;
 | 
			
		||||
      this.data = new Float32Array(this.length * this.stride);
 | 
			
		||||
      let sprite;
 | 
			
		||||
      const newSprites = [];
 | 
			
		||||
      let i = 0;
 | 
			
		||||
      for (const index in this.sprites) {
 | 
			
		||||
        sprite = this.sprites[index];
 | 
			
		||||
        if (sprite) {
 | 
			
		||||
          newSprites.push(sprite)
 | 
			
		||||
          sprite.moveVertexPointer(i)
 | 
			
		||||
          sprite.compile()
 | 
			
		||||
          i++
 | 
			
		||||
          newSprites.push(sprite);
 | 
			
		||||
          sprite.moveVertexPointer(i);
 | 
			
		||||
          sprite.compile();
 | 
			
		||||
          i++;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.sprites = newSprites
 | 
			
		||||
      this.freeSlots = []
 | 
			
		||||
      this.lastSlot = i
 | 
			
		||||
      this.sprites = newSprites;
 | 
			
		||||
      this.freeSlots = [];
 | 
			
		||||
      this.lastSlot = i;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getVertexData (): Float32Array {
 | 
			
		||||
    return this.data
 | 
			
		||||
  getVertexData(): Float32Array {
 | 
			
		||||
    return this.data;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, OnInit, OnDestroy,  OnChanges, ChangeDetectionStrategy, NgZone } from '@angular/core';
 | 
			
		||||
import { Component, ElementRef, ViewChild, HostListener, Input, Output, EventEmitter, OnInit,
 | 
			
		||||
  OnDestroy, OnChanges, ChangeDetectionStrategy, NgZone, AfterViewInit } from '@angular/core';
 | 
			
		||||
import { StateService } from 'src/app/services/state.service';
 | 
			
		||||
import { MempoolBlockWithTransactions, MempoolBlockDelta, TransactionStripped } from 'src/app/interfaces/websocket.interface';
 | 
			
		||||
import { Observable, Subscription, BehaviorSubject } from 'rxjs';
 | 
			
		||||
import { Subscription, BehaviorSubject } from 'rxjs';
 | 
			
		||||
import { WebsocketService } from 'src/app/services/websocket.service';
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array';
 | 
			
		||||
import BlockScene from './block-scene';
 | 
			
		||||
@ -14,9 +15,9 @@ import TxView from './tx-view';
 | 
			
		||||
  styleUrls: ['./mempool-block-overview.component.scss'],
 | 
			
		||||
  changeDetection: ChangeDetectionStrategy.OnPush,
 | 
			
		||||
})
 | 
			
		||||
export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges {
 | 
			
		||||
export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
 | 
			
		||||
  @Input() index: number;
 | 
			
		||||
  @Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>()
 | 
			
		||||
  @Output() txPreviewEvent = new EventEmitter<TransactionStripped | void>();
 | 
			
		||||
 | 
			
		||||
  @ViewChild('blockCanvas')
 | 
			
		||||
  canvas: ElementRef<HTMLCanvasElement>;
 | 
			
		||||
@ -41,33 +42,33 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
 | 
			
		||||
  constructor(
 | 
			
		||||
    public stateService: StateService,
 | 
			
		||||
    private websocketService: WebsocketService,
 | 
			
		||||
    readonly _ngZone: NgZone,
 | 
			
		||||
    readonly ngZone: NgZone,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.vertexArray = new FastVertexArray(512, TxSprite.dataSize)
 | 
			
		||||
    this.vertexArray = new FastVertexArray(512, TxSprite.dataSize);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.blockSub = this.stateService.mempoolBlock$.subscribe((block) => {
 | 
			
		||||
      this.replaceBlock(block)
 | 
			
		||||
    })
 | 
			
		||||
    this.blockSub = this.stateService.mempoolBlockTransactions$.subscribe((transactionsStripped) => {
 | 
			
		||||
      this.replaceBlock(transactionsStripped);
 | 
			
		||||
    });
 | 
			
		||||
    this.deltaSub = this.stateService.mempoolBlockDelta$.subscribe((delta) => {
 | 
			
		||||
      this.updateBlock(delta)
 | 
			
		||||
    })
 | 
			
		||||
      this.updateBlock(delta);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngAfterViewInit(): void {
 | 
			
		||||
    this.canvas.nativeElement.addEventListener("webglcontextlost", this.handleContextLost, false)
 | 
			
		||||
    this.canvas.nativeElement.addEventListener("webglcontextrestored", this.handleContextRestored, false)
 | 
			
		||||
    this.gl = this.canvas.nativeElement.getContext('webgl')
 | 
			
		||||
    this.initCanvas()
 | 
			
		||||
    this.canvas.nativeElement.addEventListener('webglcontextlost', this.handleContextLost, false);
 | 
			
		||||
    this.canvas.nativeElement.addEventListener('webglcontextrestored', this.handleContextRestored, false);
 | 
			
		||||
    this.gl = this.canvas.nativeElement.getContext('webgl');
 | 
			
		||||
    this.initCanvas();
 | 
			
		||||
 | 
			
		||||
    this.resizeCanvas()
 | 
			
		||||
    this.resizeCanvas();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnChanges(changes): void {
 | 
			
		||||
    if (changes.index) {
 | 
			
		||||
      this.clearBlock(changes.index.currentValue > changes.index.previousValue ? 'right' : 'left')
 | 
			
		||||
      this.isLoading$.next(true)
 | 
			
		||||
      this.clearBlock(changes.index.currentValue > changes.index.previousValue ? 'right' : 'left');
 | 
			
		||||
      this.isLoading$.next(true);
 | 
			
		||||
      this.websocketService.startTrackMempoolBlock(changes.index.currentValue);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -80,53 +81,56 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
 | 
			
		||||
 | 
			
		||||
  clearBlock(direction): void {
 | 
			
		||||
    if (this.scene) {
 | 
			
		||||
      this.scene.exit(direction)
 | 
			
		||||
      this.scene.exit(direction);
 | 
			
		||||
    }
 | 
			
		||||
    this.hoverTx = null
 | 
			
		||||
    this.selectedTx = null
 | 
			
		||||
    this.txPreviewEvent.emit(null)
 | 
			
		||||
    this.hoverTx = null;
 | 
			
		||||
    this.selectedTx = null;
 | 
			
		||||
    this.txPreviewEvent.emit(null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  replaceBlock(block: MempoolBlockWithTransactions): void {
 | 
			
		||||
  replaceBlock(transactionsStripped: TransactionStripped[]): void {
 | 
			
		||||
    if (!this.scene) {
 | 
			
		||||
      this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
 | 
			
		||||
      this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75,
 | 
			
		||||
        blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray });
 | 
			
		||||
    }
 | 
			
		||||
    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.enter(block.transactions, direction)
 | 
			
		||||
    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.enter(transactionsStripped, direction);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.scene.replace(block.transactions, blockMined ? 'right' : 'left')
 | 
			
		||||
      this.scene.replace(transactionsStripped, blockMined ? 'right' : 'left');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.lastBlockHeight = this.stateService.latestBlockHeight
 | 
			
		||||
    this.blockIndex = this.index
 | 
			
		||||
    this.isLoading$.next(false)
 | 
			
		||||
    this.lastBlockHeight = this.stateService.latestBlockHeight;
 | 
			
		||||
    this.blockIndex = this.index;
 | 
			
		||||
    this.isLoading$.next(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateBlock(delta: MempoolBlockDelta): void {
 | 
			
		||||
    if (!this.scene) {
 | 
			
		||||
      this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75, blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray })
 | 
			
		||||
      this.scene = new BlockScene({ width: this.displayWidth, height: this.displayHeight, resolution: 75,
 | 
			
		||||
        blockLimit: this.stateService.blockVSize, vertexArray: this.vertexArray });
 | 
			
		||||
    }
 | 
			
		||||
    const blockMined = (this.stateService.latestBlockHeight > this.lastBlockHeight)
 | 
			
		||||
    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(delta.added, direction)
 | 
			
		||||
    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(delta.added, direction);
 | 
			
		||||
    } else {
 | 
			
		||||
      this.scene.update(delta.added, delta.removed, blockMined ? 'right' : 'left', blockMined)
 | 
			
		||||
      this.scene.update(delta.added, delta.removed, blockMined ? 'right' : 'left', blockMined);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.lastBlockHeight = this.stateService.latestBlockHeight
 | 
			
		||||
    this.blockIndex = this.index
 | 
			
		||||
    this.isLoading$.next(false)
 | 
			
		||||
    this.lastBlockHeight = this.stateService.latestBlockHeight;
 | 
			
		||||
    this.blockIndex = this.index;
 | 
			
		||||
    this.isLoading$.next(false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initCanvas (): void {
 | 
			
		||||
    this.gl.clearColor(0.0, 0.0, 0.0, 0.0)
 | 
			
		||||
    this.gl.clear(this.gl.COLOR_BUFFER_BIT)
 | 
			
		||||
  initCanvas(): void {
 | 
			
		||||
    this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
    this.gl.clear(this.gl.COLOR_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
    const shaderSet = [
 | 
			
		||||
      {
 | 
			
		||||
@ -137,97 +141,101 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
 | 
			
		||||
        type: this.gl.FRAGMENT_SHADER,
 | 
			
		||||
        src: fragShaderSrc
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    this.shaderProgram = this.buildShaderProgram(shaderSet)
 | 
			
		||||
    this.shaderProgram = this.buildShaderProgram(shaderSet);
 | 
			
		||||
 | 
			
		||||
    this.gl.useProgram(this.shaderProgram)
 | 
			
		||||
    this.gl.useProgram(this.shaderProgram);
 | 
			
		||||
 | 
			
		||||
    // Set up alpha blending
 | 
			
		||||
    this.gl.enable(this.gl.BLEND);
 | 
			
		||||
    this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
 | 
			
		||||
 | 
			
		||||
    const glBuffer = this.gl.createBuffer()
 | 
			
		||||
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, glBuffer)
 | 
			
		||||
    const glBuffer = this.gl.createBuffer();
 | 
			
		||||
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, glBuffer);
 | 
			
		||||
 | 
			
		||||
    /* SET UP SHADER ATTRIBUTES */
 | 
			
		||||
    Object.keys(attribs).forEach((key, i) => {
 | 
			
		||||
      attribs[key].pointer = this.gl.getAttribLocation(this.shaderProgram, key)
 | 
			
		||||
      attribs[key].pointer = this.gl.getAttribLocation(this.shaderProgram, key);
 | 
			
		||||
      this.gl.enableVertexAttribArray(attribs[key].pointer);
 | 
			
		||||
    })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.start()
 | 
			
		||||
    this.start();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleContextLost(event): void {
 | 
			
		||||
    event.preventDefault()
 | 
			
		||||
    cancelAnimationFrame(this.animationFrameRequest)
 | 
			
		||||
    this.animationFrameRequest = null
 | 
			
		||||
    this.running = false
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
    cancelAnimationFrame(this.animationFrameRequest);
 | 
			
		||||
    this.animationFrameRequest = null;
 | 
			
		||||
    this.running = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleContextRestored(event): void {
 | 
			
		||||
    this.initCanvas()
 | 
			
		||||
    this.initCanvas();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @HostListener('window:resize', ['$event'])
 | 
			
		||||
  resizeCanvas(): void {
 | 
			
		||||
    this.displayWidth = this.canvas.nativeElement.parentElement.clientWidth
 | 
			
		||||
    this.displayHeight = this.canvas.nativeElement.parentElement.clientHeight
 | 
			
		||||
    this.canvas.nativeElement.width = this.displayWidth
 | 
			
		||||
    this.canvas.nativeElement.height = this.displayHeight
 | 
			
		||||
    if (this.gl) this.gl.viewport(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height)
 | 
			
		||||
    if (this.scene) this.scene.resize({ width: this.displayWidth, height: this.displayHeight })
 | 
			
		||||
    this.displayWidth = this.canvas.nativeElement.parentElement.clientWidth;
 | 
			
		||||
    this.displayHeight = this.canvas.nativeElement.parentElement.clientHeight;
 | 
			
		||||
    this.canvas.nativeElement.width = this.displayWidth;
 | 
			
		||||
    this.canvas.nativeElement.height = this.displayHeight;
 | 
			
		||||
    if (this.gl) {
 | 
			
		||||
      this.gl.viewport(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
 | 
			
		||||
    }
 | 
			
		||||
    if (this.scene) {
 | 
			
		||||
      this.scene.resize({ width: this.displayWidth, height: this.displayHeight });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  compileShader(src, type): WebGLShader {
 | 
			
		||||
    let shader = this.gl.createShader(type)
 | 
			
		||||
    const shader = this.gl.createShader(type);
 | 
			
		||||
 | 
			
		||||
    this.gl.shaderSource(shader, src)
 | 
			
		||||
    this.gl.compileShader(shader)
 | 
			
		||||
    this.gl.shaderSource(shader, src);
 | 
			
		||||
    this.gl.compileShader(shader);
 | 
			
		||||
 | 
			
		||||
    if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
 | 
			
		||||
      console.log(`Error compiling ${type === this.gl.VERTEX_SHADER ? "vertex" : "fragment"} shader:`)
 | 
			
		||||
      console.log(this.gl.getShaderInfoLog(shader))
 | 
			
		||||
      console.log(`Error compiling ${type === this.gl.VERTEX_SHADER ? 'vertex' : 'fragment'} shader:`);
 | 
			
		||||
      console.log(this.gl.getShaderInfoLog(shader));
 | 
			
		||||
    }
 | 
			
		||||
    return shader
 | 
			
		||||
    return shader;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buildShaderProgram(shaderInfo): WebGLProgram {
 | 
			
		||||
    let program = this.gl.createProgram()
 | 
			
		||||
    const program = this.gl.createProgram();
 | 
			
		||||
 | 
			
		||||
    shaderInfo.forEach((desc) => {
 | 
			
		||||
      let shader = this.compileShader(desc.src, desc.type)
 | 
			
		||||
      const shader = this.compileShader(desc.src, desc.type);
 | 
			
		||||
      if (shader) {
 | 
			
		||||
        this.gl.attachShader(program, shader)
 | 
			
		||||
        this.gl.attachShader(program, shader);
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.gl.linkProgram(program)
 | 
			
		||||
    this.gl.linkProgram(program);
 | 
			
		||||
 | 
			
		||||
    if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
 | 
			
		||||
      console.log("Error linking shader program:")
 | 
			
		||||
      console.log(this.gl.getProgramInfoLog(program))
 | 
			
		||||
      console.log('Error linking shader program:');
 | 
			
		||||
      console.log(this.gl.getProgramInfoLog(program));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return program
 | 
			
		||||
    return program;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  start(): void {
 | 
			
		||||
    this.running = true
 | 
			
		||||
    this._ngZone.runOutsideAngular(() => this.run())
 | 
			
		||||
    this.running = true;
 | 
			
		||||
    this.ngZone.runOutsideAngular(() => this.run());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  run (now?: DOMHighResTimeStamp): void {
 | 
			
		||||
  run(now?: DOMHighResTimeStamp): void {
 | 
			
		||||
    if (!now) {
 | 
			
		||||
      now = performance.now()
 | 
			
		||||
      now = performance.now();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* SET UP SHADER UNIFORMS */
 | 
			
		||||
    // screen dimensions
 | 
			
		||||
    this.gl.uniform2f(this.gl.getUniformLocation(this.shaderProgram, 'screenSize'), this.displayWidth, this.displayHeight)
 | 
			
		||||
    this.gl.uniform2f(this.gl.getUniformLocation(this.shaderProgram, 'screenSize'), this.displayWidth, this.displayHeight);
 | 
			
		||||
    // frame timestamp
 | 
			
		||||
    this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, 'now'), now)
 | 
			
		||||
    this.gl.uniform1f(this.gl.getUniformLocation(this.shaderProgram, 'now'), now);
 | 
			
		||||
 | 
			
		||||
    /* SET UP SHADER ATTRIBUTES */
 | 
			
		||||
    Object.keys(attribs).forEach((key, i) => {
 | 
			
		||||
@ -237,20 +245,20 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
 | 
			
		||||
      false, // never normalised
 | 
			
		||||
      stride,   // distance between values of the same attribute
 | 
			
		||||
      attribs[key].offset);  // offset of the first value
 | 
			
		||||
    })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const pointArray = this.vertexArray.getVertexData()
 | 
			
		||||
    const pointArray = this.vertexArray.getVertexData();
 | 
			
		||||
 | 
			
		||||
    if (pointArray.length) {
 | 
			
		||||
      this.gl.bufferData(this.gl.ARRAY_BUFFER, pointArray, this.gl.DYNAMIC_DRAW)
 | 
			
		||||
      this.gl.drawArrays(this.gl.TRIANGLES, 0, pointArray.length / TxSprite.vertexSize)
 | 
			
		||||
      this.gl.bufferData(this.gl.ARRAY_BUFFER, pointArray, this.gl.DYNAMIC_DRAW);
 | 
			
		||||
      this.gl.drawArrays(this.gl.TRIANGLES, 0, pointArray.length / TxSprite.vertexSize);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* LOOP */
 | 
			
		||||
    if (this.running) {
 | 
			
		||||
      if (this.animationFrameRequest) {
 | 
			
		||||
        cancelAnimationFrame(this.animationFrameRequest)
 | 
			
		||||
        this.animationFrameRequest = null
 | 
			
		||||
        cancelAnimationFrame(this.animationFrameRequest);
 | 
			
		||||
        this.animationFrameRequest = null;
 | 
			
		||||
      }
 | 
			
		||||
      this.animationFrameRequest = requestAnimationFrame(() => this.run());
 | 
			
		||||
    }
 | 
			
		||||
@ -258,49 +266,54 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
 | 
			
		||||
 | 
			
		||||
  @HostListener('click', ['$event'])
 | 
			
		||||
  onClick(event) {
 | 
			
		||||
    this.setPreviewTx(event.offsetX, event.offsetY, true)
 | 
			
		||||
    this.setPreviewTx(event.offsetX, event.offsetY, true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @HostListener('pointermove', ['$event'])
 | 
			
		||||
  onPointerMove(event) {
 | 
			
		||||
    this.setPreviewTx(event.offsetX, event.offsetY, false)
 | 
			
		||||
    this.setPreviewTx(event.offsetX, event.offsetY, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @HostListener('pointerleave', ['$event'])
 | 
			
		||||
  onPointerLeave(event) {
 | 
			
		||||
    this.setPreviewTx(-1, -1, false)
 | 
			
		||||
    this.setPreviewTx(-1, -1, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPreviewTx(x: number, y: number, clicked: boolean = false) {
 | 
			
		||||
    if (this.scene && (!this.selectedTx || clicked)) {
 | 
			
		||||
      const selected = this.scene.getTxAt({ x, y })
 | 
			
		||||
      const currentPreview = this.selectedTx || this.hoverTx
 | 
			
		||||
      const selected = this.scene.getTxAt({ x, y });
 | 
			
		||||
      const currentPreview = this.selectedTx || this.hoverTx;
 | 
			
		||||
 | 
			
		||||
      if (selected !== currentPreview) {
 | 
			
		||||
        if (currentPreview) currentPreview.setHover(false)
 | 
			
		||||
        if (currentPreview) {
 | 
			
		||||
          currentPreview.setHover(false);
 | 
			
		||||
        }
 | 
			
		||||
        if (selected) {
 | 
			
		||||
          selected.setHover(true)
 | 
			
		||||
          selected.setHover(true);
 | 
			
		||||
          this.txPreviewEvent.emit({
 | 
			
		||||
            txid: selected.txid,
 | 
			
		||||
            fee: selected.fee,
 | 
			
		||||
            vsize: selected.vsize,
 | 
			
		||||
            value: selected.value
 | 
			
		||||
          })
 | 
			
		||||
          if (clicked) this.selectedTx = selected
 | 
			
		||||
          else this.hoverTx = selected
 | 
			
		||||
          });
 | 
			
		||||
          if (clicked) {
 | 
			
		||||
            this.selectedTx = selected;
 | 
			
		||||
          } else {
 | 
			
		||||
            this.hoverTx = selected;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (clicked) {
 | 
			
		||||
            this.selectedTx = null
 | 
			
		||||
            this.selectedTx = null;
 | 
			
		||||
          }
 | 
			
		||||
          this.hoverTx = null
 | 
			
		||||
          this.txPreviewEvent.emit(null)
 | 
			
		||||
          this.hoverTx = null;
 | 
			
		||||
          this.txPreviewEvent.emit(null);
 | 
			
		||||
        }
 | 
			
		||||
      } else if (clicked) {
 | 
			
		||||
        if (selected === this.selectedTx) {
 | 
			
		||||
          this.hoverTx = this.selectedTx
 | 
			
		||||
          this.selectedTx = null
 | 
			
		||||
          this.hoverTx = this.selectedTx;
 | 
			
		||||
          this.selectedTx = null;
 | 
			
		||||
        } else {
 | 
			
		||||
          this.selectedTx = selected
 | 
			
		||||
          this.selectedTx = selected;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -317,16 +330,16 @@ const attribs = {
 | 
			
		||||
  colG: { type: 'FLOAT', count: 4, pointer: null, offset: 0 },
 | 
			
		||||
  colB: { type: 'FLOAT', count: 4, pointer: null, offset: 0 },
 | 
			
		||||
  colA: { type: 'FLOAT', count: 4, pointer: null, offset: 0 }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
// Calculate the number of bytes per vertex based on specified attributes
 | 
			
		||||
const stride = Object.values(attribs).reduce((total, attrib) => {
 | 
			
		||||
  return total + (attrib.count * 4)
 | 
			
		||||
}, 0)
 | 
			
		||||
  return total + (attrib.count * 4);
 | 
			
		||||
}, 0);
 | 
			
		||||
// Calculate vertex attribute offsets
 | 
			
		||||
for (let i = 0, offset = 0; i < Object.keys(attribs).length; i++) {
 | 
			
		||||
  let attrib = Object.values(attribs)[i]
 | 
			
		||||
  attrib.offset = offset
 | 
			
		||||
  offset += (attrib.count * 4)
 | 
			
		||||
  const attrib = Object.values(attribs)[i];
 | 
			
		||||
  attrib.offset = offset;
 | 
			
		||||
  offset += (attrib.count * 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const vertShaderSrc = `
 | 
			
		||||
@ -376,7 +389,7 @@ void main() {
 | 
			
		||||
 | 
			
		||||
  vColor = vec4(red, green, blue, alpha);
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
const fragShaderSrc = `
 | 
			
		||||
varying lowp vec4 vColor;
 | 
			
		||||
@ -386,4 +399,4 @@ void main() {
 | 
			
		||||
  // premultiply alpha
 | 
			
		||||
  gl_FragColor.rgb *= gl_FragColor.a;
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,18 @@
 | 
			
		||||
export type Position = {
 | 
			
		||||
  x: number,
 | 
			
		||||
  y: number,
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Square = Position & {
 | 
			
		||||
  s?: number
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Color = {
 | 
			
		||||
  r: number,
 | 
			
		||||
  g: number,
 | 
			
		||||
  b: number,
 | 
			
		||||
  a: number,
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type InterpolatedAttribute = {
 | 
			
		||||
  a: number,
 | 
			
		||||
@ -20,9 +20,9 @@ export type InterpolatedAttribute = {
 | 
			
		||||
  t: number,
 | 
			
		||||
  v: number,
 | 
			
		||||
  d: number
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Update = Position & { s: number } & Color
 | 
			
		||||
export type Update = Position & { s: number } & Color;
 | 
			
		||||
 | 
			
		||||
export type Attributes = {
 | 
			
		||||
  x: InterpolatedAttribute,
 | 
			
		||||
@ -32,7 +32,7 @@ export type Attributes = {
 | 
			
		||||
  g: InterpolatedAttribute,
 | 
			
		||||
  b: InterpolatedAttribute,
 | 
			
		||||
  a: InterpolatedAttribute
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type OptionalAttributes = {
 | 
			
		||||
  x?: InterpolatedAttribute,
 | 
			
		||||
@ -42,7 +42,8 @@ export type OptionalAttributes = {
 | 
			
		||||
  g?: InterpolatedAttribute,
 | 
			
		||||
  b?: InterpolatedAttribute,
 | 
			
		||||
  a?: InterpolatedAttribute
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type SpriteUpdateParams = {
 | 
			
		||||
  x?: number,
 | 
			
		||||
  y?: number,
 | 
			
		||||
@ -56,7 +57,7 @@ export type SpriteUpdateParams = {
 | 
			
		||||
  minDuration?: number,
 | 
			
		||||
  adjust?: boolean,
 | 
			
		||||
  temp?: boolean
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type ViewUpdateParams = {
 | 
			
		||||
  display: {
 | 
			
		||||
@ -70,4 +71,4 @@ export type ViewUpdateParams = {
 | 
			
		||||
  jitter?: number,
 | 
			
		||||
  state?: string,
 | 
			
		||||
  adjust?: boolean
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,14 @@
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array'
 | 
			
		||||
import { InterpolatedAttribute, Attributes, OptionalAttributes, SpriteUpdateParams, Update } from './sprite-types'
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array';
 | 
			
		||||
import { InterpolatedAttribute, Attributes, OptionalAttributes, SpriteUpdateParams, Update } from './sprite-types';
 | 
			
		||||
 | 
			
		||||
const attribKeys = ['a', 'b', 't', 'v']
 | 
			
		||||
const updateKeys = ['x', 'y', 's', 'r', 'g', 'b', 'a']
 | 
			
		||||
const attribKeys = ['a', 'b', 't', 'v'];
 | 
			
		||||
const updateKeys = ['x', 'y', 's', 'r', 'g', 'b', 'a'];
 | 
			
		||||
 | 
			
		||||
export default class TxSprite {
 | 
			
		||||
  static vertexSize = 30;
 | 
			
		||||
  static vertexCount = 6;
 | 
			
		||||
  static dataSize: number = (30 * 6);
 | 
			
		||||
 | 
			
		||||
  vertexArray: FastVertexArray;
 | 
			
		||||
  vertexPointer: number;
 | 
			
		||||
  vertexData: number[];
 | 
			
		||||
@ -12,17 +16,14 @@ export default class TxSprite {
 | 
			
		||||
  attributes: Attributes;
 | 
			
		||||
  tempAttributes: OptionalAttributes;
 | 
			
		||||
 | 
			
		||||
  static vertexSize: number = 30;
 | 
			
		||||
  static vertexCount: number = 6;
 | 
			
		||||
  static dataSize: number = (30*6);
 | 
			
		||||
 | 
			
		||||
  constructor(params: SpriteUpdateParams, vertexArray: FastVertexArray) {
 | 
			
		||||
    const offsetTime = params.start
 | 
			
		||||
    this.vertexArray = vertexArray
 | 
			
		||||
    this.vertexData = Array(VI.length).fill(0)
 | 
			
		||||
    const offsetTime = params.start;
 | 
			
		||||
    this.vertexArray = vertexArray;
 | 
			
		||||
    this.vertexData = Array(VI.length).fill(0);
 | 
			
		||||
    this.updateMap = {
 | 
			
		||||
      x: 0, y: 0, s: 0, r: 0, g: 0, b: 0, a: 0
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.attributes = {
 | 
			
		||||
      x: { a: params.x, b: params.x, t: offsetTime, v: 0, d: 0 },
 | 
			
		||||
@ -32,35 +33,36 @@ export default class TxSprite {
 | 
			
		||||
      g: { a: params.g, b: params.g, t: offsetTime, v: 0, d: 0 },
 | 
			
		||||
      b: { a: params.b, b: params.b, t: offsetTime, v: 0, d: 0 },
 | 
			
		||||
      a: { a: params.a, b: params.a, t: offsetTime, v: 0, d: 0 },
 | 
			
		||||
    }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Used to temporarily modify the sprite, so that the base view can be resumed later
 | 
			
		||||
    this.tempAttributes = null
 | 
			
		||||
    this.tempAttributes = null;
 | 
			
		||||
 | 
			
		||||
    this.vertexPointer = this.vertexArray.insert(this)
 | 
			
		||||
    this.vertexPointer = this.vertexArray.insert(this);
 | 
			
		||||
 | 
			
		||||
    this.compile()
 | 
			
		||||
    this.compile();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private interpolateAttributes (updateMap: Update, attributes: OptionalAttributes, offsetTime: DOMHighResTimeStamp, v: number, duration: number, minDuration: number, adjust: boolean): void {
 | 
			
		||||
  private interpolateAttributes(updateMap: Update, attributes: OptionalAttributes, offsetTime: DOMHighResTimeStamp, v: number,
 | 
			
		||||
                                duration: number, minDuration: number, adjust: boolean): void {
 | 
			
		||||
    for (const key of Object.keys(updateMap)) {
 | 
			
		||||
      // for each non-null attribute:
 | 
			
		||||
      if (updateMap[key] != null) {
 | 
			
		||||
        // calculate current interpolated value, and set as 'from'
 | 
			
		||||
        interpolateAttributeStart(attributes[key], offsetTime)
 | 
			
		||||
        interpolateAttributeStart(attributes[key], offsetTime);
 | 
			
		||||
        // update start time
 | 
			
		||||
        attributes[key].t = offsetTime
 | 
			
		||||
        attributes[key].t = offsetTime;
 | 
			
		||||
 | 
			
		||||
        if (!adjust || (duration && attributes[key].d == 0)) {
 | 
			
		||||
          attributes[key].v = v
 | 
			
		||||
          attributes[key].d = duration
 | 
			
		||||
        if (!adjust || (duration && attributes[key].d === 0)) {
 | 
			
		||||
          attributes[key].v = v;
 | 
			
		||||
          attributes[key].d = duration;
 | 
			
		||||
        } else if (minDuration > attributes[key].d) {
 | 
			
		||||
          // enforce minimum transition duration
 | 
			
		||||
          attributes[key].v = 1 / minDuration
 | 
			
		||||
          attributes[key].d = minDuration
 | 
			
		||||
          attributes[key].v = 1 / minDuration;
 | 
			
		||||
          attributes[key].d = minDuration;
 | 
			
		||||
        }
 | 
			
		||||
        // set 'to' to target value
 | 
			
		||||
        attributes[key].b = updateMap[key]
 | 
			
		||||
        attributes[key].b = updateMap[key];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -75,137 +77,139 @@ export default class TxSprite {
 | 
			
		||||
      minDuration: minimum remaining transition duration when adjust = true
 | 
			
		||||
      temp: if true, this update is only temporary (can be reversed with 'resume')
 | 
			
		||||
  */
 | 
			
		||||
  update (params: SpriteUpdateParams): void {
 | 
			
		||||
    const offsetTime = params.start || performance.now()
 | 
			
		||||
    const v = params.duration > 0 ? (1 / params.duration) : 0
 | 
			
		||||
  update(params: SpriteUpdateParams): void {
 | 
			
		||||
    const offsetTime = params.start || performance.now();
 | 
			
		||||
    const v = params.duration > 0 ? (1 / params.duration) : 0;
 | 
			
		||||
 | 
			
		||||
    updateKeys.forEach(key => {
 | 
			
		||||
      this.updateMap[key] = params[key]
 | 
			
		||||
    })
 | 
			
		||||
      this.updateMap[key] = params[key];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const isModified = !!this.tempAttributes
 | 
			
		||||
    const isModified = !!this.tempAttributes;
 | 
			
		||||
    if (!params.temp) {
 | 
			
		||||
      this.interpolateAttributes(this.updateMap, this.attributes, offsetTime, v, params.duration, params.minDuration, params.adjust)
 | 
			
		||||
      this.interpolateAttributes(this.updateMap, this.attributes, offsetTime, v, params.duration, params.minDuration, params.adjust);
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!isModified) { // set up tempAttributes
 | 
			
		||||
        this.tempAttributes = {}
 | 
			
		||||
        this.tempAttributes = {};
 | 
			
		||||
        for (const key of Object.keys(this.updateMap)) {
 | 
			
		||||
          if (this.updateMap[key] != null) {
 | 
			
		||||
            this.tempAttributes[key] = { ...this.attributes[key] }
 | 
			
		||||
            this.tempAttributes[key] = { ...this.attributes[key] };
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.interpolateAttributes(this.updateMap, this.tempAttributes, offsetTime, v, params.duration, params.minDuration, params.adjust)
 | 
			
		||||
      this.interpolateAttributes(this.updateMap, this.tempAttributes, offsetTime, v, params.duration, params.minDuration, params.adjust);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.compile()
 | 
			
		||||
    this.compile();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Transition back from modified state back to base attributes
 | 
			
		||||
  resume (duration: number, start : DOMHighResTimeStamp = performance.now()): void {
 | 
			
		||||
  resume(duration: number, start: DOMHighResTimeStamp = performance.now()): void {
 | 
			
		||||
    // If not in modified state, there's nothing to do
 | 
			
		||||
    if (!this.tempAttributes) return
 | 
			
		||||
    if (!this.tempAttributes) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const offsetTime = start
 | 
			
		||||
    const v = duration > 0 ? (1 / duration) : 0
 | 
			
		||||
    const offsetTime = start;
 | 
			
		||||
    const v = duration > 0 ? (1 / duration) : 0;
 | 
			
		||||
 | 
			
		||||
    for (const key of Object.keys(this.tempAttributes)) {
 | 
			
		||||
      // If this base attribute is static (fixed or post-transition), transition smoothly back
 | 
			
		||||
      if (this.attributes[key].v == 0 || (this.attributes[key].t + this.attributes[key].d) <= start) {
 | 
			
		||||
      if (this.attributes[key].v === 0 || (this.attributes[key].t + this.attributes[key].d) <= start) {
 | 
			
		||||
        // calculate current interpolated value, and set as 'from'
 | 
			
		||||
        interpolateAttributeStart(this.tempAttributes[key], offsetTime)
 | 
			
		||||
        this.attributes[key].a = this.tempAttributes[key].a
 | 
			
		||||
        this.attributes[key].t = offsetTime
 | 
			
		||||
        this.attributes[key].v = v
 | 
			
		||||
        this.attributes[key].d = duration
 | 
			
		||||
        interpolateAttributeStart(this.tempAttributes[key], offsetTime);
 | 
			
		||||
        this.attributes[key].a = this.tempAttributes[key].a;
 | 
			
		||||
        this.attributes[key].t = offsetTime;
 | 
			
		||||
        this.attributes[key].v = v;
 | 
			
		||||
        this.attributes[key].d = duration;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.tempAttributes = null
 | 
			
		||||
    this.tempAttributes = null;
 | 
			
		||||
 | 
			
		||||
    this.compile()
 | 
			
		||||
    this.compile();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Write current state into the graphics vertex array for rendering
 | 
			
		||||
  compile (): void {
 | 
			
		||||
    let attributes = this.attributes
 | 
			
		||||
  compile(): void {
 | 
			
		||||
    let attributes = this.attributes;
 | 
			
		||||
    if (this.tempAttributes) {
 | 
			
		||||
      attributes = {
 | 
			
		||||
        ...this.attributes,
 | 
			
		||||
        ...this.tempAttributes
 | 
			
		||||
      }
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    const size = attributes.s
 | 
			
		||||
    const size = attributes.s;
 | 
			
		||||
 | 
			
		||||
    // update vertex data in place
 | 
			
		||||
    // ugly, but avoids overhead of allocating large temporary arrays
 | 
			
		||||
    const vertexStride = VI.length + 2
 | 
			
		||||
    const vertexStride = VI.length + 2;
 | 
			
		||||
    for (let vertex = 0; vertex < 6; vertex++) {
 | 
			
		||||
      this.vertexData[vertex * vertexStride] = vertexOffsetFactors[vertex][0]
 | 
			
		||||
      this.vertexData[(vertex * vertexStride) + 1] = vertexOffsetFactors[vertex][1]
 | 
			
		||||
      this.vertexData[vertex * vertexStride] = vertexOffsetFactors[vertex][0];
 | 
			
		||||
      this.vertexData[(vertex * vertexStride) + 1] = vertexOffsetFactors[vertex][1];
 | 
			
		||||
      for (let step = 0; step < VI.length; step++) {
 | 
			
		||||
        // components of each field in the vertex array are defined by an entry in VI:
 | 
			
		||||
        // VI[i].a is the attribute, VI[i].f is the inner field, VI[i].offA and VI[i].offB are offset factors
 | 
			
		||||
        this.vertexData[(vertex * vertexStride) + step + 2] = attributes[VI[step].a][VI[step].f]
 | 
			
		||||
        this.vertexData[(vertex * vertexStride) + step + 2] = attributes[VI[step].a][VI[step].f];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.vertexArray.setData(this.vertexPointer, this.vertexData)
 | 
			
		||||
    this.vertexArray.setData(this.vertexPointer, this.vertexData);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  moveVertexPointer (index: number): void {
 | 
			
		||||
    this.vertexPointer = index
 | 
			
		||||
  moveVertexPointer(index: number): void {
 | 
			
		||||
    this.vertexPointer = index;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  destroy (): void {
 | 
			
		||||
    this.vertexArray.remove(this.vertexPointer)
 | 
			
		||||
    this.vertexPointer = null
 | 
			
		||||
  destroy(): void {
 | 
			
		||||
    this.vertexArray.remove(this.vertexPointer);
 | 
			
		||||
    this.vertexPointer = null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// expects 0 <= x <= 1
 | 
			
		||||
function smootherstep(x: number): number {
 | 
			
		||||
  let ix = 1 - x;
 | 
			
		||||
  x = x * x
 | 
			
		||||
  return x / (x + ix * ix)
 | 
			
		||||
  const ix = 1 - x;
 | 
			
		||||
  x = x * x;
 | 
			
		||||
  return x / (x + ix * ix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function interpolateAttributeStart(attribute: InterpolatedAttribute, start: DOMHighResTimeStamp): void {
 | 
			
		||||
  if (attribute.v == 0 || (attribute.t + attribute.d) <= start) {
 | 
			
		||||
  if (attribute.v === 0 || (attribute.t + attribute.d) <= start) {
 | 
			
		||||
    // transition finished, next transition starts from current end state
 | 
			
		||||
    // (clamp to 1)
 | 
			
		||||
    attribute.a = attribute.b
 | 
			
		||||
    attribute.v = 0
 | 
			
		||||
    attribute.d = 0
 | 
			
		||||
    attribute.a = attribute.b;
 | 
			
		||||
    attribute.v = 0;
 | 
			
		||||
    attribute.d = 0;
 | 
			
		||||
  } else if (attribute.t > start) {
 | 
			
		||||
    // transition not started
 | 
			
		||||
    // (clamp to 0)
 | 
			
		||||
  } else {
 | 
			
		||||
    // transition in progress
 | 
			
		||||
    // (interpolate)
 | 
			
		||||
    let progress = (start - attribute.t)
 | 
			
		||||
    let delta = smootherstep(progress / attribute.d)
 | 
			
		||||
    attribute.a = attribute.a + (delta * (attribute.b - attribute.a))
 | 
			
		||||
    attribute.d = attribute.d - progress
 | 
			
		||||
    attribute.v = 1 / attribute.d
 | 
			
		||||
    const progress = (start - attribute.t);
 | 
			
		||||
    const delta = smootherstep(progress / attribute.d);
 | 
			
		||||
    attribute.a = attribute.a + (delta * (attribute.b - attribute.a));
 | 
			
		||||
    attribute.d = attribute.d - progress;
 | 
			
		||||
    attribute.v = 1 / attribute.d;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const vertexOffsetFactors = [
 | 
			
		||||
  [0,0],
 | 
			
		||||
  [1,1],
 | 
			
		||||
  [1,0],
 | 
			
		||||
  [0,0],
 | 
			
		||||
  [1,1],
 | 
			
		||||
  [0,1]
 | 
			
		||||
]
 | 
			
		||||
  [0, 0],
 | 
			
		||||
  [1, 1],
 | 
			
		||||
  [1, 0],
 | 
			
		||||
  [0, 0],
 | 
			
		||||
  [1, 1],
 | 
			
		||||
  [0, 1]
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const VI = []
 | 
			
		||||
const VI = [];
 | 
			
		||||
updateKeys.forEach((attribute, aIndex) => {
 | 
			
		||||
  attribKeys.forEach(field => {
 | 
			
		||||
    VI.push({
 | 
			
		||||
      a: attribute,
 | 
			
		||||
      f: field
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,14 @@
 | 
			
		||||
import TxSprite from './tx-sprite'
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array'
 | 
			
		||||
import TxSprite from './tx-sprite';
 | 
			
		||||
import { FastVertexArray } from './fast-vertex-array';
 | 
			
		||||
import { TransactionStripped } from 'src/app/interfaces/websocket.interface';
 | 
			
		||||
import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from './sprite-types'
 | 
			
		||||
import { SpriteUpdateParams, Square, Color, ViewUpdateParams } from './sprite-types';
 | 
			
		||||
import { feeLevels, mempoolFeeColors } from 'src/app/app.constants';
 | 
			
		||||
 | 
			
		||||
const hoverTransitionTime = 300
 | 
			
		||||
const defaultHoverColor = hexToColor('1bd8f4')
 | 
			
		||||
const hoverTransitionTime = 300;
 | 
			
		||||
const defaultHoverColor = hexToColor('1bd8f4');
 | 
			
		||||
 | 
			
		||||
// convert from this class's update format to TxSprite's update format
 | 
			
		||||
function toSpriteUpdate(params : ViewUpdateParams): SpriteUpdateParams {
 | 
			
		||||
function toSpriteUpdate(params: ViewUpdateParams): SpriteUpdateParams {
 | 
			
		||||
  return {
 | 
			
		||||
    start: (params.start || performance.now()) + (params.delay || 0),
 | 
			
		||||
    duration: params.duration,
 | 
			
		||||
@ -16,7 +16,7 @@ function toSpriteUpdate(params : ViewUpdateParams): SpriteUpdateParams {
 | 
			
		||||
    ...params.display.position,
 | 
			
		||||
    ...params.display.color,
 | 
			
		||||
    adjust: params.adjust
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default class TxView implements TransactionStripped {
 | 
			
		||||
@ -33,41 +33,43 @@ export default class TxView implements TransactionStripped {
 | 
			
		||||
  hoverColor: Color | void;
 | 
			
		||||
 | 
			
		||||
  screenPosition: Square;
 | 
			
		||||
  gridPosition : Square | void;
 | 
			
		||||
  gridPosition: Square | void;
 | 
			
		||||
 | 
			
		||||
  dirty: boolean;
 | 
			
		||||
 | 
			
		||||
  constructor (tx: TransactionStripped, vertexArray: FastVertexArray) {
 | 
			
		||||
    this.txid = tx.txid
 | 
			
		||||
    this.fee = tx.fee
 | 
			
		||||
    this.vsize = tx.vsize
 | 
			
		||||
    this.value = tx.value
 | 
			
		||||
    this.feerate = tx.fee / tx.vsize
 | 
			
		||||
    this.initialised = false
 | 
			
		||||
    this.vertexArray = vertexArray
 | 
			
		||||
  constructor(tx: TransactionStripped, vertexArray: FastVertexArray) {
 | 
			
		||||
    this.txid = tx.txid;
 | 
			
		||||
    this.fee = tx.fee;
 | 
			
		||||
    this.vsize = tx.vsize;
 | 
			
		||||
    this.value = tx.value;
 | 
			
		||||
    this.feerate = tx.fee / tx.vsize;
 | 
			
		||||
    this.initialised = false;
 | 
			
		||||
    this.vertexArray = vertexArray;
 | 
			
		||||
 | 
			
		||||
    this.hover = false
 | 
			
		||||
    this.hover = false;
 | 
			
		||||
 | 
			
		||||
    this.screenPosition = { x: 0, y: 0, s: 0 }
 | 
			
		||||
    this.screenPosition = { x: 0, y: 0, s: 0 };
 | 
			
		||||
 | 
			
		||||
    this.dirty = true
 | 
			
		||||
    this.dirty = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  destroy (): void {
 | 
			
		||||
  destroy(): void {
 | 
			
		||||
    if (this.sprite) {
 | 
			
		||||
      this.sprite.destroy()
 | 
			
		||||
      this.sprite = null
 | 
			
		||||
      this.initialised = false
 | 
			
		||||
      this.sprite.destroy();
 | 
			
		||||
      this.sprite = null;
 | 
			
		||||
      this.initialised = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  applyGridPosition (position: Square): void {
 | 
			
		||||
    if (!this.gridPosition) this.gridPosition = { x: 0, y: 0, s: 0 }
 | 
			
		||||
    if (this.gridPosition.x != position.x || this.gridPosition.y != position.y || this.gridPosition.s != position.s) {
 | 
			
		||||
      this.gridPosition.x = position.x
 | 
			
		||||
      this.gridPosition.y = position.y
 | 
			
		||||
      this.gridPosition.s = position.s
 | 
			
		||||
      this.dirty = true
 | 
			
		||||
  applyGridPosition(position: Square): void {
 | 
			
		||||
    if (!this.gridPosition) {
 | 
			
		||||
      this.gridPosition = { x: 0, y: 0, s: 0 };
 | 
			
		||||
    }
 | 
			
		||||
    if (this.gridPosition.x !== position.x || this.gridPosition.y !== position.y || this.gridPosition.s !== position.s) {
 | 
			
		||||
      this.gridPosition.x = position.x;
 | 
			
		||||
      this.gridPosition.y = position.y;
 | 
			
		||||
      this.gridPosition.s = position.s;
 | 
			
		||||
      this.dirty = true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -81,15 +83,17 @@ export default class TxView implements TransactionStripped {
 | 
			
		||||
    jitter: if set, adds a random amount to the delay,
 | 
			
		||||
    adjust: if true, modify an in-progress transition instead of replacing it
 | 
			
		||||
  */
 | 
			
		||||
  update (params: ViewUpdateParams): void {
 | 
			
		||||
    if (params.jitter) params.delay += (Math.random() * params.jitter)
 | 
			
		||||
  update(params: ViewUpdateParams): void {
 | 
			
		||||
    if (params.jitter) {
 | 
			
		||||
      params.delay += (Math.random() * params.jitter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!this.initialised || !this.sprite) {
 | 
			
		||||
      this.initialised = true
 | 
			
		||||
      this.initialised = true;
 | 
			
		||||
      this.sprite = new TxSprite(
 | 
			
		||||
        toSpriteUpdate(params),
 | 
			
		||||
        this.vertexArray
 | 
			
		||||
      )
 | 
			
		||||
      );
 | 
			
		||||
      // apply any pending hover event
 | 
			
		||||
      if (this.hover) {
 | 
			
		||||
        this.sprite.update({
 | 
			
		||||
@ -97,48 +101,50 @@ export default class TxView implements TransactionStripped {
 | 
			
		||||
          duration: hoverTransitionTime,
 | 
			
		||||
          adjust: false,
 | 
			
		||||
          temp: true
 | 
			
		||||
        })
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      this.sprite.update(
 | 
			
		||||
        toSpriteUpdate(params)
 | 
			
		||||
      )
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    this.dirty = false
 | 
			
		||||
    this.dirty = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Temporarily override the tx color
 | 
			
		||||
  setHover (hoverOn: boolean, color: Color | void = defaultHoverColor): void {
 | 
			
		||||
  setHover(hoverOn: boolean, color: Color | void = defaultHoverColor): void {
 | 
			
		||||
    if (hoverOn) {
 | 
			
		||||
      this.hover = true
 | 
			
		||||
      this.hoverColor = color
 | 
			
		||||
      this.hover = true;
 | 
			
		||||
      this.hoverColor = color;
 | 
			
		||||
 | 
			
		||||
      this.sprite.update({
 | 
			
		||||
        ...this.hoverColor,
 | 
			
		||||
        duration: hoverTransitionTime,
 | 
			
		||||
        adjust: false,
 | 
			
		||||
        temp: true
 | 
			
		||||
      })
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.hover = false
 | 
			
		||||
      this.hoverColor = null
 | 
			
		||||
      if (this.sprite) this.sprite.resume(hoverTransitionTime)
 | 
			
		||||
      this.hover = false;
 | 
			
		||||
      this.hoverColor = null;
 | 
			
		||||
      if (this.sprite) {
 | 
			
		||||
        this.sprite.resume(hoverTransitionTime);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    this.dirty = false
 | 
			
		||||
    this.dirty = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getColor (): Color {
 | 
			
		||||
  getColor(): Color {
 | 
			
		||||
    let feeLevelIndex = feeLevels.slice().reverse().findIndex((feeLvl) => (this.feerate || 1) >= feeLvl);
 | 
			
		||||
    feeLevelIndex = feeLevelIndex >= 0 ? feeLevels.length - feeLevelIndex : feeLevelIndex;
 | 
			
		||||
    return hexToColor(mempoolFeeColors[feeLevelIndex - 1] || mempoolFeeColors[mempoolFeeColors.length - 1])
 | 
			
		||||
    return hexToColor(mempoolFeeColors[feeLevelIndex - 1] || mempoolFeeColors[mempoolFeeColors.length - 1]);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hexToColor (hex: string): Color {
 | 
			
		||||
function hexToColor(hex: string): Color {
 | 
			
		||||
  return {
 | 
			
		||||
    r: parseInt(hex.slice(0,2), 16) / 255,
 | 
			
		||||
    g: parseInt(hex.slice(2,4), 16) / 255,
 | 
			
		||||
    b: parseInt(hex.slice(4,6), 16) / 255,
 | 
			
		||||
    r: parseInt(hex.slice(0, 2), 16) / 255,
 | 
			
		||||
    g: parseInt(hex.slice(2, 4), 16) / 255,
 | 
			
		||||
    b: parseInt(hex.slice(4, 6), 16) / 255,
 | 
			
		||||
    a: 1
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,7 @@ export class StateService {
 | 
			
		||||
  bsqPrice$ = new ReplaySubject<number>(1);
 | 
			
		||||
  mempoolInfo$ = new ReplaySubject<MempoolInfo>(1);
 | 
			
		||||
  mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
 | 
			
		||||
  mempoolBlock$ = new Subject<MempoolBlockWithTransactions>();
 | 
			
		||||
  mempoolBlockTransactions$ = new Subject<TransactionStripped[]>();
 | 
			
		||||
  mempoolBlockDelta$ = new Subject<MempoolBlockDelta>();
 | 
			
		||||
  txReplaced$ = new Subject<ReplacedTransaction>();
 | 
			
		||||
  utxoSpent$ = new Subject<object>();
 | 
			
		||||
 | 
			
		||||
@ -309,12 +309,12 @@ export class WebsocketService {
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (response['projected-mempool-block']) {
 | 
			
		||||
      if (response['projected-mempool-block'].index == this.trackingMempoolBlock) {
 | 
			
		||||
        if (response['projected-mempool-block'].block) {
 | 
			
		||||
          this.stateService.mempoolBlock$.next(response['projected-mempool-block'].block);
 | 
			
		||||
        } else if (response['projected-mempool-block'].delta) {
 | 
			
		||||
          this.stateService.mempoolBlockDelta$.next(response['projected-mempool-block'].delta);
 | 
			
		||||
    if (response['projected-block-transactions']) {
 | 
			
		||||
      if (response['projected-block-transactions'].index == this.trackingMempoolBlock) {
 | 
			
		||||
        if (response['projected-block-transactions'].blockTransactions) {
 | 
			
		||||
          this.stateService.mempoolBlockTransactions$.next(response['projected-block-transactions'].blockTransactions);
 | 
			
		||||
        } else if (response['projected-block-transactions'].delta) {
 | 
			
		||||
          this.stateService.mempoolBlockDelta$.next(response['projected-block-transactions'].delta);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user