mirror of
https://github.com/mempool/mempool.git
synced 2025-02-23 14:40:38 +01:00
Merge pull request #4962 from mempool/nymkappa/accel-checkout
[accelerator] polish prepaid accel UI
This commit is contained in:
commit
42bd9811e3
9 changed files with 414 additions and 55 deletions
|
@ -8,6 +8,7 @@ import { ClockComponent } from './components/clock/clock.component';
|
|||
import { StatusViewComponent } from './components/status-view/status-view.component';
|
||||
import { AddressGroupComponent } from './components/address-group/address-group.component';
|
||||
import { TrackerComponent } from './components/tracker/tracker.component';
|
||||
import { AccelerateCheckout } from './components/accelerate-checkout/accelerate-checkout.component';
|
||||
|
||||
const browserWindow = window || {};
|
||||
// @ts-ignore
|
||||
|
|
|
@ -1,19 +1,43 @@
|
|||
<div class="container card" style="padding: 20px; background: var(--bg)">
|
||||
|
||||
<div class="row">
|
||||
@if (error) {
|
||||
<app-mempool-error [error]="error"></app-mempool-error>
|
||||
} @else {
|
||||
|
||||
@if (step === 'completed') {
|
||||
<div class="row text-center mt-3">
|
||||
<div class="col-sm">
|
||||
<div class="form-group w-100">
|
||||
<div display="d-flex flex-row justify-content-center">
|
||||
<div class="alert alert-success">Transaction is now being accelerated!</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@else if (step === 'cta') {
|
||||
<!-- Show A/B CTAs -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm">
|
||||
<h1 style="font-size: larger;">Accelerate your Bitcoin transaction?</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="mt-3">
|
||||
<form>
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="form-group form-check">
|
||||
<input type="radio" class="form-check-input" id="accelerate" name="accelerate">
|
||||
<input type="radio" class="form-check-input" id="accelerate" name="accelerate" (change)="selectedOptionChanged($event)">
|
||||
<label class="form-check-label d-flex flex-column" for="accelerate">
|
||||
<span class="font-weight-bold">Accelerate</span>
|
||||
<span class="text-muted">Settlement expected in 1 hour or less<br>10,000 sats ($7.00) fee</span>
|
||||
<span style="color: rgb(186, 186, 186)">Settlement expected in ~1 hour or less<br>
|
||||
@if (!calculating) {
|
||||
<app-fiat [value]="cost"></app-fiat>fee (<span><small style="font-family: monospace;">{{ cost | number }}</small> <span class="symbol" i18n="shared.sats|sats">sats</span></span>)
|
||||
} @else {
|
||||
<span class="estimating">Calculating cost...</span>
|
||||
}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -21,27 +45,103 @@
|
|||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<div class="form-group form-check">
|
||||
<input type="radio" class="form-check-input" id="wait" name="accelerate" checked="checked">
|
||||
<input type="radio" class="form-check-input" id="wait" name="accelerate" (change)="selectedOptionChanged($event)">
|
||||
<label class="form-check-label d-flex flex-column" for="wait">
|
||||
<span class="font-weight-bold">Wait</span>
|
||||
@if (!eta) {
|
||||
<span class="text-muted">Settlement unlikely to occur within 24 hours</span>
|
||||
} @else {
|
||||
<span class="text-muted">Settlement estimated to occur <app-time kind="until" [time]="eta" [fastRender]="false" [fixedRender]="true"></app-time></span>
|
||||
}
|
||||
<span style="color: rgb(186, 186, 186)">Settlement expected to occur <app-time kind="within" [time]="eta" [fastRender]="false" [fixedRender]="true"></app-time></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="row mt-2" [style]="choosenOption === 'wait' ? 'opacity: 0.25; pointer-events: none' : ''">
|
||||
<div class="col-sm d-flex flex-row justify-content-center">
|
||||
<button type="submit" class="btn btn-light w-100 rounded-pill" style="max-width: 250px">
|
||||
<img src="/resources/mempool-accelerator-sparkles-compressed.svg" height="25" class="mr-2" style="margin-left: -33px">
|
||||
<button type="button" class="mt-1 btn btn-light rounded-pill align-self-center d-flex flex-row justify-content-center align-items-center" style="width: 200px" (click)="enableCheckoutPage()">
|
||||
<img src="/resources/mempool-accelerator-sparkles-compressed.svg" height="20" class="mr-2" style="margin-left: -10px">
|
||||
<span>Accelerate</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@else if (step === 'checkout') {
|
||||
<!-- Show checkout page -->
|
||||
<div class="row mb-3 text-center">
|
||||
<div class="col-sm">
|
||||
<h1 style="font-size: larger;">Confirm your payment</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row text-center">
|
||||
<div class="col-sm">
|
||||
<div class="form-group w-100">
|
||||
Payment to mempool.space for acceleration of txid <a [routerLink]="'/tx/' + txid" target="_blank">{{ txid.substr(0, 10) }}..{{ txid.substr(-10) }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!loadingCashapp) {
|
||||
<div class="row text-center mt-1">
|
||||
<div class="col-sm">
|
||||
<div class="form-group w-100">
|
||||
<span><u><strong>Total additional cost</strong></u><br>
|
||||
<span style="font-size: 16px" class="d-block mt-2">
|
||||
Pay
|
||||
<strong><app-fiat [value]="cost"></app-fiat></strong>
|
||||
with
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row text-center mt-1">
|
||||
<div class="col-sm">
|
||||
<div class="form-group w-100">
|
||||
<div id="cash-app-pay" class="d-inline-block" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
|
||||
@if (loadingCashapp) {
|
||||
<div display="d-flex flex-row justify-content-center">
|
||||
<span>Loading payment method...</span>
|
||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="row mt-2 text-center">
|
||||
<div class="col-sm d-flex flex-column">
|
||||
<small>Changed your mind?</small>
|
||||
<button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="restart(); closeModal()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@else if (step === 'processing') {
|
||||
<div class="row mb-3 text-center">
|
||||
<div class="col-sm">
|
||||
<h1 style="font-size: larger;">Confirm your payment</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Processing payment -->
|
||||
<div id="cash-app-pay" class="d-inline-block" [style]="'opacity: 0; width: 0px; height: 0px; pointer-events: none;'"></div>
|
||||
|
||||
<div class="row text-center mt-1">
|
||||
<div class="col-sm">
|
||||
<div class="form-group w-100">
|
||||
<div display="d-flex flex-row justify-content-center">
|
||||
<span>We are processing your payment...</span>
|
||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<span class="close-button" (click)="closeModal()">✖</span>
|
||||
}
|
||||
|
||||
</div>
|
|
@ -3,3 +3,7 @@
|
|||
top: 0.5em;
|
||||
right: 0.5em;
|
||||
}
|
||||
|
||||
.estimating {
|
||||
color: var(--green)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { Component, OnInit, OnDestroy, Output, EventEmitter, Input } from '@angular/core';
|
||||
import { Transaction } from '../../interfaces/electrs.interface';
|
||||
import { MempoolPosition } from '../../interfaces/node-api.interface';
|
||||
import { Component, OnInit, OnDestroy, Output, EventEmitter, Input, ChangeDetectorRef } from '@angular/core';
|
||||
import { Subscription, tap, of, catchError } from 'rxjs';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
import { nextRoundNumber } from '../../shared/common.utils';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { AudioService } from '../../services/audio.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accelerate-checkout',
|
||||
|
@ -8,21 +12,243 @@ import { MempoolPosition } from '../../interfaces/node-api.interface';
|
|||
styleUrls: ['./accelerate-checkout.component.scss']
|
||||
})
|
||||
export class AccelerateCheckout implements OnInit, OnDestroy {
|
||||
@Input() tx: Transaction ;
|
||||
@Input() eta: number;
|
||||
@Input() eta: number = Date.now() + 123456789;
|
||||
@Input() txid: string = '70c18d76cdb285a1b5bd87fdaae165880afa189809c30b4083ff7c0e69ee09ad';
|
||||
@Output() close = new EventEmitter<null>();
|
||||
|
||||
constructor() {
|
||||
calculating = true;
|
||||
choosenOption: 'wait' | 'accelerate' = 'wait';
|
||||
error = '';
|
||||
|
||||
// accelerator stuff
|
||||
square: { appId: string, locationId: string};
|
||||
accelerationUUID: string;
|
||||
estimateSubscription: Subscription;
|
||||
maxBidBoost: number; // sats
|
||||
cost: number; // sats
|
||||
|
||||
// square
|
||||
loadingCashapp = false;
|
||||
cashappSubmit: any;
|
||||
payments: any;
|
||||
cashAppPay: any;
|
||||
cashAppSubscription: Subscription;
|
||||
conversionsSubscription: Subscription;
|
||||
step: 'cta' | 'checkout' | 'processing' | 'completed' = 'completed';
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private stateService: StateService,
|
||||
private audioService: AudioService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
this.accelerationUUID = window.crypto.randomUUID();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('cash_request_id')) { // Redirected from cashapp
|
||||
this.insertSquare();
|
||||
this.setupSquare();
|
||||
this.step = 'processing';
|
||||
}
|
||||
|
||||
this.servicesApiService.setupSquare$().subscribe(ids => {
|
||||
this.square = {
|
||||
appId: ids.squareAppId,
|
||||
locationId: ids.squareLocationId
|
||||
};
|
||||
if (this.step === 'cta') {
|
||||
this.estimate();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.estimateSubscription) {
|
||||
this.estimateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accelerator
|
||||
*/
|
||||
estimate() {
|
||||
if (this.estimateSubscription) {
|
||||
this.estimateSubscription.unsubscribe();
|
||||
}
|
||||
this.calculating = true;
|
||||
this.estimateSubscription = this.servicesApiService.estimate$(this.txid).pipe(
|
||||
tap((response) => {
|
||||
this.calculating = false;
|
||||
if (response.status === 204) {
|
||||
this.error = `cannot_accelerate_tx`;
|
||||
} else {
|
||||
const estimation = response.body;
|
||||
if (!estimation) {
|
||||
this.error = `cannot_accelerate_tx`;
|
||||
return;
|
||||
}
|
||||
// Make min extra fee at least 50% of the current tx fee
|
||||
const minExtraBoost = nextRoundNumber(Math.max(estimation.cost * 2, estimation.txSummary.effectiveFee));
|
||||
const DEFAULT_BID_RATIO = 2;
|
||||
this.maxBidBoost = minExtraBoost * DEFAULT_BID_RATIO;
|
||||
this.cost = this.maxBidBoost * DEFAULT_BID_RATIO + estimation.mempoolBaseFee + estimation.vsizeFee;
|
||||
}
|
||||
}),
|
||||
|
||||
catchError((response) => {
|
||||
this.error = `cannot_accelerate_tx`;
|
||||
return of(null);
|
||||
})
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Square
|
||||
*/
|
||||
insertSquare(): void {
|
||||
//@ts-ignore
|
||||
if (window.Square) {
|
||||
return;
|
||||
}
|
||||
let statsUrl = 'https://sandbox.web.squarecdn.com/v1/square.js';
|
||||
if (document.location.hostname === 'mempool-staging.fmt.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.va1.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.fra.mempool.space' ||
|
||||
document.location.hostname === 'mempool-staging.tk7.mempool.space' ||
|
||||
document.location.hostname === 'mempool.space') {
|
||||
statsUrl = 'https://web.squarecdn.com/v1/square.js';
|
||||
}
|
||||
|
||||
(function() {
|
||||
const d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
// @ts-ignore
|
||||
g.type='text/javascript'; g.src=statsUrl; s.parentNode.insertBefore(g, s);
|
||||
})();
|
||||
}
|
||||
setupSquare() {
|
||||
const init = () => {
|
||||
this.initSquare();
|
||||
};
|
||||
|
||||
//@ts-ignore
|
||||
if (!window.Square) {
|
||||
console.debug('Square.js failed to load properly. Retrying in 1 second.');
|
||||
setTimeout(init, 1000);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}
|
||||
async initSquare(): Promise<void> {
|
||||
try {
|
||||
//@ts-ignore
|
||||
this.payments = window.Square.payments(this.square.appId, this.square.locationId)
|
||||
await this.requestCashAppPayment();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.error = 'Error loading Square Payments';
|
||||
return;
|
||||
}
|
||||
}
|
||||
async requestCashAppPayment() {
|
||||
if (this.cashAppSubscription) {
|
||||
this.cashAppSubscription.unsubscribe();
|
||||
}
|
||||
if (this.conversionsSubscription) {
|
||||
this.conversionsSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
this.conversionsSubscription = this.stateService.conversions$.subscribe(
|
||||
async (conversions) => {
|
||||
if (this.cashAppPay) {
|
||||
await this.cashAppPay.destroy();
|
||||
}
|
||||
|
||||
const redirectHostname = document.location.hostname === 'localhost' ? `http://localhost:4200`: `https://${document.location.hostname}`;
|
||||
const costUSD = this.step === 'processing' ? 69.69 : (this.cost / 100_000_000 * conversions.USD); // When we're redirected to this component, the payment data is already linked to the payment token, so does not matter what amonut we put in there, therefore it's 69.69
|
||||
const paymentRequest = this.payments.paymentRequest({
|
||||
countryCode: 'US',
|
||||
currencyCode: 'USD',
|
||||
total: {
|
||||
amount: costUSD.toString(),
|
||||
label: 'Total',
|
||||
pending: true,
|
||||
productUrl: `${redirectHostname}/tracker/${this.txid}`,
|
||||
},
|
||||
button: { shape: 'semiround', size: 'small', theme: 'light'}
|
||||
});
|
||||
this.cashAppPay = await this.payments.cashAppPay(paymentRequest, {
|
||||
redirectURL: `${redirectHostname}/tracker/${this.txid}`,
|
||||
referenceId: `accelerator-${this.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
|
||||
button: { shape: 'semiround', size: 'small', theme: 'light'}
|
||||
});
|
||||
|
||||
if (this.step === 'checkout') {
|
||||
await this.cashAppPay.attach(`#cash-app-pay`, { theme: 'light', size: 'small', shape: 'semiround' })
|
||||
}
|
||||
this.loadingCashapp = false;
|
||||
|
||||
const that = this;
|
||||
this.cashAppPay.addEventListener('ontokenization', function (event) {
|
||||
const { tokenResult, error } = event.detail;
|
||||
if (error) {
|
||||
this.error = error;
|
||||
} else if (tokenResult.status === 'OK') {
|
||||
that.servicesApiService.accelerateWithCashApp$(
|
||||
that.txid,
|
||||
tokenResult.token,
|
||||
tokenResult.details.cashAppPay.cashtag,
|
||||
tokenResult.details.cashAppPay.referenceId,
|
||||
that.accelerationUUID
|
||||
).subscribe({
|
||||
next: () => {
|
||||
that.audioService.playSound('ascend-chime-cartoon');
|
||||
that.step = 'completed';
|
||||
setTimeout(() => {
|
||||
that.closeModal();
|
||||
}, 10000);
|
||||
},
|
||||
error: (response) => {
|
||||
if (response.status === 403 && response.error === 'not_available') {
|
||||
that.error = 'waitlisted';
|
||||
} else {
|
||||
that.error = response.error;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* UI events
|
||||
*/
|
||||
enableCheckoutPage() {
|
||||
this.step = 'checkout';
|
||||
this.loadingCashapp = true;
|
||||
this.insertSquare();
|
||||
this.setupSquare();
|
||||
}
|
||||
selectedOptionChanged(event) {
|
||||
this.choosenOption = event.target.id;
|
||||
if (this.choosenOption === 'wait') {
|
||||
this.restart();
|
||||
this.closeModal();
|
||||
}
|
||||
}
|
||||
restart() {
|
||||
this.step = 'cta';
|
||||
this.choosenOption = 'wait';
|
||||
}
|
||||
closeModal(): void {
|
||||
console.log('close modal')
|
||||
if (this.cashAppPay) {
|
||||
this.cashAppPay.destroy();
|
||||
}
|
||||
this.close.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -362,7 +362,6 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
|||
|
||||
that.accelerationSubscription = that.servicesApiService.accelerateWithCashApp$(
|
||||
that.tx.txid,
|
||||
that.userBid,
|
||||
tokenResult.token,
|
||||
tokenResult.details.cashAppPay.cashtag,
|
||||
tokenResult.details.cashAppPay.referenceId,
|
||||
|
|
|
@ -23,7 +23,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||
|
||||
@Input() time: number;
|
||||
@Input() dateString: number;
|
||||
@Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' = 'plain';
|
||||
@Input() kind: 'plain' | 'since' | 'until' | 'span' | 'before' | 'within' = 'plain';
|
||||
@Input() fastRender = false;
|
||||
@Input() fixedRender = false;
|
||||
@Input() relative = false;
|
||||
|
@ -80,6 +80,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||
seconds = Math.floor((+new Date() - +new Date(this.dateString || this.time * 1000)) / 1000);
|
||||
break;
|
||||
case 'until':
|
||||
case 'within':
|
||||
seconds = (+new Date(this.time) - +new Date()) / 1000;
|
||||
break;
|
||||
default:
|
||||
|
@ -91,7 +92,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||
} else if (seconds < 60) {
|
||||
if (this.relative || this.kind === 'since') {
|
||||
return $localize`:@@date-base.just-now:Just now`;
|
||||
} else if (this.kind === 'until') {
|
||||
} else if (this.kind === 'until' || this.kind === 'within') {
|
||||
seconds = 60;
|
||||
}
|
||||
}
|
||||
|
@ -112,12 +113,12 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||
if (counter > 0) {
|
||||
let rounded;
|
||||
const roundFactor = Math.pow(10,this.fractionDigits || 0);
|
||||
if (this.kind === 'until' && usedUnits < this.numUnits) {
|
||||
if ((this.kind === 'until' || this.kind === 'within') && usedUnits < this.numUnits) {
|
||||
rounded = Math.floor((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor;
|
||||
} else {
|
||||
rounded = Math.round((seconds / this.intervals[precisionUnit]) * roundFactor) / roundFactor;
|
||||
}
|
||||
if (this.kind !== 'until' || this.numUnits === 1) {
|
||||
if ((this.kind !== 'until' && this.kind !== 'within')|| this.numUnits === 1) {
|
||||
return this.formatTime(this.kind, precisionUnit, rounded);
|
||||
} else {
|
||||
if (!usedUnits) {
|
||||
|
@ -185,6 +186,29 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'within':
|
||||
if (number === 1) {
|
||||
switch (unit) { // singular (In ~1 day)
|
||||
case 'year': return $localize`:@@time-within:within ~${dateStrings.i18nYear}:DATE:`; break;
|
||||
case 'month': return $localize`:@@time-within:within ~${dateStrings.i18nMonth}:DATE:`; break;
|
||||
case 'week': return $localize`:@@time-within:within ~${dateStrings.i18nWeek}:DATE:`; break;
|
||||
case 'day': return $localize`:@@time-within:within ~${dateStrings.i18nDay}:DATE:`; break;
|
||||
case 'hour': return $localize`:@@time-within:within ~${dateStrings.i18nHour}:DATE:`; break;
|
||||
case 'minute': return $localize`:@@time-within:within ~${dateStrings.i18nMinute}:DATE:`;
|
||||
case 'second': return $localize`:@@time-within:within ~${dateStrings.i18nSecond}:DATE:`;
|
||||
}
|
||||
} else {
|
||||
switch (unit) { // plural (In ~2 days)
|
||||
case 'year': return $localize`:@@time-within:within ~${dateStrings.i18nYears}:DATE:`; break;
|
||||
case 'month': return $localize`:@@time-within:within ~${dateStrings.i18nMonths}:DATE:`; break;
|
||||
case 'week': return $localize`:@@time-within:within ~${dateStrings.i18nWeeks}:DATE:`; break;
|
||||
case 'day': return $localize`:@@time-within:within ~${dateStrings.i18nDays}:DATE:`; break;
|
||||
case 'hour': return $localize`:@@time-within:within ~${dateStrings.i18nHours}:DATE:`; break;
|
||||
case 'minute': return $localize`:@@time-within:within ~${dateStrings.i18nMinutes}:DATE:`; break;
|
||||
case 'second': return $localize`:@@time-within:within ~${dateStrings.i18nSeconds}:DATE:`; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'span':
|
||||
if (number === 1) {
|
||||
switch (unit) { // singular (1 day)
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<app-time kind="until" *ngIf="(da$ | async) as da;" [time]="da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" [fastRender]="false" [fixedRender]="true"></app-time>
|
||||
}
|
||||
@if (isMobile && paymentType === 'cashapp' && accelerationEligible && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
||||
<a [href]="'/services/accelerator/accelerate?txid=' + tx.txid" class="btn btn-sm accelerate btn-small-height" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a>
|
||||
<a class="btn btn-sm accelerate btn-small-height" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -78,7 +78,7 @@
|
|||
|
||||
<div class="bottom-panel">
|
||||
@if (showAccelerationSummary) {
|
||||
<app-accelerate-checkout *ngIf="(da$ | async) as da;" [tx]="tx" [eta]="mempoolPosition?.block >= 7 ? null : da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" (close)="showAccelerationSummary = false"></app-accelerate-checkout>
|
||||
<app-accelerate-checkout *ngIf="(da$ | async) as da;" [txid]="tx.txid" [eta]="mempoolPosition?.block >= 7 ? null : da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" (close)="showAccelerationSummary = false"></app-accelerate-checkout>
|
||||
} @else {
|
||||
@if (tx?.acceleration && !tx.status?.confirmed) {
|
||||
<div class="progress-icon">
|
||||
|
|
|
@ -146,6 +146,11 @@ export class TrackerComponent implements OnInit, OnDestroy {
|
|||
if (this.acceleratorAvailable && this.stateService.ref === 'https://cash.app/') {
|
||||
this.paymentType = 'cashapp';
|
||||
}
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.get('cash_request_id')) {
|
||||
this.showAccelerationSummary = true;
|
||||
}
|
||||
this.showAccelerationSummary = true;
|
||||
|
||||
this.enterpriseService.page();
|
||||
|
||||
|
|
|
@ -132,8 +132,8 @@ export class ServicesApiServices {
|
|||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid, accelerationUUID: accelerationUUID });
|
||||
}
|
||||
|
||||
accelerateWithCashApp$(txInput: string, userBid: number, token: string, cashtag: string, referenceId: string, accelerationUUID: string) {
|
||||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate/cashapp`, { txInput: txInput, userBid: userBid, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID });
|
||||
accelerateWithCashApp$(txInput: string, token: string, cashtag: string, referenceId: string, accelerationUUID: string) {
|
||||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate/cashapp`, { txInput: txInput, token: token, cashtag: cashtag, referenceId: referenceId, accelerationUUID: accelerationUUID });
|
||||
}
|
||||
|
||||
getAccelerations$(): Observable<Acceleration[]> {
|
||||
|
|
Loading…
Add table
Reference in a new issue