Merge branch 'master' into orangesurf/2024-07-19

This commit is contained in:
orangesurf
2024-07-25 18:47:27 +09:00
committed by GitHub
30 changed files with 566 additions and 115 deletions

View File

@@ -389,16 +389,22 @@
</div>
}
</div>
@if (canPayWithCashapp) {
@if (canPayWithCashapp || canPayWithApplePay) {
<div class="col-sm text-center flex-grow-0 d-flex flex-column justify-content-center align-items-center">
<p class="text-nowrap">&mdash;<span i18n="or">OR</span>&mdash;</p>
</div>
}
}
@if (canPayWithCashapp) {
@if (canPayWithCashapp || canPayWithApplePay) {
<div class="col-sm text-center d-flex flex-column justify-content-center align-items-center">
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container>&nbsp;<app-fiat [value]="cost"></app-fiat> with</p>
<img class="paymentMethod mx-2" src="/resources/cash-app.svg" height=55 (click)="moveToStep('cashapp')">
@if (canPayWithCashapp) {
<img class="paymentMethod mx-2" style="width: 200px" src="/resources/cash-app.svg" height=55 (click)="moveToStep('cashapp')">
}
@if (canPayWithApplePay) {
@if (canPayWithCashapp) { <hr class="w-25 mt-2 mb-2"> }
<img style="cursor: pointer;" src="/resources/apple-pay.svg" height=55 (click)="moveToStep('applepay')">
}
</div>
}
</div>
@@ -421,9 +427,9 @@
<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>
</div>
} @else if (step === 'cashapp') {
} @else if (step === 'cashapp' || step === 'applepay') {
<!-- Show checkout page -->
<div class="row mb-md-1 text-center">
<div class="row mb-md-1 text-center" id="confirm-title">
<div class="col-sm" id="confirm-payment-title">
<h1 style="font-size: larger;"><ng-content select="[slot='checkout-title']"></ng-content><span class="default-slot" i18n="accelerator.confirm-your-payment">Confirm your payment</span></h1>
</div>
@@ -437,7 +443,7 @@
</div>
</div>
@if (!loadingCashapp) {
@if (step === 'cashapp' && !loadingCashapp || step === 'applepay' && !loadingApplePay) {
<div class="row text-center mt-1">
<div class="col-sm">
<div class="form-group w-100">
@@ -456,8 +462,12 @@
<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) {
@if (step === 'applepay') {
<div id="apple-pay-button" class="apple-pay-button apple-pay-button-white" [style]="loadingApplePay ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
} @else if (step === 'cashapp') {
<div id="cash-app-pay" class="d-inline-block" [style]="loadingCashapp ? 'opacity: 0; width: 0px; height: 0px; pointer-events: none;' : ''"></div>
}
@if (loadingCashapp || loadingApplePay) {
<div display="d-flex flex-row justify-content-center">
<span i18n="accelerator.loading-payment-method">Loading payment method...</span>
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
@@ -549,7 +559,7 @@
<button type="button" *ngIf="advancedEnabled" class="btn btn-sm btn-outline-info btn-small-height ml-2" (click)="moveToStep('quote')" i18n="accelerator.customize">customize</button>
</ng-template>
<ng-template #accelerateTo let-x i18n="accelerator.accelerate-to-x">Accelerate to ~{{ x | number : '1.0-0' }} sat/vB</ng-template>
<ng-template id="accelerate-to" #accelerateTo let-x i18n="accelerator.accelerate-to-x">Accelerate to ~{{ x | number : '1.0-0' }} sat/vB</ng-template>
<ng-template #accelerateButton>
<div class="position-relative">

View File

@@ -11,8 +11,7 @@
.paymentMethod {
padding: 10px;
background-color: var(--secondary);
border-radius: 15px;
border: 2px solid var(--bg);
border-radius: 10px;
cursor: pointer;
}
@@ -202,4 +201,19 @@
.btn-error-wrapper {
height: 26px;
}
.apple-pay-button {
display: inline-block;
-webkit-appearance: -apple-pay-button;
-apple-pay-button-type: plain; /* Use any supported button type. */
}
.apple-pay-button-black {
-apple-pay-button-style: black;
}
.apple-pay-button-white {
-apple-pay-button-style: white;
}
.apple-pay-button-white-with-line {
-apple-pay-button-style: white-outline;
}

View File

@@ -1,7 +1,7 @@
import { Component, OnInit, OnDestroy, Output, EventEmitter, Input, ChangeDetectorRef, SimpleChanges, HostListener } from '@angular/core';
import { Subscription, tap, of, catchError, Observable, switchMap } from 'rxjs';
import { ServicesApiServices } from '../../services/services-api.service';
import { nextRoundNumber, insecureRandomUUID } from '../../shared/common.utils';
import { md5, nextRoundNumber, insecureRandomUUID } from '../../shared/common.utils';
import { StateService } from '../../services/state.service';
import { AudioService } from '../../services/audio.service';
import { ETA, EtaService } from '../../services/eta.service';
@@ -9,6 +9,7 @@ import { Transaction } from '../../interfaces/electrs.interface';
import { MiningStats } from '../../services/mining.service';
import { IAuth, AuthServiceMempool } from '../../services/auth.service';
import { EnterpriseService } from '../../services/enterprise.service';
import { ApiService } from '../../services/api.service';
export type PaymentMethod = 'balance' | 'bitcoin' | 'cashapp';
@@ -46,7 +47,7 @@ export const MIN_BID_RATIO = 1;
export const DEFAULT_BID_RATIO = 2;
export const MAX_BID_RATIO = 4;
type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'processing' | 'paid' | 'success';
type CheckoutStep = 'quote' | 'summary' | 'checkout' | 'cashapp' | 'applepay' | 'processing' | 'paid' | 'success';
@Component({
selector: 'app-accelerate-checkout',
@@ -60,6 +61,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
@Input() eta: ETA;
@Input() scrollEvent: boolean;
@Input() cashappEnabled: boolean = true;
@Input() applePayEnabled: boolean = false;
@Input() advancedEnabled: boolean = false;
@Input() forceMobile: boolean = false;
@Input() showDetails: boolean = false;
@@ -109,11 +111,12 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
// square
loadingCashapp = false;
loadingApplePay = false;
cashappError = false;
cashappSubmit: any;
payments: any;
cashAppPay: any;
cashAppSubscription: Subscription;
applePay: any;
conversionsSubscription: Subscription;
conversions: any;
@@ -123,6 +126,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
constructor(
public stateService: StateService,
private apiService: ApiService,
private servicesApiService: ServicesApiServices,
private etaService: EtaService,
private audioService: AudioService,
@@ -131,6 +135,12 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
private enterpriseService: EnterpriseService,
) {
this.accelerationUUID = insecureRandomUUID();
// Check if Apple Pay available
// @ts-ignore https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/checking_for_apple_pay_availability#overview
if (window.ApplePaySession) {
this.applePayEnabled = true;
}
}
ngOnInit() {
@@ -212,6 +222,12 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.loadingCashapp = true;
this.insertSquare();
this.setupSquare();
this.scrollToElementWithTimeout('confirm-title', 'center', 100);
} else if (this._step === 'applepay' && this.applePayEnabled) {
this.loadingApplePay = true;
this.insertSquare();
this.setupSquare();
this.scrollToElementWithTimeout('confirm-title', 'center', 100);
} else if (this._step === 'paid') {
this.timePaid = Date.now();
this.timeoutTimer = setTimeout(() => {
@@ -229,8 +245,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
/**
* Scroll to element id with or without setTimeout
*/
* Scroll to element id with or without setTimeout
*/
scrollToElementWithTimeout(id: string, position: ScrollLogicalPosition, timeout: number = 1000): void {
setTimeout(() => {
this.scrollToElement(id, position);
@@ -370,10 +386,11 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
this.accelerationUUID
).subscribe({
next: () => {
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
this.showSuccess = true;
this.estimateSubscription.unsubscribe();
this.moveToStep('paid')
this.moveToStep('paid');
},
error: (response) => {
this.accelerateError = response.error;
@@ -421,17 +438,113 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
try {
//@ts-ignore
this.payments = window.Square.payments(this.square.appId, this.square.locationId)
await this.requestCashAppPayment();
const urlParams = new URLSearchParams(window.location.search);
if (this._step === 'cashapp' || urlParams.get('cash_request_id')) {
await this.requestCashAppPayment();
} else if (this._step === 'applepay') {
await this.requestApplePayPayment();
}
} catch (e) {
console.debug('Error loading Square Payments', e);
this.cashappError = true;
return;
}
}
async requestCashAppPayment() {
if (this.cashAppSubscription) {
this.cashAppSubscription.unsubscribe();
/**
* APPLE PAY
*/
async requestApplePayPayment() {
if (this.conversionsSubscription) {
this.conversionsSubscription.unsubscribe();
}
this.conversionsSubscription = this.stateService.conversions$.subscribe(
async (conversions) => {
this.conversions = conversions;
if (this.applePay) {
this.applePay.destroy();
}
const costUSD = this.cost / 100_000_000 * conversions.USD;
const paymentRequest = this.payments.paymentRequest({
countryCode: 'US',
currencyCode: 'USD',
total: {
amount: costUSD.toFixed(2),
label: 'Total',
},
});
try {
this.applePay = await this.payments.applePay(paymentRequest);
const applePayButton = document.getElementById('apple-pay-button');
if (!applePayButton) {
console.error(`Unable to find apple pay button id='apple-pay-button'`);
// Try again
setTimeout(this.requestApplePayPayment.bind(this), 500);
return;
}
this.loadingApplePay = false;
applePayButton.addEventListener('click', async event => {
event.preventDefault();
const tokenResult = await this.applePay.tokenize();
if (tokenResult?.status === 'OK') {
const card = tokenResult.details?.card;
if (!card || !card.brand || !card.expMonth || !card.expYear || !card.last4) {
console.error(`Cannot retreive payment card details`);
this.accelerateError = 'apple_pay_no_card_details';
return;
}
const cardTag = md5(`${card.brand}${card.expMonth}${card.expYear}${card.last4}`.toLowerCase());
this.servicesApiService.accelerateWithApplePay$(
this.tx.txid,
tokenResult.token,
cardTag,
`accelerator-${this.tx.txid.substring(0, 15)}-${Math.round(new Date().getTime() / 1000)}`,
this.accelerationUUID
).subscribe({
next: () => {
this.audioService.playSound('ascend-chime-cartoon');
if (this.applePay) {
this.applePay.destroy();
}
setTimeout(() => {
this.moveToStep('paid');
}, 1000);
},
error: (response) => {
this.accelerateError = response.error;
if (!(response.status === 403 && response.error === 'not_available')) {
setTimeout(() => {
// Reset everything by reloading the page :D, can be improved
const urlParams = new URLSearchParams(window.location.search);
window.location.assign(window.location.toString().replace(`?cash_request_id=${urlParams.get('cash_request_id')}`, ``));
}, 3000);
}
}
});
} else {
let errorMessage = `Tokenization failed with status: ${tokenResult.status}`;
if (tokenResult.errors) {
errorMessage += ` and errors: ${JSON.stringify(
tokenResult.errors,
)}`;
}
throw new Error(errorMessage);
}
});
} catch (e) {
console.error(e);
}
}
);
}
/**
* CASHAPP
*/
async requestCashAppPayment() {
if (this.conversionsSubscription) {
this.conversionsSubscription.unsubscribe();
}
@@ -449,7 +562,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
countryCode: 'US',
currencyCode: 'USD',
total: {
amount: costUSD.toString(),
amount: costUSD.toFixed(2),
label: 'Total',
pending: true,
productUrl: `${redirectHostname}/tracker/${this.tx.txid}`,
@@ -467,23 +580,23 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
this.loadingCashapp = false;
const that = this;
this.cashAppPay.addEventListener('ontokenization', function (event) {
this.cashAppPay.addEventListener('ontokenization', event => {
const { tokenResult, error } = event.detail;
if (error) {
this.accelerateError = error;
} else if (tokenResult.status === 'OK') {
that.servicesApiService.accelerateWithCashApp$(
that.tx.txid,
this.servicesApiService.accelerateWithCashApp$(
this.tx.txid,
tokenResult.token,
tokenResult.details.cashAppPay.cashtag,
tokenResult.details.cashAppPay.referenceId,
that.accelerationUUID
this.accelerationUUID
).subscribe({
next: () => {
that.audioService.playSound('ascend-chime-cartoon');
if (that.cashAppPay) {
that.cashAppPay.destroy();
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
if (this.cashAppPay) {
this.cashAppPay.destroy();
}
setTimeout(() => {
this.moveToStep('paid');
@@ -494,7 +607,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}, 1000);
},
error: (response) => {
that.accelerateError = response.error;
this.accelerateError = response.error;
if (!(response.status === 403 && response.error === 'not_available')) {
setTimeout(() => {
// Reset everything by reloading the page :D, can be improved
@@ -530,9 +643,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
bitcoinPaymentCompleted(): void {
this.apiService.logAccelerationRequest$(this.tx.txid).subscribe();
this.audioService.playSound('ascend-chime-cartoon');
this.estimateSubscription.unsubscribe();
this.moveToStep('paid')
this.moveToStep('paid');
}
isLoggedIn(): boolean {
@@ -565,6 +679,13 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
return !!this.estimate?.availablePaymentMethods?.cashapp;
}
get couldPayWithApplePay() {
if (!this.applePayEnabled) {
return false;
}
return !!this.estimate?.availablePaymentMethods?.applePay;
}
get couldPayWithBalance() {
if (!this.hasAccessToBalanceMode) {
return false;
@@ -573,7 +694,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
get couldPay() {
return this.couldPayWithBalance || this.couldPayWithBitcoin || this.couldPayWithCashapp;
return this.couldPayWithBalance || this.couldPayWithBitcoin || this.couldPayWithCashapp || this.couldPayWithApplePay;
}
get canPayWithBitcoin() {
@@ -593,7 +714,23 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
return true;
}
}
return false;
}
get canPayWithApplePay() {
if (!this.applePayEnabled || !this.conversions) {
return false;
}
const paymentMethod = this.estimate?.availablePaymentMethods?.applePay;
if (paymentMethod) {
const costUSD = (this.cost / 100_000_000 * this.conversions.USD);
if (costUSD >= paymentMethod.min && costUSD <= paymentMethod.max) {
return true;
}
}
return false;
}
@@ -606,7 +743,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
}
get canPay() {
return this.canPayWithBalance || this.canPayWithBitcoin || this.canPayWithCashapp;
return this.canPayWithBalance || this.canPayWithBitcoin || this.canPayWithCashapp || this.canPayWithApplePay;
}
get hasAccessToBalanceMode() {

View File

@@ -16,6 +16,7 @@
<ng-container *ngIf="!pending">
<th class="fee text-right" i18n="transaction.bid-boost|Bid Boost">Bid Boost</th>
<th class="block text-right" i18n="shared.block-title">Block</th>
<th class="pool text-right" i18n="mining.pool-name" *ngIf="!this.widget">Pool</th>
<th class="status text-right" i18n="transaction.status|Transaction Status">Status</th>
<th class="date text-right" i18n="accelerator.requested" *ngIf="!this.widget">Requested</th>
</ng-container>
@@ -49,6 +50,16 @@
<a *ngIf="acceleration.blockHeight" [routerLink]="['/block' | relativeUrl, acceleration.blockHeight]">{{ acceleration.blockHeight }}</a>
<span *ngIf="!acceleration.blockHeight">~</span>
</td>
<td class="pool text-right" *ngIf="!this.widget">
@if (acceleration.minedByPoolUniqueId && pools[acceleration.minedByPoolUniqueId]) {
<a placement="bottom" [routerLink]="['/mining/pool' | relativeUrl, pools[acceleration.minedByPoolUniqueId].slug]" class="badge" style="color: #FFF;padding:0;">
<img class="pool-logo" [src]="'/resources/mining-pools/' + pools[acceleration.minedByPoolUniqueId].slug + '.svg'" onError="this.src = '/resources/mining-pools/default.svg'" [alt]="'Logo of ' + pools[acceleration.minedByPoolUniqueId].name + ' mining pool'">
{{ pools[acceleration.minedByPoolUniqueId].name }}
</a>
} @else {
~
}
</td>
<td class="status text-right">
<span *ngIf="acceleration.status === 'accelerating'" class="badge badge-warning" i18n="accelerator.pending">Pending</span>
<span *ngIf="acceleration.status.includes('completed')" class="badge badge-success" i18n="">Completed <span *ngIf="acceleration.status === 'completed_provisional'">🔄</span></span>

View File

@@ -12,7 +12,7 @@
padding-bottom: 0px;
}
.container-xl.legacy {
max-width: 1140px;
max-width: 1200px;
}
.container-xl.widget-container {
min-height: 335px;
@@ -72,9 +72,25 @@ tr, td, th {
.block {
width: 15%;
@media (max-width: 900px) {
display: none;
}
}
.pool {
width: 15%;
@media (max-width: 700px) {
display: none;
}
.pool-logo {
width: 22px;
height: 22px;
position: relative;
top: -1px;
margin-right: 2px;
}
}
.status {

View File

@@ -1,11 +1,12 @@
import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnDestroy, Inject, LOCALE_ID } from '@angular/core';
import { BehaviorSubject, Observable, Subscription, catchError, filter, of, switchMap, tap, throttleTime } from 'rxjs';
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
import { Acceleration, BlockExtended, SinglePoolStats } from '../../../interfaces/node-api.interface';
import { StateService } from '../../../services/state.service';
import { WebsocketService } from '../../../services/websocket.service';
import { ServicesApiServices } from '../../../services/services-api.service';
import { SeoService } from '../../../services/seo.service';
import { ActivatedRoute, Router } from '@angular/router';
import { MiningService } from '../../../services/mining.service';
@Component({
selector: 'app-accelerations-list',
@@ -30,11 +31,13 @@ export class AccelerationsListComponent implements OnInit, OnDestroy {
keyNavigationSubscription: Subscription;
dir: 'rtl' | 'ltr' = 'ltr';
paramSubscription: Subscription;
pools: { [id: number]: SinglePoolStats } = {};
constructor(
private servicesApiService: ServicesApiServices,
private websocketService: WebsocketService,
public stateService: StateService,
private miningService: MiningService,
private cd: ChangeDetectorRef,
private seoService: SeoService,
private route: ActivatedRoute,
@@ -79,6 +82,12 @@ export class AccelerationsListComponent implements OnInit, OnDestroy {
).subscribe(() => {
this.pageChange(this.page);
});
this.miningService.getMiningStats('1m').subscribe(stats => {
for (const pool of stats.pools) {
this.pools[pool.poolUniqueId] = pool;
}
});
}
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];

View File

@@ -249,7 +249,7 @@
</ng-template>
<ng-template #pendingBalanceRow>
<td i18n="address.unconfirmed-balance" class="font-italic">Unconfirmed balance</td>
<td i18n="accelerator.pending-state" class="font-italic">Pending</td>
<td *ngIf="mempoolStats.funded_txo_sum !== undefined; else confidentialTd" class="font-italic wrap-cell"><app-amount [satoshis]="mempoolStats.balance" [noFiat]="true" [addPlus]="true"></app-amount> <span class="fiat"><app-fiat [value]="mempoolStats.balance"></app-fiat></span></td>
</ng-template>
@@ -259,7 +259,7 @@
</ng-template>
<ng-template #pendingUtxoRow>
<td i18n="address.unconfirmed-utxos" class="font-italic">Unconfirmed UTXOs</td>
<td i18n="address.pending-utxos" class="font-italic">Pending UTXOs</td>
<td class="font-italic wrap-cell">{{ mempoolStats.utxos > 0 ? '+' : ''}}{{ mempoolStats.utxos }}</td>
</ng-template>

View File

@@ -303,7 +303,6 @@ export class SearchFormComponent implements OnInit {
(error) => { console.log(error); this.isSearching = false; }
);
} else {
this.searchResults.searchButtonClick();
this.isSearching = false;
}
}

View File

@@ -234,7 +234,7 @@ export class StartComponent implements OnInit, AfterViewChecked, OnDestroy {
this.minScrollWidth = 40 + (8 * this.blockWidth) + (this.pageWidth * 2);
if (firstVisibleBlock != null) {
this.scrollToBlock(firstVisibleBlock, offset);
this.scrollToBlock(firstVisibleBlock, offset + (this.isMobile ? this.blockWidth : 0));
} else {
this.updatePages();
}

View File

@@ -11,7 +11,9 @@ import {
tap,
map,
retry,
startWith
startWith,
repeat,
take
} from 'rxjs/operators';
import { Transaction } from '../../interfaces/electrs.interface';
import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest, BehaviorSubject } from 'rxjs';
@@ -76,6 +78,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
transactionTime = -1;
subscription: Subscription;
fetchCpfpSubscription: Subscription;
transactionTimesSubscription: Subscription;
fetchRbfSubscription: Subscription;
fetchCachedTxSubscription: Subscription;
fetchAccelerationSubscription: Subscription;
@@ -88,6 +91,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
blocksSubscription: Subscription;
miningSubscription: Subscription;
auditSubscription: Subscription;
txConfirmedSubscription: Subscription;
currencyChangeSubscription: Subscription;
fragmentParams: URLSearchParams;
rbfTransaction: undefined | Transaction;
@@ -106,6 +110,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
showCpfpDetails = false;
miningStats: MiningStats;
fetchCpfp$ = new Subject<string>();
transactionTimes$ = new Subject<string>();
fetchRbfHistory$ = new Subject<string>();
fetchCachedTx$ = new Subject<string>();
fetchAcceleration$ = new Subject<number>();
@@ -141,7 +146,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
taprootEnabled: boolean;
hasEffectiveFeeRate: boolean;
accelerateCtaType: 'alert' | 'button' = 'button';
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR && this.stateService.network === '';
acceleratorAvailable: boolean = this.stateService.env.ACCELERATOR_BUTTON && this.stateService.network === '';
eligibleForAcceleration: boolean = false;
forceAccelerationSummary = false;
hideAccelerationSummary = false;
@@ -195,7 +200,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.stateService.networkChanged$.subscribe(
(network) => {
this.network = network;
this.acceleratorAvailable = this.stateService.env.ACCELERATOR && this.stateService.network === '';
this.acceleratorAvailable = this.stateService.env.ACCELERATOR_BUTTON && this.stateService.network === '';
}
);
@@ -225,6 +230,25 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.latestBlock = blocks[0];
});
this.transactionTimesSubscription = this.transactionTimes$.pipe(
tap(() => {
this.isLoadingFirstSeen = true;
}),
switchMap((txid) => this.apiService.getTransactionTimes$([txid]).pipe(
retry({ count: 2, delay: 2000 }),
// Try again until we either get a valid response, or the transaction is confirmed
repeat({ delay: 2000 }),
filter((transactionTimes) => transactionTimes?.length && transactionTimes[0] > 0 && !this.tx.status?.confirmed),
take(1),
)),
)
.subscribe((transactionTimes) => {
this.isLoadingFirstSeen = false;
if (transactionTimes?.length && transactionTimes[0]) {
this.transactionTime = transactionTimes[0];
}
});
this.fetchCpfpSubscription = this.fetchCpfp$
.pipe(
switchMap((txId) =>
@@ -572,7 +596,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
if (tx.firstSeen) {
this.transactionTime = tx.firstSeen;
} else {
this.getTransactionTime();
this.transactionTimes$.next(tx.txid);
}
} else {
this.fetchAcceleration$.next(tx.status.block_height);
@@ -625,7 +649,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
}
);
this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => {
this.txConfirmedSubscription = this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => {
if (txConfirmed && this.tx && !this.tx.status.confirmed && txConfirmed === this.tx.txid) {
if (this.tx.acceleration) {
this.waitingForAccelerationInfo = true;
@@ -729,7 +753,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.accelerationPositions,
);
})
)
);
}
ngAfterViewInit(): void {
@@ -763,28 +787,6 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
return of(false);
}
getTransactionTime() {
this.isLoadingFirstSeen = true;
this.apiService
.getTransactionTimes$([this.tx.txid])
.pipe(
retry({ count: 2, delay: 2000 }),
catchError(() => {
this.isLoadingFirstSeen = false;
return throwError(() => new Error(''));
})
)
.subscribe((transactionTimes) => {
if (transactionTimes?.length && transactionTimes[0]) {
this.transactionTime = transactionTimes[0];
} else {
setTimeout(() => {
this.getTransactionTime();
}, 2000);
}
});
}
setCpfpInfo(cpfpInfo: CpfpInfo): void {
if (!cpfpInfo || !this.tx) {
this.cpfpInfo = null;
@@ -1057,6 +1059,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
ngOnDestroy() {
this.subscription.unsubscribe();
this.fetchCpfpSubscription.unsubscribe();
this.transactionTimesSubscription.unsubscribe();
this.fetchRbfSubscription.unsubscribe();
this.fetchCachedTxSubscription.unsubscribe();
this.fetchAccelerationSubscription.unsubscribe();
@@ -1070,6 +1073,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.blocksSubscription.unsubscribe();
this.miningSubscription?.unsubscribe();
this.auditSubscription?.unsubscribe();
this.txConfirmedSubscription?.unsubscribe();
this.currencyChangeSubscription?.unsubscribe();
this.leaveTransaction();
}