mirror of
https://github.com/mempool/mempool.git
synced 2025-03-15 12:20:28 +01:00
Merge pull request #5371 from mempool/natsoni/timeline-tooltip
Add tooltip to acceleration timeline
This commit is contained in:
commit
17ec50dba0
15 changed files with 245 additions and 9 deletions
|
@ -165,6 +165,7 @@ class BitcoinRoutes {
|
||||||
acceleration: tx.acceleration,
|
acceleration: tx.acceleration,
|
||||||
acceleratedBy: tx.acceleratedBy || undefined,
|
acceleratedBy: tx.acceleratedBy || undefined,
|
||||||
acceleratedAt: tx.acceleratedAt || undefined,
|
acceleratedAt: tx.acceleratedAt || undefined,
|
||||||
|
feeDelta: tx.feeDelta || undefined,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -453,6 +453,7 @@ class MempoolBlocks {
|
||||||
mempoolTx.acceleration = true;
|
mempoolTx.acceleration = true;
|
||||||
mempoolTx.acceleratedBy = isAcceleratedBy[txid] || acceleration?.pools;
|
mempoolTx.acceleratedBy = isAcceleratedBy[txid] || acceleration?.pools;
|
||||||
mempoolTx.acceleratedAt = acceleration?.added;
|
mempoolTx.acceleratedAt = acceleration?.added;
|
||||||
|
mempoolTx.feeDelta = acceleration?.feeDelta;
|
||||||
for (const ancestor of mempoolTx.ancestors || []) {
|
for (const ancestor of mempoolTx.ancestors || []) {
|
||||||
if (!mempool[ancestor.txid].acceleration) {
|
if (!mempool[ancestor.txid].acceleration) {
|
||||||
mempool[ancestor.txid].cpfpDirty = true;
|
mempool[ancestor.txid].cpfpDirty = true;
|
||||||
|
@ -460,6 +461,7 @@ class MempoolBlocks {
|
||||||
mempool[ancestor.txid].acceleration = true;
|
mempool[ancestor.txid].acceleration = true;
|
||||||
mempool[ancestor.txid].acceleratedBy = mempoolTx.acceleratedBy;
|
mempool[ancestor.txid].acceleratedBy = mempoolTx.acceleratedBy;
|
||||||
mempool[ancestor.txid].acceleratedAt = mempoolTx.acceleratedAt;
|
mempool[ancestor.txid].acceleratedAt = mempoolTx.acceleratedAt;
|
||||||
|
mempool[ancestor.txid].feeDelta = mempoolTx.feeDelta;
|
||||||
isAcceleratedBy[ancestor.txid] = mempoolTx.acceleratedBy;
|
isAcceleratedBy[ancestor.txid] = mempoolTx.acceleratedBy;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -823,6 +823,7 @@ class WebsocketHandler {
|
||||||
accelerated: mempoolTx.acceleration || undefined,
|
accelerated: mempoolTx.acceleration || undefined,
|
||||||
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
||||||
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
||||||
|
feeDelta: mempoolTx.feeDelta || undefined,
|
||||||
},
|
},
|
||||||
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
|
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
|
||||||
};
|
};
|
||||||
|
@ -864,6 +865,7 @@ class WebsocketHandler {
|
||||||
accelerated: mempoolTx.acceleration || undefined,
|
accelerated: mempoolTx.acceleration || undefined,
|
||||||
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
||||||
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
||||||
|
feeDelta: mempoolTx.feeDelta || undefined,
|
||||||
};
|
};
|
||||||
if (!mempoolTx.cpfpChecked) {
|
if (!mempoolTx.cpfpChecked) {
|
||||||
calculateMempoolTxCpfp(mempoolTx, newMempool);
|
calculateMempoolTxCpfp(mempoolTx, newMempool);
|
||||||
|
@ -1138,6 +1140,7 @@ class WebsocketHandler {
|
||||||
accelerated: mempoolTx.acceleration || undefined,
|
accelerated: mempoolTx.acceleration || undefined,
|
||||||
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
||||||
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
||||||
|
feeDelta: mempoolTx.feeDelta || undefined,
|
||||||
},
|
},
|
||||||
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
|
accelerationPositions: memPool.getAccelerationPositions(mempoolTx.txid),
|
||||||
});
|
});
|
||||||
|
@ -1160,6 +1163,7 @@ class WebsocketHandler {
|
||||||
accelerated: mempoolTx.acceleration || undefined,
|
accelerated: mempoolTx.acceleration || undefined,
|
||||||
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
acceleratedBy: mempoolTx.acceleratedBy || undefined,
|
||||||
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
acceleratedAt: mempoolTx.acceleratedAt || undefined,
|
||||||
|
feeDelta: mempoolTx.feeDelta || undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ export interface TransactionExtended extends IEsploraApi.Transaction {
|
||||||
acceleration?: boolean;
|
acceleration?: boolean;
|
||||||
acceleratedBy?: number[];
|
acceleratedBy?: number[];
|
||||||
acceleratedAt?: number;
|
acceleratedAt?: number;
|
||||||
|
feeDelta?: number;
|
||||||
replacement?: boolean;
|
replacement?: boolean;
|
||||||
uid?: number;
|
uid?: number;
|
||||||
flags?: number;
|
flags?: number;
|
||||||
|
@ -449,7 +450,7 @@ export interface OptimizedStatistic {
|
||||||
|
|
||||||
export interface TxTrackingInfo {
|
export interface TxTrackingInfo {
|
||||||
replacedBy?: string,
|
replacedBy?: string,
|
||||||
position?: { block: number, vsize: number, accelerated?: boolean, acceleratedBy?: number[], acceleratedAt?: number },
|
position?: { block: number, vsize: number, accelerated?: boolean, acceleratedBy?: number[], acceleratedAt?: number, feeDelta?: number },
|
||||||
cpfp?: {
|
cpfp?: {
|
||||||
ancestors?: Ancestor[],
|
ancestors?: Ancestor[],
|
||||||
bestDescendant?: Ancestor | null,
|
bestDescendant?: Ancestor | null,
|
||||||
|
@ -462,6 +463,7 @@ export interface TxTrackingInfo {
|
||||||
accelerated?: boolean,
|
accelerated?: boolean,
|
||||||
acceleratedBy?: number[],
|
acceleratedBy?: number[],
|
||||||
acceleratedAt?: number,
|
acceleratedAt?: number,
|
||||||
|
feeDelta?: number,
|
||||||
confirmed?: boolean
|
confirmed?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
<div
|
||||||
|
#tooltip
|
||||||
|
*ngIf="accelerationInfo && tooltipPosition !== null"
|
||||||
|
class="acceleration-tooltip"
|
||||||
|
[style.left]="tooltipPosition.x + 'px'"
|
||||||
|
[style.top]="tooltipPosition.y + 'px'"
|
||||||
|
>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="label" i18n="transaction.status|Transaction Status">Status</td>
|
||||||
|
<td class="value">
|
||||||
|
@if (accelerationInfo.status === 'seen') {
|
||||||
|
<span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span>
|
||||||
|
} @else if (accelerationInfo.status === 'accelerated') {
|
||||||
|
<span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span>
|
||||||
|
} @else if (accelerationInfo.status === 'mined') {
|
||||||
|
<span class="badge badge-success" i18n="transaction.rbf.mined">Mined</span>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="accelerationInfo.fee">
|
||||||
|
<td class="label" i18n="transaction.fee|Transaction fee">Fee</td>
|
||||||
|
<td class="value">{{ accelerationInfo.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="accelerationInfo.bidBoost >= 0 || accelerationInfo.feeDelta">
|
||||||
|
<td class="label" i18n="transaction.out-of-band-fees">Out-of-band fees</td>
|
||||||
|
@if (accelerationInfo.status === 'accelerated') {
|
||||||
|
<td style="color: #905cf4;" class="value">{{ accelerationInfo.feeDelta | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td>
|
||||||
|
} @else {
|
||||||
|
<td style="color: #905cf4;" class="value">{{ accelerationInfo.bidBoost | number }} <span class="symbol" i18n="shared.sat|sat">sat</span></td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="accelerationInfo.fee && accelerationInfo.weight && (accelerationInfo.feeDelta || accelerationInfo.bidBoost)">
|
||||||
|
@if (accelerationInfo.status === 'seen') {
|
||||||
|
<td class="label" i18n="transaction.fee-rate">Fee rate</td>
|
||||||
|
<td class="value"><app-fee-rate [fee]="accelerationInfo.fee" [weight]="accelerationInfo.weight"></app-fee-rate></td>
|
||||||
|
} @else if (accelerationInfo.status === 'accelerated' || accelerationInfo.status === 'mined') {
|
||||||
|
<td class="label" i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
|
||||||
|
@if (accelerationInfo.status === 'accelerated') {
|
||||||
|
<td class="value"><app-fee-rate [fee]="accelerationInfo.fee + (accelerationInfo.feeDelta || 0)" [weight]="accelerationInfo.weight"></app-fee-rate></td>
|
||||||
|
} @else {
|
||||||
|
<td class="value"><app-fee-rate [fee]="accelerationInfo.fee + (accelerationInfo.bidBoost || 0)" [weight]="accelerationInfo.weight"></app-fee-rate></td>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
<tr *ngIf="['accelerated', 'mined'].includes(accelerationInfo.status) && hasPoolsData()">
|
||||||
|
<td class="label" i18n="transaction.accelerated-by-hashrate|Accelerated to hashrate">Accelerated by</td>
|
||||||
|
<td class="value" *ngIf="accelerationInfo.pools">
|
||||||
|
<ng-container *ngFor="let pool of accelerationInfo.pools">
|
||||||
|
<img *ngIf="accelerationInfo.poolsData[pool]"
|
||||||
|
class="pool-logo"
|
||||||
|
[class.highlight]="pool === accelerationInfo?.minedByPoolUniqueId"
|
||||||
|
[src]="'/resources/mining-pools/' + accelerationInfo.poolsData[pool].slug + '.svg'"
|
||||||
|
onError="this.src = '/resources/mining-pools/default.svg'"
|
||||||
|
[alt]="'Logo of ' + pool.name + ' mining pool'">
|
||||||
|
</ng-container>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -0,0 +1,52 @@
|
||||||
|
.acceleration-tooltip {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 3;
|
||||||
|
background: color-mix(in srgb, var(--active-bg) 95%, transparent);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 1px 1px 10px rgba(0,0,0,0.5);
|
||||||
|
color: var(--tooltip-grey);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 15px;
|
||||||
|
text-align: left;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
.badge.badge-accelerated {
|
||||||
|
background-color: var(--tertiary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pool-logo {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
position: relative;
|
||||||
|
top: -1px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
filter: drop-shadow(0 0 5px #905cf4);
|
||||||
|
animation: pulse 1s infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Component, ElementRef, ViewChild, Input, OnChanges } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-acceleration-timeline-tooltip',
|
||||||
|
templateUrl: './acceleration-timeline-tooltip.component.html',
|
||||||
|
styleUrls: ['./acceleration-timeline-tooltip.component.scss'],
|
||||||
|
})
|
||||||
|
export class AccelerationTimelineTooltipComponent implements OnChanges {
|
||||||
|
@Input() accelerationInfo: any;
|
||||||
|
@Input() cursorPosition: { x: number, y: number };
|
||||||
|
|
||||||
|
tooltipPosition: any = null;
|
||||||
|
|
||||||
|
@ViewChild('tooltip') tooltipElement: ElementRef<HTMLCanvasElement>;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnChanges(changes): void {
|
||||||
|
if (changes.cursorPosition && changes.cursorPosition.currentValue) {
|
||||||
|
let x = Math.max(10, changes.cursorPosition.currentValue.x - 50);
|
||||||
|
let y = changes.cursorPosition.currentValue.y + 20;
|
||||||
|
if (this.tooltipElement) {
|
||||||
|
const elementBounds = this.tooltipElement.nativeElement.getBoundingClientRect();
|
||||||
|
if ((x + elementBounds.width) > (window.innerWidth - 10)) {
|
||||||
|
x = Math.max(0, window.innerWidth - elementBounds.width - 10);
|
||||||
|
}
|
||||||
|
if (y + elementBounds.height > (window.innerHeight - 20)) {
|
||||||
|
y = y - elementBounds.height - 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tooltipPosition = { x, y };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPoolsData(): boolean {
|
||||||
|
return Object.keys(this.accelerationInfo.poolsData).length > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@
|
||||||
<div class="node" [id]="'confirmed'">
|
<div class="node" [id]="'confirmed'">
|
||||||
<div class="acc-to-confirmed left go-faster"></div>
|
<div class="acc-to-confirmed left go-faster"></div>
|
||||||
<div class="shape-border waiting">
|
<div class="shape-border waiting">
|
||||||
<div class="shape animate"></div>
|
<div class="shape"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status"><span class="badge badge-waiting" i18n="transaction.rbf.mined">Mined</span></div>
|
<div class="status"><span class="badge badge-waiting" i18n="transaction.rbf.mined">Mined</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
<div class="nodes">
|
<div class="nodes">
|
||||||
<div class="node" [id]="'first-seen'">
|
<div class="node" [id]="'first-seen'">
|
||||||
<div class="seen-to-acc right"></div>
|
<div class="seen-to-acc right"></div>
|
||||||
<div class="shape-border">
|
<div class="shape-border hovering" (pointerover)="onHover($event, 'seen');" (pointerout)="onBlur($event);">
|
||||||
<div class="shape"></div>
|
<div class="shape"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span></div>
|
<div class="status"><span class="badge badge-primary" i18n="transaction.first-seen|Transaction first seen">First seen</span></div>
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
} @else {
|
} @else {
|
||||||
<div class="seen-to-acc right"></div>
|
<div class="seen-to-acc right"></div>
|
||||||
}
|
}
|
||||||
<div class="shape-border">
|
<div class="shape-border hovering" (pointerover)="onHover($event, 'accelerated');" (pointerout)="onBlur($event);">
|
||||||
<div class="shape"></div>
|
<div class="shape"></div>
|
||||||
@if (!tx.status.confirmed) {
|
@if (!tx.status.confirmed) {
|
||||||
<div class="connector down loading"></div>
|
<div class="connector down loading"></div>
|
||||||
|
@ -113,7 +113,10 @@
|
||||||
} @else {
|
} @else {
|
||||||
<div class="seen-to-acc left"></div>
|
<div class="seen-to-acc left"></div>
|
||||||
}
|
}
|
||||||
<div class="shape-border" [class.waiting]="!tx.status.confirmed">
|
<div class="shape-border"
|
||||||
|
[ngClass]="{'waiting': !tx.status.confirmed, 'hovering': tx.status.confirmed}"
|
||||||
|
(pointerover)="onHover($event, tx.status.confirmed ? 'mined' : null)"
|
||||||
|
(pointerout)="onBlur($event);">
|
||||||
<div class="shape"></div>
|
<div class="shape"></div>
|
||||||
</div>
|
</div>
|
||||||
@if (tx.status.confirmed) {
|
@if (tx.status.confirmed) {
|
||||||
|
@ -130,4 +133,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<app-acceleration-timeline-tooltip
|
||||||
|
[accelerationInfo]="hoverInfo"
|
||||||
|
[cursorPosition]="tooltipPosition"
|
||||||
|
></app-acceleration-timeline-tooltip>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -152,9 +152,16 @@
|
||||||
margin-bottom: -8px;
|
margin-bottom: -8px;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
transition: background-color 300ms, padding 300ms;
|
||||||
|
|
||||||
|
&.hovering {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.shape {
|
.shape {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
import { Component, Input, OnInit, OnChanges, HostListener } from '@angular/core';
|
||||||
import { ETA } from '../../services/eta.service';
|
import { ETA } from '../../services/eta.service';
|
||||||
import { Transaction } from '../../interfaces/electrs.interface';
|
import { Transaction } from '../../interfaces/electrs.interface';
|
||||||
|
import { Acceleration, SinglePoolStats } from '../../interfaces/node-api.interface';
|
||||||
|
import { MiningService } from '../../services/mining.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-acceleration-timeline',
|
selector: 'app-acceleration-timeline',
|
||||||
|
@ -10,6 +12,7 @@ import { Transaction } from '../../interfaces/electrs.interface';
|
||||||
export class AccelerationTimelineComponent implements OnInit, OnChanges {
|
export class AccelerationTimelineComponent implements OnInit, OnChanges {
|
||||||
@Input() transactionTime: number;
|
@Input() transactionTime: number;
|
||||||
@Input() tx: Transaction;
|
@Input() tx: Transaction;
|
||||||
|
@Input() accelerationInfo: Acceleration;
|
||||||
@Input() eta: ETA;
|
@Input() eta: ETA;
|
||||||
// A mined transaction has standard ETA and accelerated ETA undefined
|
// A mined transaction has standard ETA and accelerated ETA undefined
|
||||||
// A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet)
|
// A transaction in mempool has either standardETA defined (if accelerated) or acceleratedETA defined (if not accelerated yet)
|
||||||
|
@ -22,13 +25,25 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges {
|
||||||
useAbsoluteTime: boolean = false;
|
useAbsoluteTime: boolean = false;
|
||||||
interval: number;
|
interval: number;
|
||||||
|
|
||||||
constructor() {}
|
tooltipPosition = null;
|
||||||
|
hoverInfo: any = null;
|
||||||
|
poolsData: { [id: number]: SinglePoolStats } = {};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private miningService: MiningService,
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000;
|
this.acceleratedAt = this.tx.acceleratedAt ?? new Date().getTime() / 1000;
|
||||||
this.now = Math.floor(new Date().getTime() / 1000);
|
this.now = Math.floor(new Date().getTime() / 1000);
|
||||||
this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600;
|
this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600;
|
||||||
|
|
||||||
|
this.miningService.getPools().subscribe(pools => {
|
||||||
|
for (const pool of pools) {
|
||||||
|
this.poolsData[pool.unique_id] = pool;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.interval = window.setInterval(() => {
|
this.interval = window.setInterval(() => {
|
||||||
this.now = Math.floor(new Date().getTime() / 1000);
|
this.now = Math.floor(new Date().getTime() / 1000);
|
||||||
this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600;
|
this.useAbsoluteTime = this.tx.status.block_time < this.now - 7 * 24 * 3600;
|
||||||
|
@ -52,4 +67,42 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges {
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onHover(event, status: string): void {
|
||||||
|
if (status === 'seen') {
|
||||||
|
this.hoverInfo = {
|
||||||
|
status,
|
||||||
|
fee: this.tx.fee,
|
||||||
|
weight: this.tx.weight
|
||||||
|
};
|
||||||
|
} else if (status === 'accelerated') {
|
||||||
|
this.hoverInfo = {
|
||||||
|
status,
|
||||||
|
fee: this.accelerationInfo?.effectiveFee || this.tx.fee,
|
||||||
|
weight: this.tx.weight,
|
||||||
|
feeDelta: this.accelerationInfo?.feeDelta || this.tx.feeDelta,
|
||||||
|
pools: this.tx.acceleratedBy || this.accelerationInfo?.pools,
|
||||||
|
poolsData: this.poolsData
|
||||||
|
};
|
||||||
|
} else if (status === 'mined') {
|
||||||
|
this.hoverInfo = {
|
||||||
|
status,
|
||||||
|
fee: this.accelerationInfo?.effectiveFee,
|
||||||
|
weight: this.tx.weight,
|
||||||
|
bidBoost: this.accelerationInfo?.bidBoost,
|
||||||
|
minedByPoolUniqueId: this.accelerationInfo?.minedByPoolUniqueId,
|
||||||
|
pools: this.tx.acceleratedBy || this.accelerationInfo?.pools,
|
||||||
|
poolsData: this.poolsData
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur(event): void {
|
||||||
|
this.hoverInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('pointermove', ['$event'])
|
||||||
|
onPointerMove(event) {
|
||||||
|
this.tooltipPosition = { x: event.clientX, y: event.clientY };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@
|
||||||
<h2 id="acceleration-timeline" i18n="transaction.acceleration-timeline|Acceleration Timeline">Acceleration Timeline</h2>
|
<h2 id="acceleration-timeline" i18n="transaction.acceleration-timeline|Acceleration Timeline">Acceleration Timeline</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<app-acceleration-timeline [transactionTime]="transactionTime" [tx]="tx" [eta]="(ETA$ | async)" [standardETA]="(standardETA$ | async)?.time"></app-acceleration-timeline>
|
<app-acceleration-timeline [transactionTime]="transactionTime" [tx]="tx" [accelerationInfo]="accelerationInfo" [eta]="(ETA$ | async)" [standardETA]="(standardETA$ | async)?.time"></app-acceleration-timeline>
|
||||||
<br>
|
<br>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -816,6 +816,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.tx.acceleration = cpfpInfo.acceleration;
|
this.tx.acceleration = cpfpInfo.acceleration;
|
||||||
this.tx.acceleratedBy = cpfpInfo.acceleratedBy;
|
this.tx.acceleratedBy = cpfpInfo.acceleratedBy;
|
||||||
this.tx.acceleratedAt = cpfpInfo.acceleratedAt;
|
this.tx.acceleratedAt = cpfpInfo.acceleratedAt;
|
||||||
|
this.tx.feeDelta = cpfpInfo.feeDelta;
|
||||||
this.setIsAccelerated(firstCpfp);
|
this.setIsAccelerated(firstCpfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ export interface Transaction {
|
||||||
acceleration?: boolean;
|
acceleration?: boolean;
|
||||||
acceleratedBy?: number[];
|
acceleratedBy?: number[];
|
||||||
acceleratedAt?: number;
|
acceleratedAt?: number;
|
||||||
|
feeDelta?: number;
|
||||||
deleteAfter?: number;
|
deleteAfter?: number;
|
||||||
_unblinded?: any;
|
_unblinded?: any;
|
||||||
_deduced?: boolean;
|
_deduced?: boolean;
|
||||||
|
|
|
@ -31,6 +31,7 @@ export interface CpfpInfo {
|
||||||
acceleration?: boolean;
|
acceleration?: boolean;
|
||||||
acceleratedBy?: number[];
|
acceleratedBy?: number[];
|
||||||
acceleratedAt?: number;
|
acceleratedAt?: number;
|
||||||
|
feeDelta?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RbfInfo {
|
export interface RbfInfo {
|
||||||
|
|
|
@ -68,6 +68,7 @@ import { AddressTransactionsWidgetComponent } from '../components/address-transa
|
||||||
import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component';
|
import { RbfTimelineComponent } from '../components/rbf-timeline/rbf-timeline.component';
|
||||||
import { AccelerationTimelineComponent } from '../components/acceleration-timeline/acceleration-timeline.component';
|
import { AccelerationTimelineComponent } from '../components/acceleration-timeline/acceleration-timeline.component';
|
||||||
import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component';
|
import { RbfTimelineTooltipComponent } from '../components/rbf-timeline/rbf-timeline-tooltip.component';
|
||||||
|
import { AccelerationTimelineTooltipComponent } from '../components/acceleration-timeline/acceleration-timeline-tooltip.component';
|
||||||
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
import { PushTransactionComponent } from '../components/push-transaction/push-transaction.component';
|
||||||
import { TestTransactionsComponent } from '../components/test-transactions/test-transactions.component';
|
import { TestTransactionsComponent } from '../components/test-transactions/test-transactions.component';
|
||||||
import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component';
|
import { AssetsFeaturedComponent } from '../components/assets/assets-featured/assets-featured.component';
|
||||||
|
@ -180,6 +181,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||||
RbfTimelineComponent,
|
RbfTimelineComponent,
|
||||||
AccelerationTimelineComponent,
|
AccelerationTimelineComponent,
|
||||||
RbfTimelineTooltipComponent,
|
RbfTimelineTooltipComponent,
|
||||||
|
AccelerationTimelineTooltipComponent,
|
||||||
PushTransactionComponent,
|
PushTransactionComponent,
|
||||||
TestTransactionsComponent,
|
TestTransactionsComponent,
|
||||||
AssetsNavComponent,
|
AssetsNavComponent,
|
||||||
|
@ -320,6 +322,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir
|
||||||
RbfTimelineComponent,
|
RbfTimelineComponent,
|
||||||
AccelerationTimelineComponent,
|
AccelerationTimelineComponent,
|
||||||
RbfTimelineTooltipComponent,
|
RbfTimelineTooltipComponent,
|
||||||
|
AccelerationTimelineTooltipComponent,
|
||||||
PushTransactionComponent,
|
PushTransactionComponent,
|
||||||
TestTransactionsComponent,
|
TestTransactionsComponent,
|
||||||
AssetsNavComponent,
|
AssetsNavComponent,
|
||||||
|
|
Loading…
Add table
Reference in a new issue