Merge branch 'master' into natsoni/high-contrast-theme
This commit is contained in:
@@ -80,7 +80,9 @@
|
||||
<div class="title float-left">
|
||||
<h2 i18n="transaction.accelerate|Accelerate button label">Accelerate</h2>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-info accelerator-toggle btn-sm float-right" (click)="showAccelerationSummary = false" i18n="hide-accelerator">Hide accelerator</button>
|
||||
@if (!(isMobile && paymentType === 'cashapp')) {
|
||||
<button type="button" class="btn btn-outline-info accelerator-toggle btn-sm float-right" (click)="showAccelerationSummary = false" i18n="hide-accelerator">Hide accelerator</button>
|
||||
}
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="box">
|
||||
@@ -456,13 +458,18 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template #firstSeenRow>
|
||||
@if (!isLoadingTx && transactionTime !== -1) {
|
||||
@if (isLoadingTx) {
|
||||
<ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container>
|
||||
} @else if (transactionTime === -1) {
|
||||
<tr>
|
||||
<td i18n="transaction.first-seen|Transaction first seen">First seen</td>
|
||||
<td><span class="skeleton-loader"></span></td>
|
||||
</tr>
|
||||
} @else {
|
||||
<tr>
|
||||
<td i18n="transaction.first-seen|Transaction first seen">First seen</td>
|
||||
<td><i><app-time kind="since" [time]="transactionTime" [fastRender]="true"></app-time></i></td>
|
||||
</tr>
|
||||
} @else {
|
||||
<ng-container *ngTemplateOutlet="skeletonDetailsRow"></ng-container>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
@@ -530,7 +537,7 @@
|
||||
} @else if (this.mempoolPosition.block >= 7) {
|
||||
<span [class]="(acceleratorAvailable && accelerateCtaType === 'button') ? 'etaDeepMempool d-flex justify-content-end align-items-center' : ''">
|
||||
<span i18n="transaction.eta.in-several-hours|Transaction ETA in several hours or more">In several hours (or more)</span>
|
||||
@if (!tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
||||
@if (!(isMobile && paymentType === 'cashapp') && !tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
||||
<a [href]="'/services/accelerator/accelerate?txid=' + tx.txid" class="btn btn-sm accelerateDeepMempool btn-small-height" i18n="transaction.accelerate|Accelerate button label" (click)="onAccelerateClicked()">Accelerate</a>
|
||||
}
|
||||
</span>
|
||||
@@ -539,7 +546,7 @@
|
||||
} @else {
|
||||
<span class="eta justify-content-end" [class]="(acceleratorAvailable && accelerateCtaType === 'button') ? 'd-flex align-items-center' : ''">
|
||||
<app-time kind="until" *ngIf="(da$ | async) as da;" [time]="da.adjustedTimeAvg * (this.mempoolPosition.block + 1) + now + da.timeOffset" [fastRender]="false" [fixedRender]="true"></app-time>
|
||||
@if (!tx.acceleration && acceleratorAvailable && accelerateCtaType === 'button' && !tx?.acceleration) {
|
||||
@if (!(isMobile && paymentType === 'cashapp') && !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>
|
||||
}
|
||||
</span>
|
||||
@@ -554,13 +561,13 @@
|
||||
|
||||
<ng-template #gogglesRow>
|
||||
@if (!isLoadingTx) {
|
||||
@if (((auditStatus && auditStatus.accelerated) || accelerationInfo || (tx && tx.acceleration)) || filters.length) {
|
||||
@if (isAcceleration || filters.length) {
|
||||
<tr>
|
||||
<td class="td-width">
|
||||
<span class="goggles-icon"><app-svg-images name="goggles" width="100%" height="100%"></app-svg-images></span>
|
||||
</td>
|
||||
<td class="wrap-cell">
|
||||
@if ((auditStatus && auditStatus.accelerated) || accelerationInfo || (tx && tx.acceleration)) {
|
||||
@if (isAcceleration) {
|
||||
<span class="badge badge-accelerated mr-1" i18n="transaction.audit.accelerated">Accelerated</span>
|
||||
}
|
||||
<ng-container *ngFor="let filter of filters;">
|
||||
@@ -606,15 +613,15 @@
|
||||
@if (!isLoadingTx) {
|
||||
@if ((cpfpInfo && hasEffectiveFeeRate) || accelerationInfo) {
|
||||
<tr>
|
||||
@if (tx.acceleration || accelerationInfo) {
|
||||
@if (isAcceleration) {
|
||||
<td i18n="transaction.accelerated-fee-rate|Accelerated transaction fee rate">Accelerated fee rate</td>
|
||||
} @else {
|
||||
<td i18n="transaction.effective-fee-rate|Effective transaction fee rate">Effective fee rate</td>
|
||||
}
|
||||
<td>
|
||||
<div class="effective-fee-container">
|
||||
@if (accelerationInfo) {
|
||||
<app-fee-rate [fee]="accelerationInfo.acceleratedFee" [weight]="accelerationInfo.effectiveVsize * 4"></app-fee-rate>
|
||||
@if (accelerationInfo?.acceleratedFeeRate && (!tx.effectiveFeePerVsize || accelerationInfo.acceleratedFeeRate >= tx.effectiveFeePerVsize)) {
|
||||
<app-fee-rate [fee]="accelerationInfo.acceleratedFeeRate"></app-fee-rate>
|
||||
} @else {
|
||||
<app-fee-rate [fee]="tx.effectiveFeePerVsize"></app-fee-rate>
|
||||
}
|
||||
|
||||
@@ -311,6 +311,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.accelerateFullSize {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0.5rem 0.25rem;
|
||||
background-color: #653b9c;
|
||||
}
|
||||
|
||||
.goggles-icon {
|
||||
display: block;
|
||||
width: 2.2em;
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
delay,
|
||||
mergeMap,
|
||||
tap,
|
||||
map
|
||||
map,
|
||||
retry
|
||||
} from 'rxjs/operators';
|
||||
import { Transaction } from '../../interfaces/electrs.interface';
|
||||
import { of, merge, Subscription, Observable, Subject, from, throwError, combineLatest } from 'rxjs';
|
||||
@@ -93,12 +94,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
adjustedVsize: number | null;
|
||||
pool: Pool | null;
|
||||
auditStatus: AuditStatus | null;
|
||||
isAcceleration: boolean = false;
|
||||
filters: Filter[] = [];
|
||||
showCpfpDetails = false;
|
||||
fetchCpfp$ = new Subject<string>();
|
||||
fetchRbfHistory$ = new Subject<string>();
|
||||
fetchCachedTx$ = new Subject<string>();
|
||||
fetchAcceleration$ = new Subject<string>();
|
||||
fetchAcceleration$ = new Subject<number>();
|
||||
fetchMiningInfo$ = new Subject<{ hash: string, height: number, txid: string }>();
|
||||
isCached: boolean = false;
|
||||
now = Date.now();
|
||||
@@ -117,6 +119,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 +158,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']);
|
||||
@@ -280,9 +289,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
filter(() => this.stateService.env.ACCELERATOR === true),
|
||||
tap(() => {
|
||||
this.accelerationInfo = null;
|
||||
this.setIsAccelerated();
|
||||
}),
|
||||
switchMap((blockHash: string) => {
|
||||
return this.servicesApiService.getAccelerationHistory$({ blockHash });
|
||||
switchMap((blockHeight: number) => {
|
||||
return this.servicesApiService.getAccelerationHistory$({ blockHeight });
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
@@ -290,8 +300,12 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
).subscribe((accelerationHistory) => {
|
||||
for (const acceleration of accelerationHistory) {
|
||||
if (acceleration.txid === this.txId && (acceleration.status === 'completed' || acceleration.status === 'completed_provisional')) {
|
||||
acceleration.acceleratedFee = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee);
|
||||
const boostCost = acceleration.boostCost || (acceleration.feePaid - acceleration.baseFee - acceleration.vsizeFee);
|
||||
acceleration.acceleratedFeeRate = Math.max(acceleration.effectiveFee, acceleration.effectiveFee + boostCost) / acceleration.effectiveVsize;
|
||||
acceleration.boost = boostCost;
|
||||
|
||||
this.accelerationInfo = acceleration;
|
||||
this.setIsAccelerated();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -312,6 +326,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
map(block => {
|
||||
return block.extras.pool;
|
||||
}),
|
||||
retry({ count: 3, delay: 2000 }),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
@@ -332,18 +347,21 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
accelerated: isAccelerated,
|
||||
};
|
||||
}),
|
||||
retry({ count: 3, delay: 2000 }),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
})
|
||||
) : of(isCoinbase ? { coinbase: true } : null)
|
||||
]);
|
||||
}),
|
||||
catchError(() => {
|
||||
catchError((e) => {
|
||||
return of(null);
|
||||
})
|
||||
).subscribe(([pool, auditStatus]) => {
|
||||
this.pool = pool;
|
||||
this.auditStatus = auditStatus;
|
||||
|
||||
this.setIsAccelerated();
|
||||
});
|
||||
|
||||
this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => {
|
||||
@@ -475,7 +493,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.getTransactionTime();
|
||||
}
|
||||
} else {
|
||||
this.fetchAcceleration$.next(tx.status.block_hash);
|
||||
this.fetchAcceleration$.next(tx.status.block_height);
|
||||
this.fetchMiningInfo$.next({ hash: tx.status.block_hash, height: tx.status.block_height, txid: tx.txid });
|
||||
this.transactionTime = 0;
|
||||
}
|
||||
@@ -537,7 +555,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
} else {
|
||||
this.audioService.playSound('magic');
|
||||
}
|
||||
this.fetchAcceleration$.next(block.id);
|
||||
this.fetchAcceleration$.next(block.height);
|
||||
this.fetchMiningInfo$.next({ hash: block.id, height: block.height, txid: this.tx.txid });
|
||||
}
|
||||
});
|
||||
@@ -666,10 +684,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
relatives.reduce((prev, val) => prev + val.fee, 0);
|
||||
this.tx.effectiveFeePerVsize = totalFees / (totalWeight / 4);
|
||||
} else {
|
||||
this.tx.effectiveFeePerVsize = cpfpInfo.effectiveFeePerVsize;
|
||||
this.tx.effectiveFeePerVsize = cpfpInfo.effectiveFeePerVsize || this.tx.effectiveFeePerVsize || this.tx.feePerVsize || (this.tx.fee / (this.tx.weight / 4));
|
||||
}
|
||||
if (cpfpInfo.acceleration) {
|
||||
this.tx.acceleration = cpfpInfo.acceleration;
|
||||
this.setIsAccelerated();
|
||||
}
|
||||
|
||||
this.cpfpInfo = cpfpInfo;
|
||||
@@ -681,6 +700,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.hasEffectiveFeeRate = hasRelatives || (this.tx.effectiveFeePerVsize && (Math.abs(this.tx.effectiveFeePerVsize - this.tx.feePerVsize) > 0.01));
|
||||
}
|
||||
|
||||
setIsAccelerated() {
|
||||
console.log(this.tx.acceleration, this.accelerationInfo, this.pool, this.accelerationInfo?.pools);
|
||||
this.isAcceleration = (this.tx.acceleration || (this.accelerationInfo && this.pool && this.accelerationInfo.pools.some(pool => (pool === this.pool.id || pool?.['pool_unique_id'] === this.pool.id))));
|
||||
}
|
||||
|
||||
setFeatures(): void {
|
||||
if (this.tx) {
|
||||
this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit');
|
||||
@@ -720,6 +744,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();
|
||||
@@ -742,6 +771,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.pool = null;
|
||||
this.auditStatus = null;
|
||||
document.body.scrollTo(0, 0);
|
||||
this.isAcceleration = false;
|
||||
this.leaveTransaction();
|
||||
}
|
||||
|
||||
@@ -814,6 +844,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.stateService.ref = '';
|
||||
this.subscription.unsubscribe();
|
||||
this.fetchCpfpSubscription.unsubscribe();
|
||||
this.fetchRbfSubscription.unsubscribe();
|
||||
|
||||
Reference in New Issue
Block a user