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