mirror of
https://github.com/mempool/mempool.git
synced 2024-11-19 09:52:14 +01:00
Merge pull request #5445 from mempool/mononaut/persistent-goggles
Persist mempool block visualization between pages
This commit is contained in:
commit
8c2d0e1d6c
@ -198,7 +198,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
}
|
||||
|
||||
// initialize the scene without any entry transition
|
||||
setup(transactions: TransactionStripped[]): void {
|
||||
setup(transactions: TransactionStripped[], sort: boolean = false): void {
|
||||
const filtersAvailable = transactions.reduce((flagSet, tx) => flagSet || tx.flags > 0, false);
|
||||
if (filtersAvailable !== this.filtersAvailable) {
|
||||
this.setFilterFlags();
|
||||
@ -206,7 +206,7 @@ export class BlockOverviewGraphComponent implements AfterViewInit, OnDestroy, On
|
||||
this.filtersAvailable = filtersAvailable;
|
||||
if (this.scene) {
|
||||
this.clearUpdateQueue();
|
||||
this.scene.setup(transactions);
|
||||
this.scene.setup(transactions, sort);
|
||||
this.readyNextFrame = true;
|
||||
this.start();
|
||||
this.updateSearchHighlight();
|
||||
|
@ -88,16 +88,19 @@ export default class BlockScene {
|
||||
}
|
||||
|
||||
// set up the scene with an initial set of transactions, without any transition animation
|
||||
setup(txs: TransactionStripped[]) {
|
||||
setup(txs: TransactionStripped[], sort: boolean = false) {
|
||||
// clean up any old transactions
|
||||
Object.values(this.txs).forEach(tx => {
|
||||
tx.destroy();
|
||||
delete this.txs[tx.txid];
|
||||
});
|
||||
this.layout = new BlockLayout({ width: this.gridWidth, height: this.gridHeight });
|
||||
txs.forEach(tx => {
|
||||
const txView = new TxView(tx, this);
|
||||
this.txs[tx.txid] = txView;
|
||||
let txViews = txs.map(tx => new TxView(tx, this));
|
||||
if (sort) {
|
||||
txViews = txViews.sort(feeRateDescending);
|
||||
}
|
||||
txViews.forEach(txView => {
|
||||
this.txs[txView.txid] = txView;
|
||||
this.place(txView);
|
||||
this.saveGridToScreenPosition(txView);
|
||||
this.applyTxUpdate(txView, {
|
||||
|
@ -31,7 +31,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
|
||||
lastBlockHeight: number;
|
||||
blockIndex: number;
|
||||
isLoading$ = new BehaviorSubject<boolean>(true);
|
||||
isLoading$ = new BehaviorSubject<boolean>(false);
|
||||
timeLtrSubscription: Subscription;
|
||||
timeLtr: boolean;
|
||||
chainDirection: string = 'right';
|
||||
@ -95,6 +95,7 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
}
|
||||
}
|
||||
this.updateBlock({
|
||||
block: this.blockIndex,
|
||||
removed,
|
||||
changed,
|
||||
added
|
||||
@ -110,8 +111,11 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
if (this.blockGraph) {
|
||||
this.blockGraph.clear(changes.index.currentValue > changes.index.previousValue ? this.chainDirection : this.poolDirection);
|
||||
}
|
||||
this.isLoading$.next(true);
|
||||
this.websocketService.startTrackMempoolBlock(changes.index.currentValue);
|
||||
if (!this.websocketService.startTrackMempoolBlock(changes.index.currentValue) && this.stateService.mempoolBlockState && this.stateService.mempoolBlockState.block === changes.index.currentValue) {
|
||||
this.resumeBlock(Object.values(this.stateService.mempoolBlockState.transactions));
|
||||
} else {
|
||||
this.isLoading$.next(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,6 +157,19 @@ export class MempoolBlockOverviewComponent implements OnInit, OnDestroy, OnChang
|
||||
this.isLoading$.next(false);
|
||||
}
|
||||
|
||||
resumeBlock(transactionsStripped: TransactionStripped[]): void {
|
||||
if (this.blockGraph) {
|
||||
this.firstLoad = false;
|
||||
this.blockGraph.setup(transactionsStripped, true);
|
||||
this.blockIndex = this.index;
|
||||
this.isLoading$.next(false);
|
||||
} else {
|
||||
requestAnimationFrame(() => {
|
||||
this.resumeBlock(transactionsStripped);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onTxClick(event: { tx: TransactionStripped, keyModifier: boolean }): void {
|
||||
const url = new RelativeUrlPipe(this.stateService).transform(`/tx/${event.tx.txid}`);
|
||||
if (!event.keyModifier) {
|
||||
|
@ -71,7 +71,7 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
);
|
||||
|
||||
this.mempoolBlockTransactions$ = this.stateService.liveMempoolBlockTransactions$.pipe(map(txMap => Object.values(txMap)));
|
||||
this.mempoolBlockTransactions$ = this.stateService.liveMempoolBlockTransactions$.pipe(map(({transactions}) => Object.values(transactions)));
|
||||
|
||||
this.network$ = this.stateService.networkChanged$;
|
||||
}
|
||||
|
@ -72,11 +72,13 @@ export interface MempoolBlockWithTransactions extends MempoolBlock {
|
||||
}
|
||||
|
||||
export interface MempoolBlockDelta {
|
||||
block: number;
|
||||
added: TransactionStripped[];
|
||||
removed: string[];
|
||||
changed: { txid: string, rate: number, flags: number, acc: boolean }[];
|
||||
}
|
||||
export interface MempoolBlockState {
|
||||
block: number;
|
||||
transactions: TransactionStripped[];
|
||||
}
|
||||
export type MempoolBlockUpdate = MempoolBlockDelta | MempoolBlockState;
|
||||
|
@ -5,7 +5,7 @@ import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, Mempool
|
||||
import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { filter, map, scan, shareReplay } from 'rxjs/operators';
|
||||
import { filter, map, scan, share, shareReplay } from 'rxjs/operators';
|
||||
import { StorageService } from './storage.service';
|
||||
import { hasTouchScreen } from '../shared/pipes/bytes-pipe/utils';
|
||||
import { ActiveFilter } from '../shared/filters.utils';
|
||||
@ -131,6 +131,7 @@ export class StateService {
|
||||
latestBlockHeight = -1;
|
||||
blocks: BlockExtended[] = [];
|
||||
mempoolSequence: number;
|
||||
mempoolBlockState: { block: number, transactions: { [txid: string]: TransactionStripped} };
|
||||
|
||||
backend$ = new BehaviorSubject<'esplora' | 'electrum' | 'none'>('esplora');
|
||||
networkChanged$ = new ReplaySubject<string>(1);
|
||||
@ -143,7 +144,7 @@ export class StateService {
|
||||
mempoolInfo$ = new ReplaySubject<MempoolInfo>(1);
|
||||
mempoolBlocks$ = new ReplaySubject<MempoolBlock[]>(1);
|
||||
mempoolBlockUpdate$ = new Subject<MempoolBlockUpdate>();
|
||||
liveMempoolBlockTransactions$: Observable<{ [txid: string]: TransactionStripped}>;
|
||||
liveMempoolBlockTransactions$: Observable<{ block: number, transactions: { [txid: string]: TransactionStripped} }>;
|
||||
accelerations$ = new Subject<AccelerationDelta>();
|
||||
liveAccelerations$: Observable<Acceleration[]>;
|
||||
txConfirmed$ = new Subject<[string, BlockExtended]>();
|
||||
@ -231,29 +232,40 @@ export class StateService {
|
||||
}
|
||||
});
|
||||
|
||||
this.liveMempoolBlockTransactions$ = this.mempoolBlockUpdate$.pipe(scan((transactions: { [txid: string]: TransactionStripped }, change: MempoolBlockUpdate): { [txid: string]: TransactionStripped } => {
|
||||
this.liveMempoolBlockTransactions$ = this.mempoolBlockUpdate$.pipe(scan((acc: { block: number, transactions: { [txid: string]: TransactionStripped } }, change: MempoolBlockUpdate): { block: number, transactions: { [txid: string]: TransactionStripped } } => {
|
||||
if (isMempoolState(change)) {
|
||||
const txMap = {};
|
||||
change.transactions.forEach(tx => {
|
||||
txMap[tx.txid] = tx;
|
||||
});
|
||||
return txMap;
|
||||
this.mempoolBlockState = {
|
||||
block: change.block,
|
||||
transactions: txMap
|
||||
};
|
||||
return this.mempoolBlockState;
|
||||
} else {
|
||||
change.added.forEach(tx => {
|
||||
transactions[tx.txid] = tx;
|
||||
acc.transactions[tx.txid] = tx;
|
||||
});
|
||||
change.removed.forEach(txid => {
|
||||
delete transactions[txid];
|
||||
delete acc.transactions[txid];
|
||||
});
|
||||
change.changed.forEach(tx => {
|
||||
if (transactions[tx.txid]) {
|
||||
transactions[tx.txid].rate = tx.rate;
|
||||
transactions[tx.txid].acc = tx.acc;
|
||||
if (acc.transactions[tx.txid]) {
|
||||
acc.transactions[tx.txid].rate = tx.rate;
|
||||
acc.transactions[tx.txid].acc = tx.acc;
|
||||
}
|
||||
});
|
||||
return transactions;
|
||||
this.mempoolBlockState = {
|
||||
block: change.block,
|
||||
transactions: acc.transactions
|
||||
};
|
||||
return this.mempoolBlockState;
|
||||
}
|
||||
}, {}));
|
||||
}, {}),
|
||||
share()
|
||||
);
|
||||
this.liveMempoolBlockTransactions$.subscribe();
|
||||
|
||||
// Emits the full list of pending accelerations each time it changes
|
||||
this.liveAccelerations$ = this.accelerations$.pipe(
|
||||
|
@ -35,6 +35,7 @@ export class WebsocketService {
|
||||
private isTrackingAddresses: string[] | false = false;
|
||||
private isTrackingAccelerations: boolean = false;
|
||||
private trackingMempoolBlock: number;
|
||||
private stoppingTrackMempoolBlock: any | null = null;
|
||||
private latestGitCommit = '';
|
||||
private onlineCheckTimeout: number;
|
||||
private onlineCheckTimeoutTwo: number;
|
||||
@ -203,19 +204,31 @@ export class WebsocketService {
|
||||
this.websocketSubject.next({ 'track-asset': 'stop' });
|
||||
}
|
||||
|
||||
startTrackMempoolBlock(block: number, force: boolean = false) {
|
||||
startTrackMempoolBlock(block: number, force: boolean = false): boolean {
|
||||
if (this.stoppingTrackMempoolBlock) {
|
||||
clearTimeout(this.stoppingTrackMempoolBlock);
|
||||
}
|
||||
// skip duplicate tracking requests
|
||||
if (force || this.trackingMempoolBlock !== block) {
|
||||
this.websocketSubject.next({ 'track-mempool-block': block });
|
||||
this.isTrackingMempoolBlock = true;
|
||||
this.trackingMempoolBlock = block;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
stopTrackMempoolBlock() {
|
||||
this.websocketSubject.next({ 'track-mempool-block': -1 });
|
||||
stopTrackMempoolBlock(): void {
|
||||
if (this.stoppingTrackMempoolBlock) {
|
||||
clearTimeout(this.stoppingTrackMempoolBlock);
|
||||
}
|
||||
this.isTrackingMempoolBlock = false;
|
||||
this.trackingMempoolBlock = null;
|
||||
this.stoppingTrackMempoolBlock = setTimeout(() => {
|
||||
this.stoppingTrackMempoolBlock = null;
|
||||
this.websocketSubject.next({ 'track-mempool-block': -1 });
|
||||
this.trackingMempoolBlock = null;
|
||||
this.stateService.mempoolBlockState = null;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
startTrackRbf(mode: 'all' | 'fullRbf') {
|
||||
@ -424,6 +437,7 @@ export class WebsocketService {
|
||||
if (response['projected-block-transactions'].blockTransactions) {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
this.stateService.mempoolBlockUpdate$.next({
|
||||
block: this.trackingMempoolBlock,
|
||||
transactions: response['projected-block-transactions'].blockTransactions.map(uncompressTx),
|
||||
});
|
||||
} else if (response['projected-block-transactions'].delta) {
|
||||
@ -432,7 +446,7 @@ export class WebsocketService {
|
||||
this.startTrackMempoolBlock(this.trackingMempoolBlock, true);
|
||||
} else {
|
||||
this.stateService.mempoolSequence = response['projected-block-transactions'].sequence;
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(response['projected-block-transactions'].delta));
|
||||
this.stateService.mempoolBlockUpdate$.next(uncompressDeltaChange(this.trackingMempoolBlock, response['projected-block-transactions'].delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,8 +170,9 @@ export function uncompressTx(tx: TransactionCompressed): TransactionStripped {
|
||||
};
|
||||
}
|
||||
|
||||
export function uncompressDeltaChange(delta: MempoolBlockDeltaCompressed): MempoolBlockDelta {
|
||||
export function uncompressDeltaChange(block: number, delta: MempoolBlockDeltaCompressed): MempoolBlockDelta {
|
||||
return {
|
||||
block,
|
||||
added: delta.added.map(uncompressTx),
|
||||
removed: delta.removed,
|
||||
changed: delta.changed.map(tx => ({
|
||||
|
Loading…
Reference in New Issue
Block a user