Only show accelerator checkout on transaction page with #accelerate

This commit is contained in:
natsoni 2026-06-12 15:47:27 +02:00
parent 344d4b6109
commit af11081586
No known key found for this signature in database
GPG key ID: 5554443C889C720E
4 changed files with 101 additions and 56 deletions

View file

@ -330,7 +330,10 @@
<small class="form-text checkout-text mb-2" [class.mt-2]="forceMobile"><ng-container *ngTemplateOutlet="prioritizedBy; context: {$implicit:etaInfo.hashratePercentage}"></ng-container></small>
<app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true" class="ms-2"></app-active-acceleration-box>
</div>
<ng-container *ngTemplateOutlet="accelerateButton"></ng-container>
<div class="d-flex flex-column justify-content-center align-items-center">
<ng-container *ngTemplateOutlet="accelerateButton"></ng-container>
<button *ngIf="startedPastSummary" type="button" class="mt-3 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="closeModal()" i18n="transaction.view-details|View Transaction Details">View Transaction Details</button>
</div>
</div>
</div>
}
@ -442,8 +445,9 @@
<hr>
<div class="row mt-2 mb-2 text-center">
<div class="col-sm d-flex flex-column">
<button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button>
<div class="col-sm d-flex flex-row justify-content-center align-items-center flex-wrap gap-3">
<button type="button" class="btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="moveToStep('summary')" i18n="go-back">Go back</button>
<button *ngIf="startedPastSummary" type="button" class="btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="closeModal()" i18n="transaction.view-details|View Transaction Details">View Transaction Details</button>
</div>
</div>
} @else if (step === 'cashapp' || step === 'applepay' || step === 'googlepay' || step === 'cardonfile' || step === 'bitcoin') {

View file

@ -71,6 +71,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
@Input() forceMobile: boolean = false;
@Input() showDetails: boolean = false;
@Input() noCTA: boolean = false;
@Input() initialStep: CheckoutStep = 'summary';
@Input() partnerCode: string | undefined;
@Output() unavailable = new EventEmitter<boolean>();
@Output() completed = new EventEmitter<boolean>();
@ -167,7 +168,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.accelerateError = null;
this.timePaid = 0;
this.btcpayInvoiceFailed = false;
this.moveToStep('summary', true);
this.moveToStep(this.initialStep, true);
} else {
this.auth = auth;
}
@ -180,7 +181,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.insertSquare();
this.setupSquare();
} else {
this.moveToStep('summary', true);
this.moveToStep(this.initialStep, true);
}
this.conversionsSubscription = this.stateService.conversions$.subscribe(
@ -233,9 +234,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (this._step === 'checkout') {
this.insertSquare();
this.enterpriseService.goal(8);
this.scrollToElementWithTimeout('acceleratePreviewAnchor', 'start', 100);
if (!this.startedPastSummary) {
this.scrollToElementWithTimeout('acceleratePreviewAnchor', 'start', 100);
}
}
if (this._step === 'checkout' && this.canPayWithBitcoin) {
this.btcpayInvoiceFailed = false;
this.invoice = undefined;
@ -312,14 +315,14 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
tap((response) => {
if (response.status === 204) {
this.quoteError = `cannot_accelerate_tx`;
if (this.step === 'summary') {
if (this.step === 'summary' || this.startedPastSummary) {
this.unavailable.emit(true);
}
} else {
this.estimate = response.body;
if (!this.estimate) {
this.quoteError = `cannot_accelerate_tx`;
if (this.step === 'summary') {
if (this.step === 'summary' || this.startedPastSummary) {
this.unavailable.emit(true);
}
return;
@ -349,7 +352,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
if (!this.couldPay) {
this.quoteError = `cannot_accelerate_tx`;
if (this.step === 'summary') {
if (this.step === 'summary' || this.startedPastSummary) {
this.unavailable.emit(true);
}
return;
@ -368,7 +371,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.estimate = undefined;
this.quoteError = `cannot_accelerate_tx`;
this.estimateSubscription.unsubscribe();
if (this.step === 'summary') {
if (this.step === 'summary' || this.startedPastSummary) {
this.unavailable.emit(true);
} else {
this.accelerateError = 'cannot_accelerate_tx';
@ -989,6 +992,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
return this._step;
}
get startedPastSummary(): boolean {
return this.initialStep !== 'summary';
}
get paymentMethods(): PaymentMethod[] {
return Object.keys(this.estimate?.availablePaymentMethods || {}) as PaymentMethod[];
}

View file

@ -1,5 +1,6 @@
<div class="container-xl">
@if (!acceleratorCheckoutActive) {
<div class="title-block">
<div *ngIf="rbfTransaction && !tx?.status?.confirmed" class="alert alert-mempool" role="alert">
<span i18n="transaction.rbf.replacement|RBF replacement">This transaction has been replaced by:</span>
@ -63,7 +64,7 @@
[acceleratorAvailable]="acceleratorAvailable"
[accelerateCtaType]="accelerateCtaType"
[notAcceleratedOnLoad]="notAcceleratedOnLoad"
[showAccelerationSummary]="showAccelerationSummary"
[showAccelerationSummary]="acceleratorCheckoutActive"
[eligibleForAcceleration]="eligibleForAcceleration"
[hasEffectiveFeeRate]="hasEffectiveFeeRate"
[cpfpInfo]="cpfpInfo"
@ -101,35 +102,6 @@
</ng-template>
</ng-container>
<!-- Accelerator -->
<ng-container *ngIf="!tx?.status?.confirmed && showAccelerationSummary && (ETA$ | async) as eta;">
<span id='accelerate' #accelerate class="anchor"></span>
<br>
<div class="title float-start mb-1">
<h2><a [href]="[ isMempoolSpaceBuild ? '/accelerator' : 'https://mempool.space/accelerator']" [target]="isMempoolSpaceBuild ? '' : 'blank'"><app-svg-images name="accelerator" [height]="isMobile ? '35px' : '45px'"></app-svg-images></a></h2>
</div>
<button type="button" class="btn btn-outline-info accelerator-toggle btn-sm float-end" [class.hide-on-mobile]="hasAccelerationDetails" (click)="closeAccelerator()" i18n="accelerator.hide">Hide accelerator</button>
<button *ngIf="hasAccelerationDetails" class="btn btn-sm btn-outline-info details-button float-end ms-2" (click)="showAccelerationDetails = !showAccelerationDetails" i18n="transaction.details|Transaction Details">Details</button>
<div class="clearfix"></div>
<app-accelerate-checkout
*ngIf="(da$ | async) as da;"
[advancedEnabled]="true"
[tx]="tx"
[accelerating]="isAcceleration"
[eta]="eta"
[miningStats]="miningStats"
[showDetails]="showAccelerationDetails"
[noCTA]="true"
[partnerCode]="this.partnerCode"
(hasDetails)="setHasAccelerationDetails($event)"
(completed)="onAccelerationCompleted()"
(unavailable)="eligibleForAcceleration = false"
class="h-100 w-100"
></app-accelerate-checkout>
</ng-container>
<br>
<ng-container *ngIf="rbfInfo">
@ -393,4 +365,45 @@
</ng-template>
</ng-template>
} @else {
<span id='accelerate' #accelerate class="anchor"></span>
<div class="title-block">
<div class="title mb-1">
<h2><a [href]="[ isMempoolSpaceBuild ? '/accelerator' : 'https://mempool.space/accelerator']" [target]="isMempoolSpaceBuild ? '' : 'blank'"><app-svg-images name="accelerator" [height]="isMobile ? '35px' : '45px'"></app-svg-images></a></h2>
</div>
<button *ngIf="hasAccelerationDetails" class="btn btn-sm btn-outline-info details-button ms-2" (click)="showAccelerationDetails = !showAccelerationDetails" i18n="transaction.details|Transaction Details">Details</button>
</div>
@if (acceleratorCheckoutLoading) {
<ng-container *ngTemplateOutlet="acceleratorCheckoutLoadingWidget"></ng-container>
} @else {
<app-accelerate-checkout
*ngIf="!tx?.status?.confirmed && showAccelerationSummary && (da$ | async) && (ETA$ | async) as eta; else acceleratorCheckoutLoadingWidget"
[advancedEnabled]="true"
[tx]="tx"
[accelerating]="isAcceleration"
[eta]="eta"
[miningStats]="miningStats"
[showDetails]="showAccelerationDetails"
[noCTA]="true"
[initialStep]="acceleratorCheckoutRequested ? 'checkout' : 'summary'"
[partnerCode]="this.partnerCode"
(hasDetails)="setHasAccelerationDetails($event)"
(completed)="onAccelerationCompleted()"
(unavailable)="this.eligibleForAcceleration = false"
class="h-100 w-100"
></app-accelerate-checkout>
}
<ng-template #acceleratorCheckoutLoadingWidget>
<div class="box card w-100 accelerate-checkout-inner" style="background: var(--box-bg)">
<div class="row">
<div class="col-md">
<div class="d-flex flex-row justify-content-center align-items-center">
<div class="m-4 spinner-border text-light" style="width: 25px; height: 25px"></div>
</div>
</div>
</div>
</div>
</ng-template>
}
</div>

View file

@ -163,7 +163,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR_BUTTON && this.stateService.network === '';
eligibleForAcceleration: boolean = false;
forceAccelerationSummary = false;
hideAccelerationSummary = false;
accelerationFlowCompleted = false;
showAccelerationDetails = false;
hasAccelerationDetails = false;
@ -238,8 +237,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
window.location.hash = window.location.hash.replace(`&partnerCode=${this.partnerCode}`, '').replace(`#partnerCode=${this.partnerCode}`, '');
}
this.hideAccelerationSummary = this.stateService.isMempoolSpaceBuild ? this.storageService.getValue('hide-accelerator-pref') == 'true' : true;
if (!this.stateService.isLiquid()) {
this.miningService.getMiningStats('1m').subscribe(stats => {
this.miningStats = stats;
@ -576,7 +573,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.stateService.network === '') {
if (!this.mempoolPosition.accelerated) {
if (!this.accelerationFlowCompleted && !this.hideAccelerationSummary && !this.showAccelerationSummary) {
if (!this.accelerationFlowCompleted && this.acceleratorCheckoutRequested && !this.showAccelerationSummary) {
this.miningService.getMiningStats('1m').subscribe(stats => {
this.miningStats = stats;
});
@ -1195,9 +1192,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
const anchor = Array.from(this.fragmentParams.entries()).find(([, value]) => value === '')?.[0] || null;
this.inputIndex = inputIndex;
this.outputIndex = outputIndex;
if (this.fragmentParams.has('accelerate')) {
this.forceAccelerationSummary = true;
}
if (!anchor && !this.fragmentAnchor) {
this.firstFragmentScroll = false;
}
@ -1251,33 +1245,60 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
closeAccelerator(): void {
this.router.navigate([], { fragment: this.formatFragment(this.fragmentParams), queryParamsHandling: 'merge' });
this.hideAccelerationSummary = true;
this.forceAccelerationSummary = false;
this.storageService.setValue('hide-accelerator-pref', 'true');
}
openAccelerator(): void {
this.router.navigate([], { fragment: this.formatFragment(this.fragmentParams, 'accelerate'), queryParamsHandling: 'merge' });
this.accelerationFlowCompleted = false;
this.hideAccelerationSummary = false;
this.storageService.setValue('hide-accelerator-pref', 'false');
}
get showAccelerationSummary(): boolean {
return (
this.tx
&& !this.tx.status?.confirmed
&& !this.replaced
&& !this.isCached
&& this.acceleratorAvailable
&& this.eligibleForAcceleration
&& (
(!this.hideAccelerationSummary && !this.accelerationFlowCompleted)
|| this.forceAccelerationSummary
)
&& !this.accelerationFlowCompleted
&& (this.acceleratorCheckoutRequested || this.forceAccelerationSummary)
&& this.notAcceleratedOnLoad // avoid briefly showing accelerator checkout on already accelerated txs
);
}
get acceleratorCheckoutRequested(): boolean {
return this.fragmentParams?.has('accelerate') === true;
}
get acceleratorCheckoutLoading(): boolean {
if (!this.acceleratorCheckoutRequested || this.error || this.showAccelerationSummary) {
return false;
}
return (
this.isLoadingTx
|| this.loadingCachedTx
|| !!(
this.tx
&& !this.tx.status?.confirmed
&& !this.replaced
&& !this.isCached
&& this.acceleratorAvailable
&& this.eligibleForAcceleration
&& this.notAcceleratedOnLoad === null
)
);
}
get acceleratorCheckoutActive(): boolean {
return this.acceleratorCheckoutReady || this.acceleratorCheckoutLoading;
}
get acceleratorCheckoutReady(): boolean {
return this.acceleratorCheckoutRequested && !this.error && this.showAccelerationSummary;
}
ngOnDestroy() {
this.subscription.unsubscribe();
this.fetchCpfpSubscription.unsubscribe();