Merge branch 'master' into natsoni/acc-timeline-polish
This commit is contained in:
commit
07370a8dc7
@ -123,7 +123,7 @@ class StatisticsReplication {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const intervals = [ // [start, end, label ]
|
const intervals = [ // [start, end, label ]
|
||||||
[now - day, now - 60, '24h'] , // from 24 hours ago to now = 1 minute granularity
|
[now - day + 600, now - 60, '24h'] , // from 24 hours ago to now = 1 minute granularity
|
||||||
startTime < now - day ? [now - day * 7, now - day, '1w' ] : null, // from 1 week ago to 24 hours ago = 5 minutes granularity
|
startTime < now - day ? [now - day * 7, now - day, '1w' ] : null, // from 1 week ago to 24 hours ago = 5 minutes granularity
|
||||||
startTime < now - day * 7 ? [now - day * 30, now - day * 7, '1m' ] : null, // from 1 month ago to 1 week ago = 30 minutes granularity
|
startTime < now - day * 7 ? [now - day * 30, now - day * 7, '1m' ] : null, // from 1 month ago to 1 week ago = 30 minutes granularity
|
||||||
startTime < now - day * 30 ? [now - day * 90, now - day * 30, '3m' ] : null, // from 3 months ago to 1 month ago = 2 hours granularity
|
startTime < now - day * 30 ? [now - day * 90, now - day * 30, '3m' ] : null, // from 3 months ago to 1 month ago = 2 hours granularity
|
||||||
@ -170,15 +170,24 @@ class StatisticsReplication {
|
|||||||
return new Set<number>();
|
return new Set<number>();
|
||||||
}
|
}
|
||||||
|
|
||||||
const roundedTimesAlreadyHere = new Set(rows.map(row => this.roundToNearestStep(row.added, step)));
|
const roundedTimesAlreadyHere: number[] = Array.from(new Set(rows.map(row => this.roundToNearestStep(row.added, step))));
|
||||||
const missingTimes = new Set(timeSteps.filter(time => !roundedTimesAlreadyHere.has(time)));
|
|
||||||
|
const missingTimes = timeSteps.filter(time => !roundedTimesAlreadyHere.includes(time)).filter((time, i, arr) => {
|
||||||
|
// Remove outsiders
|
||||||
|
if (i === 0) {
|
||||||
|
return arr[i + 1] === time + step
|
||||||
|
} else if (i === arr.length - 1) {
|
||||||
|
return arr[i - 1] === time - step;
|
||||||
|
}
|
||||||
|
return (arr[i + 1] === time + step) && (arr[i - 1] === time - step)
|
||||||
|
});
|
||||||
|
|
||||||
// Don't bother fetching if very few rows are missing
|
// Don't bother fetching if very few rows are missing
|
||||||
if (missingTimes.size < timeSteps.length * 0.005) {
|
if (missingTimes.length < timeSteps.length * 0.01) {
|
||||||
return new Set();
|
return new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
return missingTimes;
|
return new Set(missingTimes);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.err(`Cannot fetch missing statistics times from db. Reason: ` + (e instanceof Error ? e.message : e));
|
logger.err(`Cannot fetch missing statistics times from db. Reason: ` + (e instanceof Error ? e.message : e));
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
<div class="box card w-100" style="background: var(--box-bg)" id=acceleratePreviewAnchor>
|
<div class="box card w-100" style="background: var(--box-bg)" id=acceleratePreviewAnchor>
|
||||||
@if (error) {
|
@if (accelerateError) {
|
||||||
<div class="row mt-2">
|
<div class="row mb-1 text-center">
|
||||||
<div class="col">
|
<div class="col-sm">
|
||||||
<app-mempool-error [error]="error" [alertClass]="error === 'waitlisted' ? 'alert-mempool' : 'alert-danger'"></app-mempool-error>
|
<h1 style="font-size: larger;">Sorry, something went wrong!</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<div class="row text-center mt-1">
|
||||||
@else if (step === 'quote') {
|
<div class="col-sm">
|
||||||
|
<div class="d-flex flex-row justify-content-center align-items-center">
|
||||||
|
<span i18n="accelerator.error-failed-to-accelerate">We were not able to accelerate this transaction. Please try again later.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row mt-2 mb-2 text-center">
|
||||||
|
<div class="col-sm d-flex flex-column">
|
||||||
|
<button type="button" class="mt-1 btn btn-secondary btn-sm rounded-pill align-self-center" style="width: 200px" (click)="closeModal()" i18n="close">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
} @else if (step === 'quote') {
|
||||||
<div class="accelerate-cols">
|
<div class="accelerate-cols">
|
||||||
<ng-container *ngIf="!isMobile">
|
<ng-container *ngIf="!isMobile">
|
||||||
<app-accelerate-fee-graph
|
<app-accelerate-fee-graph
|
||||||
@ -20,7 +32,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="estimate else loadingEstimate">
|
<ng-container *ngIf="estimate else loadingEstimate">
|
||||||
<div [class.disabled]="error || showSuccess">
|
<div>
|
||||||
@if (showDetails) {
|
@if (showDetails) {
|
||||||
<h5 i18n="accelerator.your-transaction">Your transaction</h5>
|
<h5 i18n="accelerator.your-transaction">Your transaction</h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -264,7 +276,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@if (!advancedEnabled) {
|
@if (!advancedEnabled) {
|
||||||
<form [class.disabled]="error || showSuccess">
|
<form>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md">
|
<div class="col-md">
|
||||||
<div class="form-group form-check mb-2">
|
<div class="form-group form-check mb-2">
|
||||||
@ -295,7 +307,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
} @else {
|
} @else {
|
||||||
<div [class.disabled]="error || showSuccess">
|
<div>
|
||||||
<div class="row summary-row">
|
<div class="row summary-row">
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
@ -342,18 +354,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md pie d-none d-md-flex" *ngIf="!forceMobile">
|
<div class="col-md pie d-none d-md-flex" *ngIf="!forceMobile">
|
||||||
<small class="form-text checkout-text mb-2" *ngIf="(etaInfo$ | async) as etaInfo"><ng-container *ngTemplateOutlet="prioritizedBy; context: {$implicit:etaInfo.hashratePercentage}"></ng-container></small>
|
<small class="form-text checkout-text mb-2" *ngIf="(etaInfo$ | async) as etaInfo"><ng-container *ngTemplateOutlet="prioritizedBy; context: {$implicit:etaInfo.hashratePercentage}"></ng-container></small>
|
||||||
<app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true"></app-active-acceleration-box>
|
<app-active-acceleration-box [miningStats]="miningStats" [pools]="estimate.pools" [chartOnly]="true" class="ml-2"></app-active-acceleration-box>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-area mt-2 p-2" [class.disabled]="error || showSuccess" style="font-size: 14px;">
|
<div class="payment-area mt-2 p-2" style="font-size: 14px;">
|
||||||
<div class="row text-center justify-content-center mx-2">
|
<div class="row text-center justify-content-center mx-2">
|
||||||
<p i18n="accelerator.payment-to-mempool-space">Payment to mempool.space for acceleration of txid <a [routerLink]="'/tx/' + tx.txid" target="_blank">{{ tx.txid.substr(0, 10) }}..{{ tx.txid.substr(-10) }}</a></p>
|
<p i18n="accelerator.payment-to-mempool-space">Payment to mempool.space for acceleration of txid <a [routerLink]="'/tx/' + tx.txid" target="_blank">{{ tx.txid.substr(0, 10) }}..{{ tx.txid.substr(-10) }}</a></p>
|
||||||
</div>
|
</div>
|
||||||
@if (canPayWithBalance || !(canPayWithBitcoin || canPayWithCashapp)) {
|
@if (canPayWithBalance || !(canPayWithBitcoin || canPayWithCashapp)) {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm text-center d-flex flex-column justify-content-center align-items-center">
|
<div class="col-sm text-center d-flex flex-column justify-content-center align-items-center">
|
||||||
<p>Your account will be debited no more than <span><small style="font-family: monospace;">{{ cost | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></span></p>
|
<p><ng-container i18n="accelerator.your-account-will-be-debited">Your account will be debited no more than</ng-container> <small style="font-family: monospace;">{{ cost | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></p>
|
||||||
<div class="d-flex justify-content-center" [class.grayOut]="!canPayWithBalance || error || showSuccess">
|
<div class="d-flex justify-content-center" [class.grayOut]="!canPayWithBalance || quoteError || accelerateError || showSuccess">
|
||||||
<ng-container *ngTemplateOutlet="accountPayButton"></ng-container>
|
<ng-container *ngTemplateOutlet="accountPayButton"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -365,6 +377,11 @@
|
|||||||
@if (invoice) {
|
@if (invoice) {
|
||||||
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <span><small style="font-family: monospace;">{{ ((invoice.btcDue * 100_000_000) || cost) | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></span></p>
|
<p><ng-container i18n="transaction.pay|Pay button label">Pay</ng-container> <span><small style="font-family: monospace;">{{ ((invoice.btcDue * 100_000_000) || cost) | number }}</small> <span class="symbol" i18n="shared.sats">sats</span></span></p>
|
||||||
<app-bitcoin-invoice style="width: 100%;" [invoice]="invoice" [minimal]="true" (completed)="bitcoinPaymentCompleted()"></app-bitcoin-invoice>
|
<app-bitcoin-invoice style="width: 100%;" [invoice]="invoice" [minimal]="true" (completed)="bitcoinPaymentCompleted()"></app-bitcoin-invoice>
|
||||||
|
} @else if (btcpayInvoiceFailed) {
|
||||||
|
<p i18n="accelerator.failed-to-load-invoice">Failed to load invoice</p>
|
||||||
|
<div class="d-flex flex-column align-items-center justify-content-center" style="width: 100%; height: 292px;">
|
||||||
|
<fa-icon style="font-size: 24px; color: var(--red)" [icon]="['fas', 'circle-xmark']"></fa-icon>
|
||||||
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<p i18n="accelerator.loading-invoice">Loading invoice...</p>
|
<p i18n="accelerator.loading-invoice">Loading invoice...</p>
|
||||||
<div class="d-flex align-items-center justify-content-center" style="width: 100%; height: 292px;">
|
<div class="d-flex align-items-center justify-content-center" style="width: 100%; height: 292px;">
|
||||||
@ -486,9 +503,12 @@
|
|||||||
|
|
||||||
<div class="row text-center mt-1">
|
<div class="row text-center mt-1">
|
||||||
<div class="col-sm">
|
<div class="col-sm">
|
||||||
<div class="d-flex flex-row justify-content-center align-items-center">
|
<div class="d-flex flex-row flex-column justify-content-center align-items-center">
|
||||||
<span i18n="accelerator.confirming-acceleration-with-miners">Confirming your acceleration with our mining pool partners...</span>
|
<span i18n="accelerator.confirming-acceleration-with-miners">Confirming your acceleration with our mining pool partners...</span>
|
||||||
<div class="ml-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
@if (timeSincePaid > 20000) {
|
||||||
|
<span i18n="accelerator.confirming-acceleration-with-miners">...sorry, this is taking longer than expected...</span>
|
||||||
|
}
|
||||||
|
<div class="m-2 spinner-border text-light" style="width: 25px; height: 25px"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -532,16 +552,21 @@
|
|||||||
<ng-template #accelerateTo let-x i18n="accelerator.accelerate-to-x">Accelerate to ~{{ x | number : '1.0-0' }} sat/vB</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 #accelerateButton>
|
<ng-template #accelerateButton>
|
||||||
@if (canPayWithBalance || canPayWithBitcoin || canPayWithCashapp) {
|
@if (!couldPay && !quoteError && !(estimate?.availablePaymentMethods.bitcoin || estimate?.availablePaymentMethods.balance)) {
|
||||||
<button type="button" class="mt-1 btn btn-purple rounded-pill align-self-center d-flex flex-row justify-content-center align-items-center" [class.disabled]="!canPay || calculating || (!advancedEnabled && selectedOption !== 'accel')" style="width: 200px" (click)="moveToStep('checkout')">
|
|
||||||
<img src="/resources/mempool-accelerator-sparkles-light.svg" height="20" class="mr-2" style="margin-left: -10px">
|
|
||||||
<span i18n="transaction.accelerate|Accelerate button label">Accelerate</span>
|
|
||||||
</button>
|
|
||||||
} @else {
|
|
||||||
<button type="button" class="mt-1 btn btn-purple rounded-pill align-self-center d-flex flex-row justify-content-center align-items-center disabled" style="width: 200px">
|
<button type="button" class="mt-1 btn btn-purple rounded-pill align-self-center d-flex flex-row justify-content-center align-items-center disabled" style="width: 200px">
|
||||||
<img src="/resources/mempool-accelerator-sparkles-light.svg" height="20" class="mr-2" style="margin-left: -10px">
|
<img src="/resources/mempool-accelerator-sparkles-light.svg" height="20" class="mr-2" style="margin-left: -10px">
|
||||||
<span>Coming soon</span>
|
<span>Coming soon</span>
|
||||||
</button>
|
</button>
|
||||||
|
} @else {
|
||||||
|
<div class="position-relative">
|
||||||
|
<button type="button" class="mt-1 btn btn-purple rounded-pill align-self-center d-flex flex-row justify-content-center align-items-center" [class.disabled]="!canPay || quoteError || cantPayReason || calculating || (!advancedEnabled && selectedOption !== 'accel')" style="width: 200px" (click)="moveToStep('checkout')">
|
||||||
|
<img src="/resources/mempool-accelerator-sparkles-light.svg" height="20" class="mr-2" style="margin-left: -10px">
|
||||||
|
<span i18n="transaction.accelerate|Accelerate button label">Accelerate</span>
|
||||||
|
</button>
|
||||||
|
@if (quoteError || cantPayReason) {
|
||||||
|
<div class="btn-error-wrapper"><span class="btn-error"><app-mempool-error [error]="quoteError || cantPayReason" [textOnly]="true" alertClass=""></app-mempool-error></span></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
@ -189,3 +189,17 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-error {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--red);
|
||||||
|
text-align: center;
|
||||||
|
width: 200px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-error-wrapper {
|
||||||
|
height: 26px;
|
||||||
|
}
|
@ -23,6 +23,10 @@ export type AccelerationEstimate = {
|
|||||||
vsizeFee: number;
|
vsizeFee: number;
|
||||||
pools: number[];
|
pools: number[];
|
||||||
availablePaymentMethods: {[method: string]: {min: number, max: number}};
|
availablePaymentMethods: {[method: string]: {min: number, max: number}};
|
||||||
|
unavailable?: boolean;
|
||||||
|
options: { // recommended bid options
|
||||||
|
fee: number; // recommended userBid in sats
|
||||||
|
}[];
|
||||||
}
|
}
|
||||||
export type TxSummary = {
|
export type TxSummary = {
|
||||||
txid: string; // txid of the current transaction
|
txid: string; // txid of the current transaction
|
||||||
@ -59,19 +63,25 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
@Input() forceMobile: boolean = false;
|
@Input() forceMobile: boolean = false;
|
||||||
@Input() showDetails: boolean = false;
|
@Input() showDetails: boolean = false;
|
||||||
@Input() noCTA: boolean = false;
|
@Input() noCTA: boolean = false;
|
||||||
|
@Output() unavailable = new EventEmitter<boolean>();
|
||||||
@Output() completed = new EventEmitter<boolean>();
|
@Output() completed = new EventEmitter<boolean>();
|
||||||
@Output() hasDetails = new EventEmitter<boolean>();
|
@Output() hasDetails = new EventEmitter<boolean>();
|
||||||
@Output() changeMode = new EventEmitter<boolean>();
|
@Output() changeMode = new EventEmitter<boolean>();
|
||||||
|
|
||||||
calculating = true;
|
calculating = true;
|
||||||
selectedOption: 'wait' | 'accel';
|
selectedOption: 'wait' | 'accel';
|
||||||
error = '';
|
cantPayReason = '';
|
||||||
|
quoteError = ''; // error fetching estimate or initial data
|
||||||
|
accelerateError = ''; // error executing acceleration
|
||||||
|
btcpayInvoiceFailed = false;
|
||||||
|
timePaid: number = 0; // time acceleration requested
|
||||||
math = Math;
|
math = Math;
|
||||||
isMobile: boolean = window.innerWidth <= 767.98;
|
isMobile: boolean = window.innerWidth <= 767.98;
|
||||||
|
|
||||||
private _step: CheckoutStep = 'summary';
|
private _step: CheckoutStep = 'summary';
|
||||||
simpleMode: boolean = true;
|
simpleMode: boolean = true;
|
||||||
paymentMethod: 'cashapp' | 'btcpay';
|
paymentMethod: 'cashapp' | 'btcpay';
|
||||||
|
timeoutTimer: any;
|
||||||
|
|
||||||
authSubscription$: Subscription;
|
authSubscription$: Subscription;
|
||||||
auth: IAuth | null = null;
|
auth: IAuth | null = null;
|
||||||
@ -98,6 +108,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// square
|
// square
|
||||||
loadingCashapp = false;
|
loadingCashapp = false;
|
||||||
|
cashappError = false;
|
||||||
cashappSubmit: any;
|
cashappSubmit: any;
|
||||||
payments: any;
|
payments: any;
|
||||||
cashAppPay: any;
|
cashAppPay: any;
|
||||||
@ -125,7 +136,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
if (this.auth?.user?.userId !== auth?.user?.userId) {
|
if (this.auth?.user?.userId !== auth?.user?.userId) {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
this.estimate = null;
|
this.estimate = null;
|
||||||
this.error = null;
|
this.quoteError = null;
|
||||||
|
this.accelerateError = null;
|
||||||
|
this.timePaid = 0;
|
||||||
|
this.btcpayInvoiceFailed = false;
|
||||||
this.moveToStep('summary');
|
this.moveToStep('summary');
|
||||||
} else {
|
} else {
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
@ -178,10 +192,14 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
moveToStep(step: CheckoutStep) {
|
moveToStep(step: CheckoutStep) {
|
||||||
this._step = step;
|
this._step = step;
|
||||||
|
if (this.timeoutTimer) {
|
||||||
|
clearTimeout(this.timeoutTimer);
|
||||||
|
}
|
||||||
if (!this.estimate && ['quote', 'summary', 'checkout'].includes(this.step)) {
|
if (!this.estimate && ['quote', 'summary', 'checkout'].includes(this.step)) {
|
||||||
this.fetchEstimate();
|
this.fetchEstimate();
|
||||||
}
|
}
|
||||||
if (this._step === 'checkout' && this.canPayWithBitcoin) {
|
if (this._step === 'checkout' && this.canPayWithBitcoin) {
|
||||||
|
this.btcpayInvoiceFailed = false;
|
||||||
this.loadingBtcpayInvoice = true;
|
this.loadingBtcpayInvoice = true;
|
||||||
this.invoice = null;
|
this.invoice = null;
|
||||||
this.requestBTCPayInvoice();
|
this.requestBTCPayInvoice();
|
||||||
@ -189,6 +207,13 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.loadingCashapp = true;
|
this.loadingCashapp = true;
|
||||||
this.insertSquare();
|
this.insertSquare();
|
||||||
this.setupSquare();
|
this.setupSquare();
|
||||||
|
} else if (this._step === 'paid') {
|
||||||
|
this.timePaid = Date.now();
|
||||||
|
this.timeoutTimer = setTimeout(() => {
|
||||||
|
if (this.step === 'paid') {
|
||||||
|
this.accelerateError = 'internal_server_error';
|
||||||
|
}
|
||||||
|
}, 120000)
|
||||||
}
|
}
|
||||||
this.hasDetails.emit(this._step === 'quote');
|
this.hasDetails.emit(this._step === 'quote');
|
||||||
}
|
}
|
||||||
@ -226,47 +251,47 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.estimateSubscription.unsubscribe();
|
this.estimateSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
this.calculating = true;
|
this.calculating = true;
|
||||||
|
this.quoteError = null;
|
||||||
|
this.accelerateError = null;
|
||||||
this.estimateSubscription = this.servicesApiService.estimate$(this.tx.txid).pipe(
|
this.estimateSubscription = this.servicesApiService.estimate$(this.tx.txid).pipe(
|
||||||
tap((response) => {
|
tap((response) => {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
this.error = `cannot_accelerate_tx`;
|
this.quoteError = `cannot_accelerate_tx`;
|
||||||
|
if (this.step === 'summary') {
|
||||||
|
this.unavailable.emit(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.estimate = response.body;
|
this.estimate = response.body;
|
||||||
if (!this.estimate) {
|
if (!this.estimate) {
|
||||||
this.error = `cannot_accelerate_tx`;
|
this.quoteError = `cannot_accelerate_tx`;
|
||||||
|
if (this.step === 'summary') {
|
||||||
|
this.unavailable.emit(true);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.estimate.hasAccess === true && this.estimate.userBalance <= 0) {
|
if (this.estimate.hasAccess === true && this.estimate.userBalance <= 0) {
|
||||||
if (this.isLoggedIn()) {
|
if (this.isLoggedIn()) {
|
||||||
this.error = `not_enough_balance`;
|
this.quoteError = `not_enough_balance`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.estimate.unavailable) {
|
||||||
|
this.quoteError = `temporarily_unavailable`;
|
||||||
|
}
|
||||||
this.hasAncestors = this.estimate.txSummary.ancestorCount > 1;
|
this.hasAncestors = this.estimate.txSummary.ancestorCount > 1;
|
||||||
this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate, this.miningStats);
|
this.etaInfo$ = this.etaService.getProjectedEtaObservable(this.estimate, this.miningStats);
|
||||||
|
|
||||||
// Make min extra fee at least 50% of the current tx fee
|
this.maxRateOptions = this.estimate.options.map((option, index) => ({
|
||||||
this.minExtraCost = nextRoundNumber(Math.max(this.estimate.cost * 2, this.estimate.txSummary.effectiveFee));
|
fee: option.fee,
|
||||||
|
rate: (this.estimate.txSummary.effectiveFee + option.fee) / this.estimate.txSummary.effectiveVsize,
|
||||||
this.maxRateOptions = [1, 2, 4].map((multiplier, index) => {
|
index
|
||||||
return {
|
}));
|
||||||
fee: this.minExtraCost * multiplier,
|
|
||||||
rate: (this.estimate.txSummary.effectiveFee + (this.minExtraCost * multiplier)) / this.estimate.txSummary.effectiveVsize,
|
|
||||||
index,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.minBidAllowed = this.minExtraCost * MIN_BID_RATIO;
|
|
||||||
this.defaultBid = this.minExtraCost * DEFAULT_BID_RATIO;
|
|
||||||
this.maxBidAllowed = this.minExtraCost * MAX_BID_RATIO;
|
|
||||||
|
|
||||||
|
this.defaultBid = this.maxRateOptions[1].fee;
|
||||||
this.userBid = this.defaultBid;
|
this.userBid = this.defaultBid;
|
||||||
if (this.userBid < this.minBidAllowed) {
|
|
||||||
this.userBid = this.minBidAllowed;
|
|
||||||
} else if (this.userBid > this.maxBidAllowed) {
|
|
||||||
this.userBid = this.maxBidAllowed;
|
|
||||||
}
|
|
||||||
this.cost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
this.cost = this.userBid + this.estimate.mempoolBaseFee + this.estimate.vsizeFee;
|
||||||
|
|
||||||
|
this.validateChoice();
|
||||||
|
|
||||||
if (this.step === 'checkout' && this.canPayWithBitcoin && !this.loadingBtcpayInvoice) {
|
if (this.step === 'checkout' && this.canPayWithBitcoin && !this.loadingBtcpayInvoice) {
|
||||||
this.loadingBtcpayInvoice = true;
|
this.loadingBtcpayInvoice = true;
|
||||||
this.requestBTCPayInvoice();
|
this.requestBTCPayInvoice();
|
||||||
@ -279,13 +304,32 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
catchError((response) => {
|
catchError((response) => {
|
||||||
this.estimate = undefined;
|
this.estimate = undefined;
|
||||||
this.error = `cannot_accelerate_tx`;
|
this.quoteError = `cannot_accelerate_tx`;
|
||||||
this.estimateSubscription.unsubscribe();
|
this.estimateSubscription.unsubscribe();
|
||||||
|
if (this.step === 'summary') {
|
||||||
|
this.unavailable.emit(true);
|
||||||
|
} else {
|
||||||
|
this.accelerateError = 'cannot_accelerate_tx';
|
||||||
|
}
|
||||||
return of(null);
|
return of(null);
|
||||||
})
|
})
|
||||||
).subscribe();
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateChoice(): void {
|
||||||
|
if (!this.canPay) {
|
||||||
|
if (this.estimate?.availablePaymentMethods?.balance) {
|
||||||
|
if (this.cost >= this.estimate?.userBalance) {
|
||||||
|
this.cantPayReason = 'not_enough_balance';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.cantPayReason = 'cannot_accelerate_tx';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.cantPayReason = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User changed his bid
|
* User changed his bid
|
||||||
*/
|
*/
|
||||||
@ -319,11 +363,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.moveToStep('paid')
|
this.moveToStep('paid')
|
||||||
},
|
},
|
||||||
error: (response) => {
|
error: (response) => {
|
||||||
if (response.status === 403 && response.error === 'not_available') {
|
this.accelerateError = response.error;
|
||||||
this.error = 'waitlisted';
|
|
||||||
} else {
|
|
||||||
this.error = response.error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -371,6 +411,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
await this.requestCashAppPayment();
|
await this.requestCashAppPayment();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.debug('Error loading Square Payments', e);
|
console.debug('Error loading Square Payments', e);
|
||||||
|
this.cashappError = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -417,7 +458,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
this.cashAppPay.addEventListener('ontokenization', function (event) {
|
this.cashAppPay.addEventListener('ontokenization', function (event) {
|
||||||
const { tokenResult, error } = event.detail;
|
const { tokenResult, error } = event.detail;
|
||||||
if (error) {
|
if (error) {
|
||||||
this.error = error;
|
this.accelerateError = error;
|
||||||
} else if (tokenResult.status === 'OK') {
|
} else if (tokenResult.status === 'OK') {
|
||||||
that.servicesApiService.accelerateWithCashApp$(
|
that.servicesApiService.accelerateWithCashApp$(
|
||||||
that.tx.txid,
|
that.tx.txid,
|
||||||
@ -440,10 +481,8 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
error: (response) => {
|
error: (response) => {
|
||||||
if (response.status === 403 && response.error === 'not_available') {
|
that.accelerateError = response.error;
|
||||||
that.error = 'waitlisted';
|
if (!(response.status === 403 && response.error === 'not_available')) {
|
||||||
} else {
|
|
||||||
that.error = response.error;
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Reset everything by reloading the page :D, can be improved
|
// Reset everything by reloading the page :D, can be improved
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@ -468,6 +507,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
}),
|
}),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
this.btcpayInvoiceFailed = true;
|
||||||
return of(null);
|
return of(null);
|
||||||
})
|
})
|
||||||
).subscribe((invoice) => {
|
).subscribe((invoice) => {
|
||||||
@ -497,6 +537,32 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
return this._step;
|
return this._step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get paymentMethods() {
|
||||||
|
return Object.keys(this.estimate?.availablePaymentMethods || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
get couldPayWithBitcoin() {
|
||||||
|
return !!this.estimate?.availablePaymentMethods?.bitcoin;
|
||||||
|
}
|
||||||
|
|
||||||
|
get couldPayWithCashapp() {
|
||||||
|
if (!this.cashappEnabled || this.stateService.referrer !== 'https://cash.app/') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!this.estimate?.availablePaymentMethods?.cashapp;
|
||||||
|
}
|
||||||
|
|
||||||
|
get couldPayWithBalance() {
|
||||||
|
if (!this.hasAccessToBalanceMode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!this.estimate?.availablePaymentMethods?.balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get couldPay() {
|
||||||
|
return this.couldPayWithBalance || this.couldPayWithBitcoin || this.couldPayWithCashapp;
|
||||||
|
}
|
||||||
|
|
||||||
get canPayWithBitcoin() {
|
get canPayWithBitcoin() {
|
||||||
const paymentMethod = this.estimate?.availablePaymentMethods?.bitcoin;
|
const paymentMethod = this.estimate?.availablePaymentMethods?.bitcoin;
|
||||||
return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max;
|
return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max;
|
||||||
@ -523,7 +589,7 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const paymentMethod = this.estimate?.availablePaymentMethods?.balance;
|
const paymentMethod = this.estimate?.availablePaymentMethods?.balance;
|
||||||
return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max;
|
return paymentMethod && this.cost >= paymentMethod.min && this.cost <= paymentMethod.max && this.cost <= this.estimate?.userBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canPay() {
|
get canPay() {
|
||||||
@ -534,6 +600,10 @@ export class AccelerateCheckout implements OnInit, OnDestroy {
|
|||||||
return this.isLoggedIn() && this.estimate?.hasAccess;
|
return this.isLoggedIn() && this.estimate?.hasAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get timeSincePaid(): number {
|
||||||
|
return Date.now() - this.timePaid;
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
onResize(): void {
|
onResize(): void {
|
||||||
this.isMobile = window.innerWidth <= 767.98;
|
this.isMobile = window.innerWidth <= 767.98;
|
||||||
|
@ -80,7 +80,7 @@ export class ActiveAccelerationBox implements OnChanges {
|
|||||||
data.push(getDataItem(
|
data.push(getDataItem(
|
||||||
(this.miningStats.lastEstimatedHashrate - totalAcceleratedHashrate),
|
(this.miningStats.lastEstimatedHashrate - totalAcceleratedHashrate),
|
||||||
'rgba(127, 127, 127, 0.3)',
|
'rgba(127, 127, 127, 0.3)',
|
||||||
`not accelerating (${notAcceleratedByPercentage})`,
|
$localize`not accelerating` + ` (${notAcceleratedByPercentage})`,
|
||||||
false,
|
false,
|
||||||
) as PieSeriesOption);
|
) as PieSeriesOption);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ export class TimeComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
calculate() {
|
calculate() {
|
||||||
if (!this.time) {
|
if (this.time == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@
|
|||||||
[miningStats]="miningStats"
|
[miningStats]="miningStats"
|
||||||
[eta]="eta"
|
[eta]="eta"
|
||||||
[scrollEvent]="scrollIntoAccelPreview"
|
[scrollEvent]="scrollIntoAccelPreview"
|
||||||
|
(unavailable)="eligibleForAcceleration = false"
|
||||||
class="h-100 w-100"
|
class="h-100 w-100"
|
||||||
></app-accelerate-checkout>
|
></app-accelerate-checkout>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -128,7 +128,7 @@
|
|||||||
<div class="title float-left mb-1">
|
<div class="title float-left mb-1">
|
||||||
<h2><a [href]="[ isMempoolSpaceBuild ? '/accelerator' : 'https://mempool.space/accelerator']" [target]="isMempoolSpaceBuild ? '' : 'blank'"><app-svg-images name="accelerator" [height]="isMobile ? '35px' : '45px'"></app-svg-images></a></h2>
|
<h2><a [href]="[ isMempoolSpaceBuild ? '/accelerator' : 'https://mempool.space/accelerator']" [target]="isMempoolSpaceBuild ? '' : 'blank'"><app-svg-images name="accelerator" [height]="isMobile ? '35px' : '45px'"></app-svg-images></a></h2>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline-info accelerator-toggle btn-sm float-right" (click)="closeAccelerator()" i18n="accelerator.hide">Hide accelerator</button>
|
<button type="button" class="btn btn-outline-info accelerator-toggle btn-sm float-right" [class.hide-on-mobile]="hasAccelerationDetails" (click)="closeAccelerator()" i18n="accelerator.hide">Hide accelerator</button>
|
||||||
<button *ngIf="hasAccelerationDetails" class="btn btn-sm btn-outline-info details-button float-right ml-2" (click)="showAccelerationDetails = !showAccelerationDetails" i18n="transaction.details|Transaction Details">Details</button>
|
<button *ngIf="hasAccelerationDetails" class="btn btn-sm btn-outline-info details-button float-right ml-2" (click)="showAccelerationDetails = !showAccelerationDetails" i18n="transaction.details|Transaction Details">Details</button>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
@ -146,6 +146,7 @@
|
|||||||
[noCTA]="true"
|
[noCTA]="true"
|
||||||
(hasDetails)="setHasAccelerationDetails($event)"
|
(hasDetails)="setHasAccelerationDetails($event)"
|
||||||
(completed)="onAccelerationCompleted()"
|
(completed)="onAccelerationCompleted()"
|
||||||
|
(unavailable)="eligibleForAcceleration = false"
|
||||||
class="h-100 w-100"
|
class="h-100 w-100"
|
||||||
></app-accelerate-checkout>
|
></app-accelerate-checkout>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -167,6 +167,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px){
|
||||||
|
.hide-on-mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.effective-fee-rating {
|
.effective-fee-rating {
|
||||||
@media (max-width: 767px){
|
@media (max-width: 767px){
|
||||||
margin-right: 0px !important;
|
margin-right: 0px !important;
|
||||||
|
@ -35,9 +35,13 @@ const routes: Routes = [
|
|||||||
loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule),
|
loadChildren: () => import('../components/about/about.module').then(m => m.AboutModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'blocks',
|
path: 'blocks/:page',
|
||||||
component: BlocksList,
|
component: BlocksList,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'blocks',
|
||||||
|
redirectTo: 'blocks/1',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'terms-of-service',
|
path: 'terms-of-service',
|
||||||
loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule),
|
loadChildren: () => import('../components/terms-of-service/terms-of-service.module').then(m => m.TermsOfServiceModule),
|
||||||
|
@ -1,2 +1,9 @@
|
|||||||
<div class="alert" [class]="alertClass" [innerHTML]="errorContent">
|
@if (!textOnly) {
|
||||||
</div>
|
<div class="alert" [class]="alertClass" [innerHTML]="errorContent">
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<span [class]="alertClass" [innerHTML]="errorContent">
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
<ng-template #lowBalance i18n="accelerator.low-balance">Your balance is too low.<br/>Please <a style="color:#105fb0" href="/services/accelerator/overview">top up your account</a>.</ng-template>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { Component, Input, OnInit } from "@angular/core";
|
import { Component, EmbeddedViewRef, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
|
|
||||||
const MempoolErrors = {
|
export const MempoolErrors = {
|
||||||
'bad_request': `Your request was not valid. Please try again.`,
|
'bad_request': `Your request was not valid. Please try again.`,
|
||||||
'internal_server_error': `Something went wrong, please try again later`,
|
'internal_server_error': `Something went wrong, please try again later`,
|
||||||
|
'temporarily_unavailable': `Acceleration temporarily unavailable`,
|
||||||
'acceleration_duplicated': `This transaction has already been accelerated.`,
|
'acceleration_duplicated': `This transaction has already been accelerated.`,
|
||||||
'acceleration_outbid': `Your fee delta is too low.`,
|
'acceleration_outbid': `Your fee delta is too low.`,
|
||||||
'cannot_accelerate_tx': `Cannot accelerate this transaction.`,
|
'cannot_accelerate_tx': `Cannot accelerate this transaction.`,
|
||||||
@ -15,7 +16,7 @@ const MempoolErrors = {
|
|||||||
'mempool_rejected_raw_tx': `Our mempool rejected this transaction`,
|
'mempool_rejected_raw_tx': `Our mempool rejected this transaction`,
|
||||||
'no_mining_pool_available': `No mining pool available at the moment`,
|
'no_mining_pool_available': `No mining pool available at the moment`,
|
||||||
'not_available': `You current subscription does not allow you to access this feature.`,
|
'not_available': `You current subscription does not allow you to access this feature.`,
|
||||||
'not_enough_balance': `Your balance is too low. Please <a style="color:#105fb0" href="/services/accelerator/overview">top up your account</a>.`,
|
'not_enough_balance': ``,
|
||||||
'not_verified': `You must verify your account to use this feature.`,
|
'not_verified': `You must verify your account to use this feature.`,
|
||||||
'recommended_fees_not_available': `Recommended fees are not available right now.`,
|
'recommended_fees_not_available': `Recommended fees are not available right now.`,
|
||||||
'too_many_relatives': `This transaction has too many relatives.`,
|
'too_many_relatives': `This transaction has too many relatives.`,
|
||||||
@ -42,14 +43,30 @@ export function isMempoolError(error: string) {
|
|||||||
templateUrl: './mempool-error.component.html'
|
templateUrl: './mempool-error.component.html'
|
||||||
})
|
})
|
||||||
export class MempoolErrorComponent implements OnInit {
|
export class MempoolErrorComponent implements OnInit {
|
||||||
|
@ViewChild('lowBalance', { static: true }) lowBalance!: TemplateRef<any>;
|
||||||
@Input() error: string;
|
@Input() error: string;
|
||||||
@Input() alertClass = 'alert-danger';
|
@Input() alertClass = 'alert-danger';
|
||||||
|
@Input() textOnly = false;
|
||||||
errorContent: SafeHtml;
|
errorContent: SafeHtml;
|
||||||
|
|
||||||
constructor(private sanitizer: DomSanitizer) { }
|
constructor(
|
||||||
|
private sanitizer: DomSanitizer,
|
||||||
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
if (Object.keys(MempoolErrors).includes(this.error)) {
|
// Special hack for the i18n string with a href link inside
|
||||||
|
const embeddedViewRef: EmbeddedViewRef<any> = this.lowBalance.createEmbeddedView({});
|
||||||
|
embeddedViewRef.detectChanges();
|
||||||
|
const rawHtml = embeddedViewRef.rootNodes.map((node) => {
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
return node.textContent;
|
||||||
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
return node.outerHTML;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}).join('');
|
||||||
|
MempoolErrors['not_enough_balance'] = rawHtml;
|
||||||
|
if (Object.keys(MempoolErrors).includes(this.error)) {
|
||||||
this.errorContent = this.sanitizer.bypassSecurityTrustHtml(MempoolErrors[this.error]);
|
this.errorContent = this.sanitizer.bypassSecurityTrustHtml(MempoolErrors[this.error]);
|
||||||
} else {
|
} else {
|
||||||
this.errorContent = this.error;
|
this.errorContent = this.error;
|
||||||
|
@ -4,7 +4,7 @@ import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstra
|
|||||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
|
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
|
||||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
|
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
|
||||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline } from '@fortawesome/free-solid-svg-icons';
|
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||||
import { MenuComponent } from '../components/menu/menu.component';
|
import { MenuComponent } from '../components/menu/menu.component';
|
||||||
import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component';
|
import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component';
|
||||||
@ -433,5 +433,6 @@ export class SharedModule {
|
|||||||
library.addIcons(faWandMagicSparkles);
|
library.addIcons(faWandMagicSparkles);
|
||||||
library.addIcons(faFaucetDrip);
|
library.addIcons(faFaucetDrip);
|
||||||
library.addIcons(faTimeline);
|
library.addIcons(faTimeline);
|
||||||
|
library.addIcons(faCircleXmark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -131,7 +131,6 @@
|
|||||||
"node208.fra.mempool.space",
|
"node208.fra.mempool.space",
|
||||||
"node209.fra.mempool.space",
|
"node209.fra.mempool.space",
|
||||||
"node210.fra.mempool.space",
|
"node210.fra.mempool.space",
|
||||||
"node211.fra.mempool.space",
|
|
||||||
"node212.fra.mempool.space",
|
"node212.fra.mempool.space",
|
||||||
"node213.fra.mempool.space",
|
"node213.fra.mempool.space",
|
||||||
"node214.fra.mempool.space",
|
"node214.fra.mempool.space",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user