Merge pull request #5671 from mempool/natsoni/cancelled-accel-on-timeline

Canceled acceleration on timeline
This commit is contained in:
wiz 2024-12-08 13:12:27 +09:00 committed by GitHub
commit 0d31143fed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 52 additions and 33 deletions

View file

@ -1,6 +1,6 @@
<div class="acceleration-timeline box" [class.lower-padding]="!tx.status.confirmed"> <div class="acceleration-timeline box" [class.lower-padding]="!tx.status.confirmed">
<div class="timeline-wrapper"> <div class="timeline-wrapper">
@if (!tx.status.confirmed) { @if (!tx.status.confirmed || canceled) {
<div class="timeline"> <div class="timeline">
<div class="intervals"> <div class="intervals">
<div class="node-spacer"></div> <div class="node-spacer"></div>
@ -8,7 +8,7 @@
<div class="node-spacer"></div> <div class="node-spacer"></div>
<div class="interval"> <div class="interval">
<div class="interval-time"> <div class="interval-time">
@if (eta) { @if (eta && !canceled) {
~<app-time [time]="eta?.wait / 1000"></app-time> ~<app-time [time]="eta?.wait / 1000"></app-time>
} }
</div> </div>
@ -19,16 +19,20 @@
<div class="node-spacer"></div> <div class="node-spacer"></div>
<div class="interval-spacer"></div> <div class="interval-spacer"></div>
<div class="node"> <div class="node">
<div class="acc-to-confirmed right go-faster"></div> <div class="acc-to-confirmed right go-faster" [class.no-animation]="canceled"></div>
</div> </div>
<div class="interval-spacer"> <div class="interval-spacer">
</div> </div>
<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" [class.no-animation]="canceled"></div>
<div class="shape-border waiting"> <div class="shape-border waiting">
<div class="shape"></div> <div class="shape"></div>
</div> </div>
@if (canceled) {
<div class="status"><span class="badge badge-danger" i18n="accelerator.canceled">Canceled</span></div>
} @else {
<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>
</div> </div>
</div> </div>
@ -45,9 +49,9 @@
<div class="interval"> <div class="interval">
<div class="interval-time"> <div class="interval-time">
@if (tx.status.confirmed) { @if (tx.status.confirmed) {
<div class="interval-time">
<app-time [time]="acceleratedToMined"></app-time> <app-time [time]="acceleratedToMined"></app-time>
</div> } @else if (eta && canceled) {
~<app-time [time]="eta?.wait / 1000"></app-time>
} }
</div> </div>
</div> </div>
@ -71,42 +75,42 @@
<div class="interval-spacer"> <div class="interval-spacer">
<div class="seen-to-acc"></div> <div class="seen-to-acc"></div>
</div> </div>
<div class="node" [class.accelerated]="!tx.status.confirmed" [id]="'accelerated'"> <div class="node" [class.accelerated]="!tx.status.confirmed && !canceled" [id]="'accelerated'">
<div class="seen-to-acc left"></div> <div class="seen-to-acc left"></div>
@if (tx.status.confirmed) { @if (tx.status.confirmed && !canceled) {
<div class="acc-to-confirmed right"></div> <div class="acc-to-confirmed right"></div>
} @else { } @else {
<div class="seen-to-acc right"></div> <div class="seen-to-acc right"></div>
} }
<div class="shape-border hovering" (pointerover)="onHover($event, 'accelerated');" (pointerout)="onBlur($event);"> <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 || canceled) {
<div class="connector down loading"></div> <div class="connector down" [class.loading]="!canceled"></div>
} }
</div> </div>
@if (tx.status.confirmed) { @if (tx.status.confirmed && !canceled) {
<div class="status"><span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span></div> <div class="status"><span class="badge badge-accelerated" i18n="transaction.audit.accelerated">Accelerated</span></div>
} }
<div class="time" [class.no-margin]="!tx.status.confirmed" [class.offset-left]="!tx.status.confirmed"> <div class="time" [class.no-margin]="!tx.status.confirmed || canceled" [class.offset-left]="!tx.status.confirmed || canceled">
@if (!tx.status.confirmed) { @if (!tx.status.confirmed) {
<span i18n="transaction.audit.accelerated">Accelerated</span>{{ "" }} <span i18n="transaction.audit.accelerated">Accelerated</span>{{ "" }}
} }
@if (useAbsoluteTime) { @if (useAbsoluteTime) {
<span>{{ acceleratedAt * 1000 | date }}</span> <span>{{ acceleratedAt * 1000 | date }}</span>
} @else { } @else {
<app-time kind="since" [time]="acceleratedAt" [lowercaseStart]="!tx.status.confirmed"></app-time> <app-time kind="since" [time]="acceleratedAt" [lowercaseStart]="!tx.status.confirmed || canceled"></app-time>
} }
</div> </div>
</div> </div>
<div class="interval-spacer"> <div class="interval-spacer">
@if (tx.status.confirmed) { @if (tx.status.confirmed && !canceled) {
<div class="acc-to-confirmed"></div> <div class="acc-to-confirmed"></div>
} @else { } @else {
<div class="seen-to-acc"></div> <div class="seen-to-acc"></div>
} }
</div> </div>
<div class="node" [class.selected]="tx.status.confirmed" [id]="'confirmed'"> <div class="node" [class.selected]="tx.status.confirmed" [id]="'confirmed'">
@if (tx.status.confirmed) { @if (tx.status.confirmed && !canceled) {
<div class="acc-to-confirmed left"></div> <div class="acc-to-confirmed left"></div>
} @else { } @else {
<div class="seen-to-acc left"></div> <div class="seen-to-acc left"></div>

View file

@ -129,6 +129,9 @@
margin-left: calc(-4em + 5px); margin-left: calc(-4em + 5px);
animation: goFasterLeft 0.8s infinite linear; animation: goFasterLeft 0.8s infinite linear;
} }
&.no-animation {
animation: none;
}
} }
&.left { &.left {

View file

@ -15,6 +15,7 @@ export class AccelerationTimelineComponent implements OnInit, OnChanges {
@Input() tx: Transaction; @Input() tx: Transaction;
@Input() accelerationInfo: Acceleration; @Input() accelerationInfo: Acceleration;
@Input() eta: ETA; @Input() eta: ETA;
@Input() canceled: boolean;
now: number; now: number;
accelerateRatio: number; accelerateRatio: number;

View file

@ -247,7 +247,7 @@
<ng-template #effectiveRateRow> <ng-template #effectiveRateRow>
@if (!isLoadingTx) { @if (!isLoadingTx) {
@if ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo) { @if ((cpfpInfo && hasEffectiveFeeRate) || (accelerationInfo && isAcceleration)) {
<tr> <tr>
@if (isAcceleration) { @if (isAcceleration) {
<td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td> <td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>

View file

@ -165,12 +165,12 @@
<br> <br>
</ng-container> </ng-container>
<ng-container *ngIf="transactionTime > 0 && tx.acceleratedAt > 0 && isAcceleration"> <ng-container *ngIf="transactionTime > 0 && tx.acceleratedAt > 0 && (isAcceleration || accelerationCanceled)">
<div class="title float-left"> <div class="title float-left">
<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" [acceleratedAt]="tx.acceleratedAt" [tx]="tx" [accelerationInfo]="accelerationInfo" [eta]="(ETA$ | async)"></app-acceleration-timeline> <app-acceleration-timeline [transactionTime]="transactionTime" [acceleratedAt]="tx.acceleratedAt" [tx]="tx" [accelerationInfo]="accelerationInfo" [eta]="(ETA$ | async)" [canceled]="accelerationCanceled"></app-acceleration-timeline>
<br> <br>
</ng-container> </ng-container>

View file

@ -107,6 +107,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
pool: Pool | null; pool: Pool | null;
auditStatus: TxAuditStatus | null; auditStatus: TxAuditStatus | null;
isAcceleration: boolean = false; isAcceleration: boolean = false;
accelerationCanceled: boolean = false;
filters: Filter[] = []; filters: Filter[] = [];
showCpfpDetails = false; showCpfpDetails = false;
miningStats: MiningStats; miningStats: MiningStats;
@ -360,16 +361,17 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
).subscribe((accelerationHistory) => { ).subscribe((accelerationHistory) => {
for (const acceleration of accelerationHistory) { for (const acceleration of accelerationHistory) {
if (acceleration.txid === this.txId) { if (acceleration.txid === this.txId) {
if (acceleration.status === 'completed' || acceleration.status === 'completed_provisional') { if ((acceleration.status === 'completed' || acceleration.status === 'completed_provisional') && acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
if (acceleration.pools.includes(acceleration.minedByPoolUniqueId)) {
const boostCost = acceleration.boostCost || acceleration.bidBoost; const boostCost = acceleration.boostCost || acceleration.bidBoost;
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize; acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
acceleration.boost = boostCost; acceleration.boost = boostCost;
this.tx.acceleratedAt = acceleration.added; this.tx.acceleratedAt = acceleration.added;
this.accelerationInfo = acceleration; this.accelerationInfo = acceleration;
} else {
this.tx.feeDelta = undefined;
} }
if (acceleration.status === 'failed' || acceleration.status === 'failed_provisional') {
this.accelerationCanceled = true;
this.tx.acceleratedAt = acceleration.added;
this.accelerationInfo = acceleration;
} }
this.waitingForAccelerationInfo = false; this.waitingForAccelerationInfo = false;
this.setIsAccelerated(); this.setIsAccelerated();
@ -878,9 +880,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
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.tx.feeDelta = cpfpInfo.feeDelta;
this.accelerationCanceled = false;
this.setIsAccelerated(firstCpfp); this.setIsAccelerated(firstCpfp);
} else if (this.tx.acceleration) { // Acceleration was cancelled while on the tx page, reset acceleration state } else if (cpfpInfo.acceleratedAt) { // Acceleration was cancelled: reset acceleration state
this.tx.acceleration = false; this.tx.acceleratedBy = cpfpInfo.acceleratedBy;
this.tx.acceleratedAt = cpfpInfo.acceleratedAt;
this.tx.feeDelta = cpfpInfo.feeDelta;
this.accelerationCanceled = true;
this.setIsAccelerated(firstCpfp); this.setIsAccelerated(firstCpfp);
} }
@ -904,7 +910,12 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
} }
setIsAccelerated(initialState: boolean = false) { setIsAccelerated(initialState: boolean = false) {
this.isAcceleration = ((this.tx.acceleration && (!this.tx.status.confirmed || this.waitingForAccelerationInfo)) || (this.accelerationInfo && this.pool && this.accelerationInfo.pools.some(pool => (pool === this.pool.id)))); this.isAcceleration =
(
(this.tx.acceleration && (!this.tx.status.confirmed || this.waitingForAccelerationInfo)) ||
(this.accelerationInfo && this.pool && this.accelerationInfo.pools.some(pool => (pool === this.pool.id)))
) &&
!this.accelerationCanceled;
if (this.isAcceleration) { if (this.isAcceleration) {
if (initialState) { if (initialState) {
this.accelerationFlowCompleted = true; this.accelerationFlowCompleted = true;