Merge branch 'master' into nymkappa/network-switch-align
This commit is contained in:
@@ -207,7 +207,7 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
this.isLoadingTransactions = true;
|
||||
this.retryLoadMore = false;
|
||||
this.electrsApiService.getAddressTransactionsFromHash$(this.address.address, this.lastTransactionTxId)
|
||||
this.electrsApiService.getAddressTransactions$(this.address.address, this.lastTransactionTxId)
|
||||
.subscribe((transactions: Transaction[]) => {
|
||||
this.lastTransactionTxId = transactions[transactions.length - 1].txid;
|
||||
this.loadedConfirmedTxCount += transactions.length;
|
||||
@@ -217,6 +217,10 @@ export class AddressComponent implements OnInit, OnDestroy {
|
||||
(error) => {
|
||||
this.isLoadingTransactions = false;
|
||||
this.retryLoadMore = true;
|
||||
// In the unlikely event of the txid wasn't found in the mempool anymore and we must reload the page.
|
||||
if (error.status === 422) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export default class TxView implements TransactionStripped {
|
||||
value: number;
|
||||
feerate: number;
|
||||
rate?: number;
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'added' | 'censored' | 'selected' | 'fullrbf';
|
||||
status?: 'found' | 'missing' | 'sigop' | 'fresh' | 'freshcpfp' | 'added' | 'censored' | 'selected' | 'fullrbf';
|
||||
context?: 'projected' | 'actual';
|
||||
scene?: BlockScene;
|
||||
|
||||
@@ -210,6 +210,7 @@ export default class TxView implements TransactionStripped {
|
||||
case 'fullrbf':
|
||||
return marginalFeeColors[feeLevelIndex] || marginalFeeColors[mempoolFeeColors.length - 1];
|
||||
case 'fresh':
|
||||
case 'freshcpfp':
|
||||
return auditColors.missing;
|
||||
case 'added':
|
||||
return auditColors.added;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<td *ngSwitchCase="'missing'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
|
||||
<td *ngSwitchCase="'sigop'"><span class="badge badge-warning" i18n="transaction.audit.sigop">High sigop count</span></td>
|
||||
<td *ngSwitchCase="'fresh'"><span class="badge badge-warning" i18n="transaction.audit.recently-broadcasted">Recently broadcasted</span></td>
|
||||
<td *ngSwitchCase="'freshcpfp'"><span class="badge badge-warning" i18n="transaction.audit.recently-cpfped">Recently CPFP'd</span></td>
|
||||
<td *ngSwitchCase="'added'"><span class="badge badge-warning" i18n="transaction.audit.added">Added</span></td>
|
||||
<td *ngSwitchCase="'selected'"><span class="badge badge-warning" i18n="transaction.audit.marginal">Marginal fee rate</span></td>
|
||||
<td *ngSwitchCase="'fullrbf'"><span class="badge badge-warning" i18n="transaction.audit.fullrbf">Full RBF</span></td>
|
||||
|
||||
@@ -370,7 +370,11 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
tx.status = 'found';
|
||||
} else {
|
||||
if (isFresh[tx.txid]) {
|
||||
tx.status = 'fresh';
|
||||
if (tx.rate - (tx.fee / tx.vsize) >= 0.1) {
|
||||
tx.status = 'freshcpfp';
|
||||
} else {
|
||||
tx.status = 'fresh';
|
||||
}
|
||||
} else if (isSigop[tx.txid]) {
|
||||
tx.status = 'sigop';
|
||||
} else if (isFullRbf[tx.txid]) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">{{ currency$ | async }}</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" formControlName="fiat" (input)="transformInput('fiat')">
|
||||
<input type="text" class="form-control" formControlName="fiat" (input)="transformInput('fiat')" (click)="selectAll($event)">
|
||||
<app-clipboard [button]="true" [text]="form.get('fiat').value" [class]="'btn btn-lg btn-secondary ml-1'"></app-clipboard>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">BTC</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" formControlName="bitcoin" (input)="transformInput('bitcoin')">
|
||||
<input type="text" class="form-control" formControlName="bitcoin" (input)="transformInput('bitcoin')" (click)="selectAll($event)">
|
||||
<app-clipboard [button]="true" [text]="form.get('bitcoin').value" [class]="'btn btn-lg btn-secondary ml-1'"></app-clipboard>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">sats</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" formControlName="satoshis" (input)="transformInput('satoshis')">
|
||||
<input type="text" class="form-control" formControlName="satoshis" (input)="transformInput('satoshis')" (click)="selectAll($event)">
|
||||
<app-clipboard [button]="true" [text]="form.get('satoshis').value" [class]="'btn btn-lg btn-secondary ml-1'"></app-clipboard>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -23,4 +23,8 @@
|
||||
.sats {
|
||||
font-size: 20px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,9 @@ export class CalculatorComponent implements OnInit {
|
||||
]).subscribe(([price, value]) => {
|
||||
const rate = (value / price).toFixed(8);
|
||||
const satsRate = Math.round(value / price * 100_000_000);
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
this.form.get('bitcoin').setValue(rate, { emitEvent: false });
|
||||
this.form.get('satoshis').setValue(satsRate, { emitEvent: false } );
|
||||
});
|
||||
@@ -63,6 +66,9 @@ export class CalculatorComponent implements OnInit {
|
||||
this.form.get('bitcoin').valueChanges
|
||||
]).subscribe(([price, value]) => {
|
||||
const rate = parseFloat((value * price).toFixed(8));
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
this.form.get('fiat').setValue(rate, { emitEvent: false } );
|
||||
this.form.get('satoshis').setValue(Math.round(value * 100_000_000), { emitEvent: false } );
|
||||
});
|
||||
@@ -73,6 +79,9 @@ export class CalculatorComponent implements OnInit {
|
||||
]).subscribe(([price, value]) => {
|
||||
const rate = parseFloat((value / 100_000_000 * price).toFixed(8));
|
||||
const bitcoinRate = (value / 100_000_000).toFixed(8);
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
this.form.get('fiat').setValue(rate, { emitEvent: false } );
|
||||
this.form.get('bitcoin').setValue(bitcoinRate, { emitEvent: false });
|
||||
});
|
||||
@@ -88,7 +97,16 @@ export class CalculatorComponent implements OnInit {
|
||||
if (value === '.') {
|
||||
value = '0';
|
||||
}
|
||||
const sanitizedValue = this.removeExtraDots(value);
|
||||
let sanitizedValue = this.removeExtraDots(value);
|
||||
if (name === 'bitcoin' && this.countDecimals(sanitizedValue) > 8) {
|
||||
sanitizedValue = this.toFixedWithoutRounding(sanitizedValue, 8);
|
||||
}
|
||||
if (sanitizedValue === '') {
|
||||
sanitizedValue = '0';
|
||||
}
|
||||
if (name === 'satoshis') {
|
||||
sanitizedValue = parseFloat(sanitizedValue).toFixed(0);
|
||||
}
|
||||
formControl.setValue(sanitizedValue, {emitEvent: true});
|
||||
}
|
||||
|
||||
@@ -100,4 +118,20 @@ export class CalculatorComponent implements OnInit {
|
||||
const afterDotReplaced = afterDot.replace(/\./g, '');
|
||||
return `${beforeDot}.${afterDotReplaced}`;
|
||||
}
|
||||
|
||||
countDecimals(numberString: string): number {
|
||||
const decimalPos = numberString.indexOf('.');
|
||||
if (decimalPos === -1) return 0;
|
||||
return numberString.length - decimalPos - 1;
|
||||
}
|
||||
|
||||
toFixedWithoutRounding(numStr: string, fixed: number): string {
|
||||
const re = new RegExp(`^-?\\d+(?:.\\d{0,${(fixed || -1)}})?`);
|
||||
const result = numStr.match(re);
|
||||
return result ? result[0] : numStr;
|
||||
}
|
||||
|
||||
selectAll(event): void {
|
||||
event.target.select();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
|
||||
--chain-height: 60px;
|
||||
--clock-width: 300px;
|
||||
|
||||
@@ -37,7 +37,7 @@ export class PoolComponent implements OnInit {
|
||||
|
||||
auditAvailable = false;
|
||||
|
||||
loadMoreSubject: BehaviorSubject<number> = new BehaviorSubject(this.blocks[0]?.height);
|
||||
loadMoreSubject: BehaviorSubject<number> = new BehaviorSubject(this.blocks[this.blocks.length - 1]?.height);
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
@@ -91,7 +91,7 @@ export class PoolComponent implements OnInit {
|
||||
if (this.slug === undefined) {
|
||||
return [];
|
||||
}
|
||||
return this.apiService.getPoolBlocks$(this.slug, this.blocks[0]?.height);
|
||||
return this.apiService.getPoolBlocks$(this.slug, this.blocks[this.blocks.length - 1]?.height);
|
||||
}),
|
||||
tap((newBlocks) => {
|
||||
this.blocks = this.blocks.concat(newBlocks);
|
||||
@@ -237,7 +237,7 @@ export class PoolComponent implements OnInit {
|
||||
}
|
||||
|
||||
loadMore() {
|
||||
this.loadMoreSubject.next(this.blocks[0]?.height);
|
||||
this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height);
|
||||
}
|
||||
|
||||
trackByBlock(index: number, block: BlockExtended) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<h1 class="float-left" i18n="page.rbf-replacements">RBF Replacements</h1>
|
||||
<div *ngIf="isLoading" class="spinner-border ml-3" role="status"></div>
|
||||
|
||||
<div class="mode-toggle float-right" *ngIf="fullRbfEnabled">
|
||||
<div class="mode-toggle float-right">
|
||||
<form class="formRadioGroup">
|
||||
<div class="btn-group btn-group-toggle" name="radioBasic">
|
||||
<label class="btn btn-primary btn-sm" [class.active]="!fullRbf">
|
||||
|
||||
@@ -17,7 +17,6 @@ export class RbfList implements OnInit, OnDestroy {
|
||||
rbfTrees$: Observable<RbfTree[]>;
|
||||
nextRbfSubject = new BehaviorSubject(null);
|
||||
urlFragmentSubscription: Subscription;
|
||||
fullRbfEnabled: boolean;
|
||||
fullRbf: boolean;
|
||||
isLoading = true;
|
||||
|
||||
@@ -27,9 +26,7 @@ export class RbfList implements OnInit, OnDestroy {
|
||||
private apiService: ApiService,
|
||||
public stateService: StateService,
|
||||
private websocketService: WebsocketService,
|
||||
) {
|
||||
this.fullRbfEnabled = stateService.env.FULL_RBF_ENABLED;
|
||||
}
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.urlFragmentSubscription = this.route.fragment.subscribe((fragment) => {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
<div class="container-buttons">
|
||||
<app-confirmations
|
||||
*ngIf="tx"
|
||||
[chainTip]="latestBlock?.height"
|
||||
[height]="tx?.status?.block_height"
|
||||
[replaced]="replaced"
|
||||
|
||||
@@ -379,7 +379,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
ancestors: tx.ancestors,
|
||||
bestDescendant: tx.bestDescendant,
|
||||
};
|
||||
const hasRelatives = !!(tx.ancestors.length || tx.bestDescendant);
|
||||
const hasRelatives = !!(tx.ancestors?.length || tx.bestDescendant);
|
||||
this.hasEffectiveFeeRate = hasRelatives || (tx.effectiveFeePerVsize && (Math.abs(tx.effectiveFeePerVsize - tx.feePerVsize) > 0.01));
|
||||
} else {
|
||||
this.fetchCpfp$.next(this.tx.txid);
|
||||
|
||||
Reference in New Issue
Block a user