[accelerator] accelerate with lightning
This commit is contained in:
		
							parent
							
								
									f1572f0038
								
							
						
					
					
						commit
						66a88b8422
					
				| @ -58,12 +58,24 @@ | |||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     </form> |     </form> | ||||||
|   } |  | ||||||
|    |    | ||||||
|   @else if (step === 'checkout') { |   } @else if (step === 'paymentMethod') { | ||||||
|     <!-- Show checkout page --> |  | ||||||
|     <div class="row mb-md-1 text-center"> |     <div class="row mb-md-1 text-center"> | ||||||
|       <div class="col-sm"> |       <div class="col-sm"> | ||||||
|  |         <h1 style="font-size: larger;">Select your payment method</h1> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <div class="pt-2 d-flex justify-content-around"> | ||||||
|  |       @if (cashappEnabled) { | ||||||
|  |         <img class="paymentMethod" src="/resources/cash-app.svg" height=55 (click)="selectPaymentMethod('cashapp')"> | ||||||
|  |       } | ||||||
|  |       <img class="paymentMethod" src="/resources/btcpay.svg" height=55 (click)="selectPaymentMethod('btcpay')"> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |   } @else if (step === 'checkout') { | ||||||
|  |     <!-- Show checkout page --> | ||||||
|  |     <div class="row mb-md-1 text-center"> | ||||||
|  |       <div class="col-sm" id="confirm-payment-title"> | ||||||
|         <h1 style="font-size: larger;">Confirm your payment</h1> |         <h1 style="font-size: larger;">Confirm your payment</h1> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| @ -76,6 +88,7 @@ | |||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|  |     @if (paymentMethod === 'cashapp') { | ||||||
|       @if (!loadingCashapp) { |       @if (!loadingCashapp) { | ||||||
|         <div class="row text-center mt-1"> |         <div class="row text-center mt-1"> | ||||||
|           <div class="col-sm"> |           <div class="col-sm"> | ||||||
| @ -105,6 +118,9 @@ | |||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |     } @else if (paymentMethod === 'btcpay' && invoice?.btcpayInvoiceId) { | ||||||
|  |       <app-bitcoin-invoice [invoiceId]="invoice.btcpayInvoiceId" (completed)="closeModal(2000)"></app-bitcoin-invoice> | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     <hr> |     <hr> | ||||||
|     <div class="row mt-2 mb-2 text-center"> |     <div class="row mt-2 mb-2 text-center"> | ||||||
| @ -118,7 +134,7 @@ | |||||||
|   @else if (step === 'processing') { |   @else if (step === 'processing') { | ||||||
|     <div class="row mb-1 text-center"> |     <div class="row mb-1 text-center"> | ||||||
|       <div class="col-sm"> |       <div class="col-sm"> | ||||||
|         <h1 style="font-size: larger;">Confirm your payment</h1> |         <h1 style="font-size: larger;">Confirming your payment</h1> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,3 +7,11 @@ | |||||||
| .estimating { | .estimating { | ||||||
|   color: var(--green) |   color: var(--green) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .paymentMethod { | ||||||
|  |   padding: 10px; | ||||||
|  |   background-color: var(--secondary); | ||||||
|  |   border-radius: 15px; | ||||||
|  |   border: 2px solid var(--bg); | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
| @ -16,12 +16,16 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   @Input() eta: number | null = null; |   @Input() eta: number | null = null; | ||||||
|   @Input() txid: string = '70c18d76cdb285a1b5bd87fdaae165880afa189809c30b4083ff7c0e69ee09ad'; |   @Input() txid: string = '70c18d76cdb285a1b5bd87fdaae165880afa189809c30b4083ff7c0e69ee09ad'; | ||||||
|   @Input() scrollEvent: boolean; |   @Input() scrollEvent: boolean; | ||||||
|  |   @Input() cashappEnabled: boolean; | ||||||
|   @Output() close = new EventEmitter<null>(); |   @Output() close = new EventEmitter<null>(); | ||||||
| 
 | 
 | ||||||
|   calculating = true; |   calculating = true; | ||||||
|   choosenOption: 'wait' | 'accelerate' = 'wait'; |   choosenOption: 'wait' | 'accelerate' = 'wait'; | ||||||
|   error = ''; |   error = ''; | ||||||
| 
 | 
 | ||||||
|  |   step: 'paymentMethod' | 'cta' | 'checkout' | 'processing' = 'cta'; | ||||||
|  |   paymentMethod: 'cashapp' | 'btcpay'; | ||||||
|  | 
 | ||||||
|   // accelerator stuff
 |   // accelerator stuff
 | ||||||
|   square: { appId: string, locationId: string}; |   square: { appId: string, locationId: string}; | ||||||
|   accelerationUUID: string; |   accelerationUUID: string; | ||||||
| @ -38,7 +42,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|   cashAppPay: any; |   cashAppPay: any; | ||||||
|   cashAppSubscription: Subscription; |   cashAppSubscription: Subscription; | ||||||
|   conversionsSubscription: Subscription; |   conversionsSubscription: Subscription; | ||||||
|   step: 'cta' | 'checkout' | 'processing' = 'cta'; |    | ||||||
|  |   // btcpay
 | ||||||
|  |   loadingBtcpayInvoice = false; | ||||||
|  |   invoice = undefined; | ||||||
| 
 | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     private servicesApiService: ServicesApiServices, |     private servicesApiService: ServicesApiServices, | ||||||
| @ -77,19 +84,19 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|   ngOnChanges(changes: SimpleChanges): void { |   ngOnChanges(changes: SimpleChanges): void { | ||||||
|     if (changes.scrollEvent) { |     if (changes.scrollEvent) { | ||||||
|       this.scrollToPreview('acceleratePreviewAnchor', 'start'); |       this.scrollToElement('acceleratePreviewAnchor', 'start'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|   * Scroll to element id with or without setTimeout |   * Scroll to element id with or without setTimeout | ||||||
|   */ |   */ | ||||||
|   scrollToPreviewWithTimeout(id: string, position: ScrollLogicalPosition) { |   scrollToElementWithTimeout(id: string, position: ScrollLogicalPosition, timeout: number = 1000) { | ||||||
|     setTimeout(() => { |     setTimeout(() => { | ||||||
|       this.scrollToPreview(id, position); |       this.scrollToElement(id, position); | ||||||
|     }, 1000); |     }, timeout); | ||||||
|   } |   } | ||||||
|   scrollToPreview(id: string, position: ScrollLogicalPosition) { |   scrollToElement(id: string, position: ScrollLogicalPosition) { | ||||||
|     const acceleratePreviewAnchor = document.getElementById(id); |     const acceleratePreviewAnchor = document.getElementById(id); | ||||||
|     if (acceleratePreviewAnchor) { |     if (acceleratePreviewAnchor) { | ||||||
|       this.cd.markForCheck(); |       this.cd.markForCheck(); | ||||||
| @ -111,7 +118,6 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     this.calculating = true; |     this.calculating = true; | ||||||
|     this.estimateSubscription = this.servicesApiService.estimate$(this.txid).pipe( |     this.estimateSubscription = this.servicesApiService.estimate$(this.txid).pipe( | ||||||
|       tap((response) => { |       tap((response) => { | ||||||
|         this.calculating = false; |  | ||||||
|         if (response.status === 204) { |         if (response.status === 204) { | ||||||
|           this.error = `cannot_accelerate_tx`; |           this.error = `cannot_accelerate_tx`; | ||||||
|         } else { |         } else { | ||||||
| @ -126,6 +132,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|           this.maxBidBoost = minExtraBoost * DEFAULT_BID_RATIO; |           this.maxBidBoost = minExtraBoost * DEFAULT_BID_RATIO; | ||||||
|           this.cost = this.maxBidBoost + this.estimate.mempoolBaseFee + this.estimate.vsizeFee; |           this.cost = this.maxBidBoost + this.estimate.mempoolBaseFee + this.estimate.vsizeFee; | ||||||
|           this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate); |           this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate); | ||||||
|  |           this.calculating = false; | ||||||
|  |           this.cd.markForCheck(); | ||||||
|         } |         } | ||||||
|       }), |       }), | ||||||
| 
 | 
 | ||||||
| @ -265,19 +273,48 @@ export class AccelerateCheckout implements OnInit, OnDestroy { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** | ||||||
|  |    * BTCPay | ||||||
|  |    */ | ||||||
|  |   async requestBTCPayInvoice() { | ||||||
|  |     this.servicesApiService.generateBTCPayAcceleratorInvoice$(this.txid).subscribe({ | ||||||
|  |       next: (response) => { | ||||||
|  |         this.invoice = response; | ||||||
|  |         this.cd.markForCheck(); | ||||||
|  |         this.scrollToElementWithTimeout('acceleratePreviewAnchor', 'start', 500); | ||||||
|  |       }, | ||||||
|  |       error: (response) => { | ||||||
|  |         console.log(response); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /** |   /** | ||||||
|    * UI events |    * UI events | ||||||
|    */ |    */ | ||||||
|   enableCheckoutPage() { |   enableCheckoutPage() { | ||||||
|  |     this.step = 'paymentMethod'; | ||||||
|  |   } | ||||||
|  |   selectPaymentMethod(paymentMethod: 'cashapp' | 'btcpay') { | ||||||
|     this.step = 'checkout'; |     this.step = 'checkout'; | ||||||
|  |     this.paymentMethod = paymentMethod; | ||||||
|  |     if (paymentMethod === 'cashapp') { | ||||||
|       this.loadingCashapp = true; |       this.loadingCashapp = true; | ||||||
|       this.insertSquare(); |       this.insertSquare(); | ||||||
|       this.setupSquare(); |       this.setupSquare(); | ||||||
|  |     } else if (paymentMethod === 'btcpay') { | ||||||
|  |       this.loadingBtcpayInvoice = true; | ||||||
|  |       this.requestBTCPayInvoice(); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   selectedOptionChanged(event) { |   selectedOptionChanged(event) { | ||||||
|     this.choosenOption = event.target.id; |     this.choosenOption = event.target.id; | ||||||
|   } |   } | ||||||
|   closeModal(): void { |   closeModal(timeout: number = 0): void { | ||||||
|  |     setTimeout(() => { | ||||||
|  |       this.step = 'processing'; | ||||||
|  |       this.cd.markForCheck(); | ||||||
|       this.close.emit(); |       this.close.emit(); | ||||||
|  |     }, timeout); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,89 @@ | |||||||
|  | <div class="wrapper"> | ||||||
|  | 
 | ||||||
|  |   <span *ngIf="paymentStatus === 3" class="valid-feedback d-block mt-5"> | ||||||
|  |     Payment successful. You can close this page. | ||||||
|  |   </span> | ||||||
|  |    | ||||||
|  |   <span *ngIf="paymentStatus === 4" class="valid-feedback d-block mt-5"> | ||||||
|  |     A transaction <a [href]="'/tx/' + invoice.cryptoInfo[0].payments[0].id.split('-')[0]">has been detected in the mempool</a> fully paying for this invoice. Waiting for on-chain confirmation. | ||||||
|  |   </span> | ||||||
|  | 
 | ||||||
|  |   <div *ngIf="paymentStatus === 2"> | ||||||
|  |      | ||||||
|  |     <form [formGroup]="paymentForm"> | ||||||
|  | 
 | ||||||
|  |       <div class="form-group"> | ||||||
|  |         <div class="btn-group btn-group-toggle" data-toggle="buttons"> | ||||||
|  |           <!-- <label *ngIf="invoice.addresses.BTC" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'chain'}"> | ||||||
|  |             <input type="radio" value="chain" formControlName="method"> <fa-icon [icon]="['fas', 'link']" [fixedWidth]="true" title="Onchain"></fa-icon> | ||||||
|  |           </label> --> | ||||||
|  |           <label *ngIf="invoice.addresses.BTC_LightningLike" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'lightning'}"> | ||||||
|  |             <input type="radio" value="lightning" formControlName="method"> <fa-icon [icon]="['fas', 'bolt']" [fixedWidth]="true" title="Lightning"></fa-icon> | ||||||
|  |           </label> | ||||||
|  |           <!-- <label *ngIf="invoice.addresses.LBTC" class="btn btn-primary" [ngClass]="{'active': paymentForm.get('method')?.value === 'lbtc'}"> | ||||||
|  |             <input type="radio" value="lbtc" formControlName="method"> <fa-icon [icon]="['fas', 'tint']" [fixedWidth]="true" title="Liquid Bitcoin"></fa-icon> | ||||||
|  |           </label> --> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |     </form> | ||||||
|  | 
 | ||||||
|  |     <ng-template [ngIf]="paymentForm.get('method')?.value === 'chain' && invoice"> | ||||||
|  | 
 | ||||||
|  |         <div class="qr-wrapper"> | ||||||
|  |           <a [href]="bypassSecurityTrustUrl('bitcoin:' + invoice.addresses.BTC + '?amount=' + invoice.amount)" target="_blank"> | ||||||
|  |               <app-qrcode imageUrl="/resources/bitcoin-logo.png" [size]="200" [data]="'bitcoin:' + invoice.addresses.BTC + '?amount=' + invoice.amount"></app-qrcode> | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  |          | ||||||
|  |         <div class="input-group input-group-sm info-group"> | ||||||
|  |           <input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.BTC"> | ||||||
|  |           <div class="input-group-append"> | ||||||
|  |               <button class="btn btn-outline-secondary" type="button" ><app-clipboard [text]="invoice.addresses.BTC"></app-clipboard></button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <p>{{ invoice.amount }} <span class="symbol">BTC</span></p> | ||||||
|  | 
 | ||||||
|  |     </ng-template> | ||||||
|  | 
 | ||||||
|  |     <ng-template [ngIf]="paymentForm.get('method')?.value === 'lightning' && invoice"> | ||||||
|  | 
 | ||||||
|  |         <div class="qr-wrapper"> | ||||||
|  |           <a [href]="bypassSecurityTrustUrl('lightning:' + invoice.addresses.BTC_LightningLike)" target="_blank"> | ||||||
|  |               <app-qrcode imageUrl="/resources/bitcoin-logo.png" [size]="200" [data]="invoice.addresses.BTC_LightningLike.toUpperCase()"></app-qrcode> | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <div class="input-group input-group-sm info-group"> | ||||||
|  |           <input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.BTC_LightningLike"> | ||||||
|  |           <div class="input-group-append"> | ||||||
|  |               <button class="btn btn-outline-secondary" type="button"><app-clipboard [text]="invoice.addresses.BTC_LightningLike"></app-clipboard></button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <p>{{ invoice.amount * 100_000_000 }} <span class="symbol">sats</span></p> | ||||||
|  | 
 | ||||||
|  |     </ng-template> | ||||||
|  | 
 | ||||||
|  |     <ng-template [ngIf]="invoice && (paymentForm.get('method')?.value === 'lbtc' || paymentForm.get('method')?.value === 'tlbtc')"> | ||||||
|  | 
 | ||||||
|  |         <div class="qr-wrapper"> | ||||||
|  |           <a [href]="bypassSecurityTrustUrl('liquidnetwork:' + invoice.addresses.LBTC + '?amount=' + invoice.amount + '&assetid=6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d')" target="_blank"> | ||||||
|  |               <app-qrcode imageUrl="/resources/liquid-bitcoin.png" [size]="200" [data]="'liquidnetwork:' + invoice.addresses.LBTC + '?amount=' + invoice.amount + '&assetid=6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d'"></app-qrcode> | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|  |         <br> | ||||||
|  |         <div class="input-group input-group-sm info-group"> | ||||||
|  |           <input type="text" class="form-control input-dark" readonly [value]="invoice.addresses.LBTC" /> | ||||||
|  |           <div class="input-group-append"> | ||||||
|  |               <button class="btn btn-outline-secondary" type="button" ><app-clipboard [text]="invoice.addresses.LBTC"></app-clipboard></button> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <p>{{ invoice.amount }} <span class="symbol">BTC</span></p> | ||||||
|  | 
 | ||||||
|  |     </ng-template> | ||||||
|  | 
 | ||||||
|  |     <p>Waiting for transaction... </p> | ||||||
|  |     <div class="spinner-border text-light"></div> | ||||||
|  |   </div> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,149 @@ | |||||||
|  | .form-panel { | ||||||
|  |   background-color: #292b45; | ||||||
|  |   padding: 20px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .sponsor-page { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .qr-wrapper { | ||||||
|  |     background-color: #FFF; | ||||||
|  |     padding: 10px; | ||||||
|  |     display: inline-block; | ||||||
|  |     padding-bottom: 5px; | ||||||
|  |     margin: 20px auto 0px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .info-group { | ||||||
|  |   max-width: 400px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card { | ||||||
|  |   width: 240px; | ||||||
|  |   height: 220px; | ||||||
|  |   background-color: var(--bg); | ||||||
|  |   border: 2px solid var(--bg); | ||||||
|  |   cursor: pointer; | ||||||
|  |   position: relative; | ||||||
|  |   transition: 100ms all; | ||||||
|  |   margin: 30px 30px 20px 30px; | ||||||
|  |   @media(min-width: 476px) { | ||||||
|  |     margin: 30px 100px 20px 100px; | ||||||
|  |   } | ||||||
|  |   @media(min-width: 851px) { | ||||||
|  |     margin: 60px 20px 40px 20px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .card-title { | ||||||
|  |     font-weight: bold; | ||||||
|  |     span { | ||||||
|  |       font-weight: 100; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.bigger { | ||||||
|  |     height: 220px; | ||||||
|  |     width: 240px; | ||||||
|  |     margin-top: 40px; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     background-color: #5058926b; | ||||||
|  |     border: 2px solid #505892; | ||||||
|  |     transform: scale(1.1) translateY(-10px); | ||||||
|  |     margin-top: 70px; | ||||||
|  | 
 | ||||||
|  |     .card-header { | ||||||
|  |       background-color: #505892; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .donation-form { | ||||||
|  |   max-width: 280px; | ||||||
|  |   margin: auto; | ||||||
|  |   button { | ||||||
|  |     width: 100%; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card-header { | ||||||
|  |   background-color: #171929; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .flex-container { | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: row; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   justify-content: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .middle-card { | ||||||
|  |   width: 280px; | ||||||
|  |   height: 260px; | ||||||
|  |   margin-top: 40px; | ||||||
|  |   &:hover { | ||||||
|  |     margin-top: 50px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .shiny-border { | ||||||
|  |   background-color: #5058926b; | ||||||
|  |   border: 2px solid #505892; | ||||||
|  |   transform: scale(1.1) translateY(-10px); | ||||||
|  |   margin-top: 70px; | ||||||
|  |   box-shadow: 0px 0px 100px #9858ff52; | ||||||
|  |   .card-header { | ||||||
|  |     background-color: #505892; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.middle-card { | ||||||
|  |     margin-top: 50px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .input-group { | ||||||
|  |   margin: 20px auto; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .donation-confirmed { | ||||||
|  |   h2 { | ||||||
|  |     margin-top: 50px; | ||||||
|  |     span { | ||||||
|  |       display: block; | ||||||
|  |       &:last-child { | ||||||
|  |         color: #9858ff; | ||||||
|  |         font-weight: bold; | ||||||
|  |         font-size: 2rem; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .order-details { | ||||||
|  |     margin-top: 50px; | ||||||
|  |     span { | ||||||
|  |       color: #d81b60; | ||||||
|  |       margin-left: 10px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .card-body { | ||||||
|  |   align-items: center; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: center; | ||||||
|  |   flex-direction: column; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .wrapper { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .input-dark { | ||||||
|  |   background-color: var(--bg); | ||||||
|  |   border-color: var(--active-bg); | ||||||
|  |   color: white; | ||||||
|  | } | ||||||
| @ -0,0 +1,94 @@ | |||||||
|  | import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; | ||||||
|  | import { FormBuilder, FormGroup } from '@angular/forms'; | ||||||
|  | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; | ||||||
|  | import { ActivatedRoute } from '@angular/router'; | ||||||
|  | import { Subscription, timer } from 'rxjs'; | ||||||
|  | import { retry, switchMap, tap } from 'rxjs/operators'; | ||||||
|  | import { ServicesApiServices } from '../../services/services-api.service'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'app-bitcoin-invoice', | ||||||
|  |   templateUrl: './bitcoin-invoice.component.html', | ||||||
|  |   styleUrls: ['./bitcoin-invoice.component.scss'] | ||||||
|  | }) | ||||||
|  | export class BitcoinInvoiceComponent implements OnInit, OnDestroy { | ||||||
|  |   @Input() invoiceId: string; | ||||||
|  |   @Input() redirect = true; | ||||||
|  |   @Output() completed = new EventEmitter(); | ||||||
|  | 
 | ||||||
|  |   paymentForm: FormGroup; | ||||||
|  |   requestSubscription: Subscription | undefined; | ||||||
|  |   paymentStatusSubscription: Subscription | undefined; | ||||||
|  |   invoice: any; | ||||||
|  |   paymentStatus = 1; // 1 - Waiting for invoice | 2 - Pending payment | 3 - Payment completed
 | ||||||
|  |   paramMapSubscription: Subscription | undefined; | ||||||
|  |   invoiceSubscription: Subscription | undefined; | ||||||
|  |   invoiceTimeout; // Wait for angular to load all the things before making a request
 | ||||||
|  | 
 | ||||||
|  |   constructor( | ||||||
|  |     private formBuilder: FormBuilder, | ||||||
|  |     private apiService: ServicesApiServices, | ||||||
|  |     private sanitizer: DomSanitizer, | ||||||
|  |     private activatedRoute: ActivatedRoute | ||||||
|  |   ) { } | ||||||
|  | 
 | ||||||
|  |   ngOnDestroy() { | ||||||
|  |     if (this.requestSubscription) { | ||||||
|  |       this.requestSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |     if (this.paramMapSubscription) { | ||||||
|  |       this.paramMapSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |     if (this.invoiceSubscription) { | ||||||
|  |       this.invoiceSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |     if (this.paymentStatusSubscription) { | ||||||
|  |       this.paymentStatusSubscription.unsubscribe(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ngOnInit(): void { | ||||||
|  |     this.paymentForm = this.formBuilder.group({ | ||||||
|  |       'method': 'lightning' | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * If the invoice is passed in the url, fetch it and display btcpay payment | ||||||
|  |      * Otherwise get a new invoice | ||||||
|  |      */ | ||||||
|  |     this.paramMapSubscription = this.activatedRoute.paramMap | ||||||
|  |       .pipe( | ||||||
|  |         tap((paramMap) => { | ||||||
|  |           const invoiceId = paramMap.get('invoiceId') ?? this.invoiceId; | ||||||
|  |           if (invoiceId) { | ||||||
|  |             this.paymentStatusSubscription = this.apiService.retreiveInvoice$(invoiceId).pipe( | ||||||
|  |               tap((invoice: any) => { | ||||||
|  |                 this.invoice = invoice; | ||||||
|  |                 this.invoice.amount = invoice.btcDue ?? (invoice.cryptoInfo.length ? parseFloat(invoice.cryptoInfo[0].totalDue) : 0) ?? 0; | ||||||
|  | 
 | ||||||
|  |                 if (this.invoice.amount > 0) { | ||||||
|  |                   this.paymentStatus = 2; | ||||||
|  |                 } else { | ||||||
|  |                   this.paymentStatus = 4; | ||||||
|  |                 } | ||||||
|  |               }), | ||||||
|  |               switchMap(() => this.apiService.getPaymentStatus$(this.invoice.id) | ||||||
|  |                 .pipe( | ||||||
|  |                   retry({ delay: () => timer(2000)}) | ||||||
|  |                 ) | ||||||
|  |               ), | ||||||
|  |             ).subscribe({ | ||||||
|  |               next: ((result) => { | ||||||
|  |                 this.paymentStatus = 3; | ||||||
|  |                 this.completed.emit(); | ||||||
|  |               }), | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }) | ||||||
|  |       ).subscribe(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bypassSecurityTrustUrl(text: string): SafeUrl { | ||||||
|  |     return this.sanitizer.bypassSecurityTrustUrl(text); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -75,7 +75,7 @@ | |||||||
|                   } @else { |                   } @else { | ||||||
|                     <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> |                     <app-time kind="until" [time]="eta.time" [fastRender]="false" [fixedRender]="true"></app-time> | ||||||
|                   } |                   } | ||||||
|                   @if (!showAccelerationSummary && isMobile && paymentType === 'cashapp' && accelerationEligible && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { |                   @if (!showAccelerationSummary && isMobile && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) { | ||||||
|                     <a 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> |                 </span> | ||||||
| @ -116,7 +116,7 @@ | |||||||
| 
 | 
 | ||||||
|     <div class="bottom-panel"> |     <div class="bottom-panel"> | ||||||
|       @if (showAccelerationSummary && !accelerationFlowCompleted) { |       @if (showAccelerationSummary && !accelerationFlowCompleted) { | ||||||
|         <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)="accelerationFlowCompleted = true" [scrollEvent]="scrollIntoAccelPreview" class="h-100 w-100"></app-accelerate-checkout> |         <app-accelerate-checkout *ngIf="(da$ | async) as da;" [cashappEnabled]="accelerationEligible" [txid]="tx.txid" [eta]="mempoolPosition?.block >= 7 ? null : da.adjustedTimeAvg * (mempoolPosition.block + 1) + now + da.timeOffset" (close)="accelerationFlowCompleted = true" [scrollEvent]="scrollIntoAccelPreview" class="h-100 w-100"></app-accelerate-checkout> | ||||||
|       } @else { |       } @else { | ||||||
|         @if (tx?.acceleration && !tx.status?.confirmed) { |         @if (tx?.acceleration && !tx.status?.confirmed) { | ||||||
|           <div class="progress-icon"> |           <div class="progress-icon"> | ||||||
|  | |||||||
| @ -107,7 +107,6 @@ export class TrackerComponent implements OnInit, OnDestroy { | |||||||
|   now = Date.now(); |   now = Date.now(); | ||||||
|   da$: Observable<DifficultyAdjustment>; |   da$: Observable<DifficultyAdjustment>; | ||||||
|   isMobile: boolean; |   isMobile: boolean; | ||||||
|   paymentType: 'bitcoin' | 'cashapp' = 'bitcoin'; |  | ||||||
| 
 | 
 | ||||||
|   trackerStage: TrackerStage = 'waiting'; |   trackerStage: TrackerStage = 'waiting'; | ||||||
| 
 | 
 | ||||||
| @ -158,9 +157,6 @@ export class TrackerComponent implements OnInit, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; |     this.acceleratorAvailable = this.stateService.env.OFFICIAL_MEMPOOL_SPACE && this.stateService.env.ACCELERATOR && this.stateService.network === ''; | ||||||
| 
 | 
 | ||||||
|     if (this.acceleratorAvailable && this.stateService.referrer === 'https://cash.app/') { |  | ||||||
|       this.paymentType = 'cashapp'; |  | ||||||
|     } |  | ||||||
|     const urlParams = new URLSearchParams(window.location.search); |     const urlParams = new URLSearchParams(window.location.search); | ||||||
|     if (urlParams.get('cash_request_id')) { |     if (urlParams.get('cash_request_id')) { | ||||||
|       this.showAccelerationSummary = true; |       this.showAccelerationSummary = true; | ||||||
| @ -390,11 +386,9 @@ export class TrackerComponent implements OnInit, OnDestroy { | |||||||
|             this.trackerStage = 'replaced'; |             this.trackerStage = 'replaced'; | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|  |           this.showAccelerationSummary = true; | ||||||
|           if (txPosition.position?.block > 0 && this.tx.weight < 4000) { |           if (txPosition.position?.block > 0 && this.tx.weight < 4000) { | ||||||
|             this.accelerationEligible = true; |             this.accelerationEligible = true; | ||||||
|             if (this.acceleratorAvailable && this.paymentType === 'cashapp') { |  | ||||||
|               this.showAccelerationSummary = true; |  | ||||||
|             } |  | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } else { |       } else { | ||||||
|  | |||||||
| @ -167,4 +167,19 @@ export class ServicesApiServices { | |||||||
|   requestTestnet4Coins$(address: string, sats: number) { |   requestTestnet4Coins$(address: string, sats: number) { | ||||||
|     return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' }); |     return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' }); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   generateBTCPayAcceleratorInvoice$(txid: string): Observable<any> { | ||||||
|  |     const params = { | ||||||
|  |       product: txid | ||||||
|  |     }; | ||||||
|  |     return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/payments/bitcoin`, params); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   retreiveInvoice$(invoiceId: string): Observable<any[]> { | ||||||
|  |     return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/invoice?id=${invoiceId}`); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getPaymentStatus$(orderId: string): Observable<any[]> { | ||||||
|  |     return this.httpClient.get<any[]>(`${SERVICES_API_PREFIX}/payments/bitcoin/check?order_id=${orderId}`); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -115,6 +115,7 @@ import { HttpErrorComponent } from '../shared/components/http-error/http-error.c | |||||||
| import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component'; | import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component'; | ||||||
| import { FaucetComponent } from '../components/faucet/faucet.component'; | import { FaucetComponent } from '../components/faucet/faucet.component'; | ||||||
| import { TwitterLogin } from '../components/twitter-login/twitter-login.component'; | import { TwitterLogin } from '../components/twitter-login/twitter-login.component'; | ||||||
|  | import { BitcoinInvoiceComponent } from '../components/bitcoin-invoice/bitcoin-invoice.component'; | ||||||
| 
 | 
 | ||||||
| import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives'; | import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives'; | ||||||
| 
 | 
 | ||||||
| @ -230,6 +231,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     FaucetComponent, |     FaucetComponent, | ||||||
|     TwitterLogin, |     TwitterLogin, | ||||||
|  |     BitcoinInvoiceComponent, | ||||||
|   ], |   ], | ||||||
|   imports: [ |   imports: [ | ||||||
|     CommonModule, |     CommonModule, | ||||||
| @ -359,6 +361,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir | |||||||
|     HttpErrorComponent, |     HttpErrorComponent, | ||||||
|     TwitterWidgetComponent, |     TwitterWidgetComponent, | ||||||
|     TwitterLogin, |     TwitterLogin, | ||||||
|  |     BitcoinInvoiceComponent, | ||||||
| 
 | 
 | ||||||
|     MempoolBlockOverviewComponent, |     MempoolBlockOverviewComponent, | ||||||
|     ClockchainComponent, |     ClockchainComponent, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user