Display multiple arrows for accelerated transactions
This commit is contained in:
@@ -49,7 +49,10 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }" [class.blink]="txPosition?.accelerated"></div>
|
||||
<div *ngIf="arrowVisible" id="arrow-up" [ngStyle]="{'right': rightPosition + 75 + 'px', transition: transition }"></div>
|
||||
<ng-container *ngFor="let pool of accelerationPositions; trackBy: accTrackByFn">
|
||||
<div class="acceleration-arrow blink" [ngStyle]="{'right': pool.offset + 75 + 'px', transition: 'background 2s, right 2s, transform 1s' }"></div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -122,6 +122,17 @@
|
||||
border-bottom: 35px solid #FFF;
|
||||
}
|
||||
|
||||
.acceleration-arrow {
|
||||
position: relative;
|
||||
right: 75px;
|
||||
top: 105px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 35px solid transparent;
|
||||
border-right: 35px solid transparent;
|
||||
border-bottom: 35px solid #FFF;
|
||||
}
|
||||
|
||||
.blockLink {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -154,7 +165,7 @@
|
||||
}
|
||||
|
||||
:host-context(.rtl-layout) {
|
||||
#arrow-up {
|
||||
#arrow-up, .acceleration-arrow {
|
||||
transform: translateX(70px);
|
||||
}
|
||||
}
|
||||
@@ -172,8 +183,6 @@
|
||||
}
|
||||
|
||||
.blink{
|
||||
width:400px;
|
||||
height:400px;
|
||||
border-bottom: 35px solid #FFF;
|
||||
animation: blink 0.2s infinite;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { feeLevels, mempoolFeeColors } from '../../app.constants';
|
||||
import { specialBlocks } from '../../app.constants';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { Location } from '@angular/common';
|
||||
import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
|
||||
import { AccelerationPosition, DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
@@ -66,13 +66,19 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
blockPadding: number = 30;
|
||||
containerOffset: number = 40;
|
||||
arrowVisible = false;
|
||||
accelerationArrow = true;
|
||||
tabHidden = false;
|
||||
feeRounding = '1.0-0';
|
||||
|
||||
rightPosition = 0;
|
||||
transition = 'background 2s, right 2s, transform 1s';
|
||||
accelerationPositions: AccelerationPosition[] = [];
|
||||
accTransition = 'background 2s, right 2s, transform 1s';
|
||||
animatingAcceleration: boolean = false;
|
||||
|
||||
markIndex: number;
|
||||
markedTxid: string;
|
||||
|
||||
txPosition: MempoolPosition;
|
||||
txFeePerVSize: number;
|
||||
|
||||
@@ -160,7 +166,6 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.now = Date.now();
|
||||
|
||||
this.updateMempoolBlockStyles();
|
||||
this.calculateTransactionPosition();
|
||||
|
||||
return this.mempoolBlocks;
|
||||
}),
|
||||
@@ -188,6 +193,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.markIndex = undefined;
|
||||
this.txPosition = undefined;
|
||||
this.txFeePerVSize = undefined;
|
||||
this.accelerationPositions = [];
|
||||
if (state.mempoolBlockIndex !== undefined) {
|
||||
this.markIndex = state.mempoolBlockIndex;
|
||||
}
|
||||
@@ -197,7 +203,20 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
if (state.txFeePerVSize) {
|
||||
this.txFeePerVSize = state.txFeePerVSize;
|
||||
}
|
||||
this.calculateTransactionPosition();
|
||||
if (state.accelerationPositions) {
|
||||
this.accelerationPositions = state.accelerationPositions;
|
||||
}
|
||||
if (this.txPosition && this.txPosition.accelerated) {
|
||||
const newlyAccelerated = (!this.accelerationArrow && state.txid === this.markedTxid);
|
||||
this.calculateTransactionPosition(true);
|
||||
if (newlyAccelerated || !this.animatingAcceleration) {
|
||||
this.calculateAccelerationPositions(newlyAccelerated);
|
||||
}
|
||||
} else {
|
||||
this.accelerationArrow = false;
|
||||
this.calculateTransactionPosition();
|
||||
}
|
||||
this.markedTxid = state.txid;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
|
||||
@@ -289,6 +308,10 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return (block.isStack) ? `stack-${block.index}` : block.index;
|
||||
}
|
||||
|
||||
accTrackByFn(index: number, pool: AccelerationPosition) {
|
||||
return pool.pool;
|
||||
}
|
||||
|
||||
reduceEmptyBlocksToFitScreen(blocks: MempoolBlock[]): MempoolBlock[] {
|
||||
const innerWidth = this.containerWidth || (this.stateService.env.BASE_MODULE !== 'liquid' && window.innerWidth <= 767.98 ? window.innerWidth : window.innerWidth / 2);
|
||||
let blocksAmount = this.stateService.env.MEMPOOL_BLOCKS_AMOUNT;
|
||||
@@ -389,7 +412,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
calculateTransactionPosition() {
|
||||
calculateTransactionPosition(fromFee: boolean = false) {
|
||||
if ((!this.txPosition && !this.txFeePerVSize && (this.markIndex === undefined || this.markIndex === -1)) || !this.mempoolBlocks) {
|
||||
this.arrowVisible = false;
|
||||
return;
|
||||
@@ -408,7 +431,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
this.arrowVisible = true;
|
||||
|
||||
if (this.txPosition) {
|
||||
if (this.txPosition && !fromFee) {
|
||||
if (this.txPosition.block >= this.mempoolBlocks.length) {
|
||||
this.rightPosition = ((this.mempoolBlocks.length - 1) * (this.blockWidth + this.blockPadding)) + this.blockWidth;
|
||||
} else {
|
||||
@@ -418,9 +441,9 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
} else {
|
||||
let found = false;
|
||||
for (let txInBlockIndex = 0; txInBlockIndex < this.mempoolBlocks.length && !found; txInBlockIndex++) {
|
||||
for (let txInBlockIndex = this.mempoolBlocks.length - 1; txInBlockIndex >= 0 && !found; txInBlockIndex--) {
|
||||
const block = this.mempoolBlocks[txInBlockIndex];
|
||||
for (let i = 0; i < block.feeRange.length - 1 && !found; i++) {
|
||||
for (let i = block.feeRange.length - 2; i >= 0 && !found; i--) {
|
||||
if (this.txFeePerVSize < block.feeRange[i + 1] && this.txFeePerVSize >= block.feeRange[i]) {
|
||||
const feeRangeIndex = i;
|
||||
const feeRangeChunkSize = 1 / (block.feeRange.length - 1);
|
||||
@@ -448,6 +471,39 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
calculateAccelerationPositions(animate: boolean = false) {
|
||||
if (!this.accelerationPositions || !this.mempoolBlocks) {
|
||||
this.accelerationArrow = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.accelerationArrow = true;
|
||||
|
||||
const applyPositions = () => {
|
||||
for (const accelerationPosition of this.accelerationPositions) {
|
||||
if (accelerationPosition.block >= this.mempoolBlocks.length) {
|
||||
accelerationPosition.offset = ((this.mempoolBlocks.length - 1) * (this.blockWidth + this.blockPadding)) + this.blockWidth;
|
||||
} else {
|
||||
const positionInBlock = Math.min(1, this.txPosition.vsize / this.stateService.blockVSize) * this.blockWidth;
|
||||
const positionOfBlock = accelerationPosition.block * (this.blockWidth + this.blockPadding);
|
||||
accelerationPosition.offset = positionOfBlock + positionInBlock;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (animate) {
|
||||
this.animatingAcceleration = true;
|
||||
for (const accelerationPosition of this.accelerationPositions) {
|
||||
accelerationPosition.offset = this.rightPosition;
|
||||
}
|
||||
setTimeout(applyPositions, 100);
|
||||
setTimeout(() => {
|
||||
this.animatingAcceleration = false;
|
||||
}, 200);
|
||||
} else {
|
||||
applyPositions();
|
||||
}
|
||||
}
|
||||
|
||||
mountEmptyBlocks() {
|
||||
const emptyBlocks = [];
|
||||
const numberOfBlocks = this.stateService.env.MEMPOOL_BLOCKS_AMOUNT;
|
||||
|
||||
@@ -21,7 +21,7 @@ import { ApiService } from '../../services/api.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration } from '../../interfaces/node-api.interface';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition } from '../../interfaces/node-api.interface';
|
||||
import { LiquidUnblinding } from './liquid-ublinding';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { Price, PriceService } from '../../services/price.service';
|
||||
@@ -38,6 +38,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
txId: string;
|
||||
txInBlockIndex: number;
|
||||
mempoolPosition: MempoolPosition;
|
||||
accelerationPositions: AccelerationPosition[];
|
||||
isLoadingTx = true;
|
||||
error: any = undefined;
|
||||
errorUnblinded: any = undefined;
|
||||
@@ -265,10 +266,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.now = Date.now();
|
||||
if (txPosition && txPosition.txid === this.txId && txPosition.position) {
|
||||
this.mempoolPosition = txPosition.position;
|
||||
this.accelerationPositions = txPosition.accelerationPositions;
|
||||
if (this.tx && !this.tx.status.confirmed) {
|
||||
const txFeePerVSize = this.getUnacceleratedFeeRate(this.tx, this.tx.acceleration || this.mempoolPosition?.accelerated);
|
||||
this.stateService.markBlock$.next({
|
||||
txid: txPosition.txid,
|
||||
mempoolPosition: this.mempoolPosition
|
||||
txFeePerVSize,
|
||||
mempoolPosition: this.mempoolPosition,
|
||||
accelerationPositions: this.accelerationPositions,
|
||||
});
|
||||
this.txInBlockIndex = this.mempoolPosition.block;
|
||||
|
||||
@@ -278,6 +283,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
} else {
|
||||
this.mempoolPosition = null;
|
||||
this.accelerationPositions = null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -400,11 +406,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
this.fetchCpfp$.next(this.tx.txid);
|
||||
} else {
|
||||
const txFeePerVSize = this.getUnacceleratedFeeRate(this.tx, this.tx.acceleration || this.mempoolPosition?.accelerated);
|
||||
if (tx.cpfpChecked) {
|
||||
this.stateService.markBlock$.next({
|
||||
txid: tx.txid,
|
||||
txFeePerVSize: tx.effectiveFeePerVsize,
|
||||
txFeePerVSize,
|
||||
mempoolPosition: this.mempoolPosition,
|
||||
accelerationPositions: this.accelerationPositions,
|
||||
});
|
||||
this.cpfpInfo = {
|
||||
ancestors: tx.ancestors,
|
||||
@@ -619,6 +627,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.accelerationInfo = null;
|
||||
this.txInBlockIndex = null;
|
||||
this.mempoolPosition = null;
|
||||
this.accelerationPositions = null;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.leaveTransaction();
|
||||
}
|
||||
@@ -632,6 +641,20 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
return +(cpfpTx.fee / (cpfpTx.weight / 4)).toFixed(1);
|
||||
}
|
||||
|
||||
getUnacceleratedFeeRate(tx: Transaction, accelerated: boolean): number {
|
||||
if (accelerated) {
|
||||
let ancestorVsize = tx.weight / 4;
|
||||
let ancestorFee = tx.fee;
|
||||
for (const ancestor of tx.ancestors || []) {
|
||||
ancestorVsize += (ancestor.weight / 4);
|
||||
ancestorFee += ancestor.fee;
|
||||
}
|
||||
return Math.min(tx.fee / (tx.weight / 4), (ancestorFee / ancestorVsize));
|
||||
} else {
|
||||
return tx.effectiveFeePerVsize;
|
||||
}
|
||||
}
|
||||
|
||||
setupGraph() {
|
||||
this.maxInOut = Math.min(this.inOutLimit, Math.max(this.tx?.vin?.length || 1, this.tx?.vout?.length + 1 || 1));
|
||||
this.graphHeight = this.graphExpanded ? this.maxInOut * 15 : Math.min(360, this.maxInOut * 80);
|
||||
|
||||
@@ -196,6 +196,11 @@ export interface MempoolPosition {
|
||||
accelerated?: boolean
|
||||
}
|
||||
|
||||
export interface AccelerationPosition extends MempoolPosition {
|
||||
pool: string;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface RewardStats {
|
||||
startBlock: number;
|
||||
endBlock: number;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
|
||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
|
||||
import { AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { filter, map, scan, shareReplay } from 'rxjs/operators';
|
||||
@@ -16,6 +16,7 @@ export interface MarkBlockState {
|
||||
mempoolBlockIndex?: number;
|
||||
txFeePerVSize?: number;
|
||||
mempoolPosition?: MempoolPosition;
|
||||
accelerationPositions?: AccelerationPosition[];
|
||||
}
|
||||
|
||||
export interface ILoadingIndicators { [name: string]: number; }
|
||||
@@ -116,7 +117,7 @@ export class StateService {
|
||||
utxoSpent$ = new Subject<object>();
|
||||
difficultyAdjustment$ = new ReplaySubject<DifficultyAdjustment>(1);
|
||||
mempoolTransactions$ = new Subject<Transaction>();
|
||||
mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null}>();
|
||||
mempoolTxPosition$ = new Subject<{ txid: string, position: MempoolPosition, cpfp: CpfpInfo | null, accelerationPositions?: AccelerationPosition[] }>();
|
||||
mempoolRemovedTransactions$ = new Subject<Transaction>();
|
||||
blockTransactions$ = new Subject<Transaction>();
|
||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||
|
||||
Reference in New Issue
Block a user