diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html index 3c8571dd4..65fea39a3 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.html @@ -65,24 +65,26 @@
-
How much more are you willing to pay?
-
-
- Choose the maximum extra transaction fee you're willing to pay to get into the next block. -
-
-
- - - + @if (paymentType !== 'cashapp') { +
How much more are you willing to pay?
+
+
+ Choose the maximum extra transaction fee you're willing to pay to get into the next block. +
+
+
+ + + +
-
+ }
Acceleration summary
@@ -90,27 +92,51 @@ - - - - - - - - - - - - + @if (paymentType === 'cashapp') { + + + + + + + + + + + + + } @else { + + + + + + + + + + + + + } @@ -141,53 +167,76 @@ - - - - - - - - - - - + + @if (paymentType === 'cashapp') { + + + + + + + + + } @else { + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + } @@ -237,14 +286,17 @@ -
-
Accelerate with
-
-
- Loading -
+ @if (!hideCashApp && paymentType === 'cashapp') { +
+
+
Accelerate for with
+
+
+ Loading +
+
-
+ }
diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss index 2e2b19ee8..e9ff233ae 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.scss @@ -109,4 +109,53 @@ .item { white-space: initial; +} + +.cashapp-cta { + width: 100%; + height: 54px; + background: #653b9c; + position: relative; + bottom: initial; + top: initial; + border-radius: 3px; + font-size: 14px; + line-height: 16px; + text-align: center; + padding: 4px 6px; + cursor: pointer; + box-shadow: 0px 0px 15px 0px #000; + + &.sticky-top { + position: fixed; + width: calc(100vw - 30px - 1.5rem); + margin: auto; + z-index: 50; + left: 0; + right: 0; + top: 102px; + @media (min-width: 573px) { + top: 62px; + } + } + &.sticky-bottom { + position: fixed; + width: calc(100vw - 30px - 1.5rem); + margin: auto; + z-index: 50; + left: 0; + right: 0; + bottom: 50px; + @media (min-width: 430px) { + bottom: 56px; + } + } +} + +.cashapp-placeholder { + height: 54px; + + &.non-stick { + height: 0px; + } } \ No newline at end of file diff --git a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts index aee0189aa..e88337ac3 100644 --- a/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts +++ b/frontend/src/app/components/accelerate-preview/accelerate-preview.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, Input, OnDestroy, OnChanges, SimpleChanges, HostListener, ChangeDetectorRef, ViewChild, ElementRef } from '@angular/core'; import { Subscription, catchError, of, tap } from 'rxjs'; import { StorageService } from '../../services/storage.service'; import { Transaction } from '../../interfaces/electrs.interface'; @@ -43,6 +43,9 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges @Input() tx: Transaction | undefined; @Input() scrollEvent: boolean; + @ViewChild('cashappCTA') + cashappCTA: ElementRef; + math = Math; error = ''; showSuccess = false; @@ -59,6 +62,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges selectFeeRateIndex = 1; isMobile: boolean = window.innerWidth <= 767.98; user: any = undefined; + stickyCTA: string = 'non-stick'; maxRateOptions: RateOption[] = []; @@ -66,6 +70,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges paymentType: 'bitcoin' | 'cashapp' = 'bitcoin'; cashAppSubscription: Subscription; conversionsSubscription: Subscription; + cashappSubmit: any; payments: any; showSpinner = false; square: any; @@ -96,14 +101,14 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges ngOnInit() { if (this.stateService.ref === 'https://cash.app/') { this.paymentType = 'cashapp'; - this.stateService.ref = ''; } else { this.paymentType = 'bitcoin'; } + this.onScroll(); } ngOnChanges(changes: SimpleChanges): void { - if (changes.scrollEvent) { + if (changes.scrollEvent && this.paymentType !== 'cashapp') { this.scrollToPreview('acceleratePreviewAnchor', 'start'); } } @@ -173,10 +178,11 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges this.maxCost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee; if (!this.error) { - this.scrollToPreview('acceleratePreviewAnchor', 'start'); if (this.paymentType === 'cashapp') { this.setupSquare(); - } + } else { + this.scrollToPreview('acceleratePreviewAnchor', 'start'); + } } } }), @@ -310,13 +316,15 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges label: 'Total', pending: true, productUrl: `https://mempool.space/tx/${this.tx.txid}`, - } + }, + button: { shape: 'semiround', size: 'small', theme: 'light'} }); this.cashAppPay = await this.payments.cashAppPay(paymentRequest, { redirectURL: `https://mempool.space/tx/${this.tx.txid}`, referenceId: `accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`, + button: { shape: 'semiround', size: 'small', theme: 'light'} }); - await this.cashAppPay.attach('#cash-app-pay'); + const renderPromise = this.cashAppPay.CashAppPayInstance.render('#cash-app-pay', { button: { theme: 'light', size: 'small', shape: 'semiround' }, manage: false }); this.showSpinner = false; const that = this; @@ -351,6 +359,8 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges }); } }); + + this.cashappSubmit = await renderPromise; } ); } @@ -367,4 +377,28 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges g.type='text/javascript'; g.src=statsUrl; s.parentNode.insertBefore(g, s); })(); } + + submitCashappPay(): void { + if (this.cashappSubmit) { + this.cashappSubmit?.begin(); + } + } + + @HostListener('window:scroll', ['$event']) // for window scroll events + onScroll() { + if (!this.cashappCTA?.nativeElement || this.paymentType !== 'cashapp' || !this.isMobile) { + return; + } + const cta = this.cashappCTA.nativeElement; + const rect = cta.getBoundingClientRect(); + const topOffset = window.innerWidth <= 572 ? 102 : 62; + const bottomOffset = window.innerWidth < 430 ? 50 : 56; + if (rect.top < topOffset) { + this.stickyCTA = 'sticky-top'; + } else if (rect.top > window.innerHeight - (bottomOffset + 54)) { + this.stickyCTA = 'sticky-bottom'; + } else { + this.stickyCTA = 'non-stick'; + } + } } diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index b7e9d16d3..7662f2eda 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -80,7 +80,9 @@

Accelerate

- + @if (!(isMobile && paymentType === 'cashapp')) { + + }
@@ -530,7 +532,7 @@ } @else if (this.mempoolPosition.block >= 7) { In several hours (or more) - @if (!tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { + @if (!(isMobile && paymentType === 'cashapp') && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { Accelerate } @@ -539,7 +541,7 @@ } @else { - @if (!tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { + @if (!(isMobile && paymentType === 'cashapp') && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { Accelerate } diff --git a/frontend/src/app/components/transaction/transaction.component.scss b/frontend/src/app/components/transaction/transaction.component.scss index bfdd4cc03..2d396383a 100644 --- a/frontend/src/app/components/transaction/transaction.component.scss +++ b/frontend/src/app/components/transaction/transaction.component.scss @@ -311,6 +311,13 @@ } } +.accelerateFullSize { + width: 100%; + height: 100%; + padding: 0.5rem 0.25rem; + background-color: #653b9c; +} + .goggles-icon { display: block; width: 2.2em; diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 5df80fb1c..5b0239d79 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -117,6 +117,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { flowEnabled: boolean; tooltipPosition: { x: number, y: number }; isMobile: boolean; + paymentType: 'bitcoin' | 'cashapp' = 'bitcoin'; + firstLoad = true; featuresEnabled: boolean; segwitEnabled: boolean; @@ -154,6 +156,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { ngOnInit() { this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; + if (this.acceleratorAvailable && this.stateService.ref === 'https://cash.app/') { + this.showAccelerationSummary = true; + this.paymentType = 'cashapp'; + } + this.enterpriseService.page(); this.websocketService.want(['blocks', 'mempool-blocks']); @@ -720,6 +727,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } resetTransaction() { + if (!this.firstLoad) { + this.stateService.ref = ''; + } else { + this.firstLoad = false; + } this.error = undefined; this.tx = null; this.setFeatures(); @@ -814,6 +826,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } ngOnDestroy() { + this.stateService.ref = ''; this.subscription.unsubscribe(); this.fetchCpfpSubscription.unsubscribe(); this.fetchRbfSubscription.unsubscribe();
Next block market rate - {{ estimate.targetFeeRate | number : '1.0-0' }} - sat/vB
- Estimated extra fee required - - {{ math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee) | number }} - - sats - -
Boost rate + {{ maxRateOptions[selectFeeRateIndex].rate | number : '1.0-0' }} + sat/vB
+ Boost fee + + {{ maxRateOptions[selectFeeRateIndex].fee | number }} + + sats + +
Next block market rate + {{ estimate.targetFeeRate | number : '1.0-0' }} + sat/vB
+ Estimated extra fee required + + {{ math.max(0, estimate.nextBlockFee - estimate.txSummary.effectiveFee) | number }} + + sats + +
- Estimated acceleration cost - - - {{ estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee | number }} - - - sats - -
- -
+ Total cost + + + {{ maxCost | number }} + + + sats + + + +
+ Estimated acceleration cost + + + {{ estimate.cost + estimate.mempoolBaseFee + estimate.vsizeFee | number }} + + + sats + +
+ +
- Maximum acceleration cost - - - {{ maxCost | number }} - - - sats - - - -
- -
+ Maximum acceleration cost + + + {{ maxCost | number }} + + + sats + + + +
+ +