mirror of
https://github.com/mempool/mempool.git
synced 2025-03-13 11:36:07 +01:00
custom dashboard wallet widgets
This commit is contained in:
parent
862c9591a1
commit
e095192968
18 changed files with 149 additions and 24 deletions
|
@ -83,7 +83,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
|
|||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.isLoading = true;
|
||||
if (!this.address || !this.stats) {
|
||||
if (!this.addressSummary$ && (!this.address || !this.stats)) {
|
||||
return;
|
||||
}
|
||||
if (changes.address || changes.isPubkey || changes.addressSummary$ || changes.stats) {
|
||||
|
@ -144,15 +144,16 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
|
|||
}
|
||||
|
||||
prepareChartOptions(summary: AddressTxSummary[]) {
|
||||
if (!summary || !this.stats) {
|
||||
if (!summary) {
|
||||
return;
|
||||
}
|
||||
|
||||
let total = (this.stats.funded_txo_sum - this.stats.spent_txo_sum);
|
||||
const total = this.stats ? (this.stats.funded_txo_sum - this.stats.spent_txo_sum) : summary.reduce((acc, tx) => acc + tx.value, 0);
|
||||
let runningTotal = total;
|
||||
const processData = summary.map(d => {
|
||||
const balance = total;
|
||||
const fiatBalance = total * d.price / 100_000_000;
|
||||
total -= d.value;
|
||||
const balance = runningTotal;
|
||||
const fiatBalance = runningTotal * d.price / 100_000_000;
|
||||
runningTotal -= d.value;
|
||||
return {
|
||||
time: d.time * 1000,
|
||||
balance,
|
||||
|
@ -172,7 +173,7 @@ export class AddressGraphComponent implements OnChanges, OnDestroy {
|
|||
this.fiatData = this.fiatData.filter(d => d[0] >= startFiat);
|
||||
}
|
||||
this.data.push(
|
||||
{value: [now, this.stats.funded_txo_sum - this.stats.spent_txo_sum], symbol: 'none', tooltip: { show: false }}
|
||||
{value: [now, total], symbol: 'none', tooltip: { show: false }}
|
||||
);
|
||||
|
||||
const maxValue = this.data.reduce((acc, d) => Math.max(acc, Math.abs(d[1] ?? d.value[1])), 0);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<app-truncate [text]="transaction.txid" [lastChars]="5"></app-truncate>
|
||||
</a>
|
||||
</td>
|
||||
<td class="table-cell-satoshis"><app-amount [satoshis]="transaction.value" digitsInfo="1.2-4" [noFiat]="true"></app-amount></td>
|
||||
<td class="table-cell-satoshis"><app-amount [satoshis]="transaction.value" [digitsInfo]="getAmountDigits(transaction.value)" [noFiat]="true"></app-amount></td>
|
||||
<td class="table-cell-fiat" ><app-fiat [value]="transaction.value" [blockConversion]="transaction.price" digitsInfo="1.0-0"></app-fiat></td>
|
||||
<td class="table-cell-date"><app-time kind="since" [time]="transaction.time" [fastRender]="true" [showTooltip]="true"></app-time></td>
|
||||
</tr>
|
||||
|
|
|
@ -43,7 +43,7 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On
|
|||
|
||||
startAddressSubscription(): void {
|
||||
this.isLoading = true;
|
||||
if (!this.address || !this.addressInfo) {
|
||||
if (!this.addressSummary$ && (!this.address || !this.addressInfo)) {
|
||||
return;
|
||||
}
|
||||
this.transactions$ = (this.addressSummary$ || (this.isPubkey
|
||||
|
@ -55,7 +55,7 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On
|
|||
})
|
||||
)).pipe(
|
||||
map(summary => {
|
||||
return summary?.slice(0, 6);
|
||||
return summary?.filter(tx => Math.abs(tx.value) >= 1000000)?.slice(0, 6);
|
||||
}),
|
||||
switchMap(txs => {
|
||||
return (zip(txs.map(tx => this.priceService.getBlockPrice$(tx.time, txs.length < 3, this.currency).pipe(
|
||||
|
@ -68,6 +68,12 @@ export class AddressTransactionsWidgetComponent implements OnInit, OnChanges, On
|
|||
))));
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
getAmountDigits(value: number): string {
|
||||
const decimals = Math.max(0, 4 - Math.ceil(Math.log10(Math.abs(value / 100_000_000))));
|
||||
return `1.${decimals}-${decimals}`;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
<div class="item">
|
||||
<h5 class="card-title" i18n="dashboard.btc-holdings">BTC Holdings</h5>
|
||||
<div class="card-text">
|
||||
{{ ((addressInfo.chain_stats.funded_txo_sum - addressInfo.chain_stats.spent_txo_sum) / 100_000_000) | number: '1.2-2' }} <span class="symbol" i18n="shared.btc|BTC">BTC</span>
|
||||
{{ ((total) / 100_000_000) | number: '1.2-2' }} <span class="symbol" i18n="shared.btc|BTC">BTC</span>
|
||||
</div>
|
||||
<div class="symbol">
|
||||
<app-fiat [value]="(addressInfo.chain_stats.funded_txo_sum - addressInfo.chain_stats.spent_txo_sum)"></app-fiat>
|
||||
<app-fiat [value]="(total)"></app-fiat>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
|
|
|
@ -19,6 +19,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges {
|
|||
isLoading: boolean = true;
|
||||
error: any;
|
||||
|
||||
total: number = 0;
|
||||
delta7d: number = 0;
|
||||
delta30d: number = 0;
|
||||
|
||||
|
@ -34,7 +35,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges {
|
|||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.isLoading = true;
|
||||
if (!this.address || !this.addressInfo) {
|
||||
if (!this.addressSummary$ && (!this.address || !this.addressInfo)) {
|
||||
return;
|
||||
}
|
||||
(this.addressSummary$ || (this.isPubkey
|
||||
|
@ -57,6 +58,7 @@ export class BalanceWidgetComponent implements OnInit, OnChanges {
|
|||
calculateStats(summary: AddressTxSummary[]): void {
|
||||
let weekTotal = 0;
|
||||
let monthTotal = 0;
|
||||
this.total = this.addressInfo ? this.addressInfo.chain_stats.funded_txo_sum - this.addressInfo.chain_stats.spent_txo_sum : summary.reduce((acc, tx) => acc + tx.value, 0);
|
||||
|
||||
const weekAgo = (new Date(new Date().setHours(0, 0, 0, 0) - (7 * 24 * 60 * 60 * 1000)).getTime()) / 1000;
|
||||
const monthAgo = (new Date(new Date().setHours(0, 0, 0, 0) - (30 * 24 * 60 * 60 * 1000)).getTime()) / 1000;
|
||||
|
|
|
@ -257,6 +257,36 @@
|
|||
</div>
|
||||
</div>
|
||||
}
|
||||
@case ('walletBalance') {
|
||||
<div class="col card-wrapper" [style.order]="isMobile && widget.mobileOrder || 8">
|
||||
<div class="main-title" i18n="dashboard.treasury">Treasury</div>
|
||||
<app-balance-widget [addressSummary$]="walletSummary$"></app-balance-widget>
|
||||
</div>
|
||||
}
|
||||
@case ('wallet') {
|
||||
<div class="col" style="max-height: 410px" [style.order]="isMobile && widget.mobileOrder || 8">
|
||||
<div class="card graph-card">
|
||||
<div class="card-body">
|
||||
<span class="title-link">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.balance-history">Balance History</h5>
|
||||
</span>
|
||||
<app-address-graph [addressSummary$]="walletSummary$" [period]="widget.props.period || 'all'" [widget]="true" [height]="graphHeight"></app-address-graph>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@case ('walletTransactions') {
|
||||
<div class="col" style="max-height: 410px" [style.order]="isMobile && widget.mobileOrder || 8">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<span class="title-link">
|
||||
<h5 class="card-title d-inline" i18n="dashboard.treasury-transactions">Treasury Transactions</h5>
|
||||
</span>
|
||||
<app-address-transactions-widget [addressSummary$]="walletSummary$"></app-address-transactions-widget>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@case ('twitter') {
|
||||
<div class="col" style="min-height:410px" [style.order]="isMobile && widget.mobileOrder || 8">
|
||||
<div class="card graph-card">
|
||||
|
|
|
@ -62,8 +62,10 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
widgets;
|
||||
|
||||
addressSubscription: Subscription;
|
||||
walletSubscription: Subscription;
|
||||
blockTxSubscription: Subscription;
|
||||
addressSummary$: Observable<AddressTxSummary[]>;
|
||||
walletSummary$: Observable<AddressTxSummary[]>;
|
||||
address: Address;
|
||||
|
||||
goggleResolution = 82;
|
||||
|
@ -107,6 +109,10 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
this.websocketService.stopTrackingAddress();
|
||||
this.address = null;
|
||||
}
|
||||
if (this.walletSubscription) {
|
||||
this.walletSubscription.unsubscribe();
|
||||
this.websocketService.stopTrackingWallet();
|
||||
}
|
||||
this.destroy$.next(1);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
@ -260,6 +266,7 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
});
|
||||
|
||||
this.startAddressSubscription();
|
||||
this.startWalletSubscription();
|
||||
}
|
||||
|
||||
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
||||
|
@ -358,6 +365,51 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni
|
|||
}
|
||||
}
|
||||
|
||||
startWalletSubscription(): void {
|
||||
if (this.stateService.env.customize && this.stateService.env.customize.dashboard.widgets.some(w => w.props?.wallet)) {
|
||||
const walletName = this.stateService.env.customize.dashboard.widgets.find(w => w.props?.wallet).props.wallet;
|
||||
this.websocketService.startTrackingWallet(walletName);
|
||||
|
||||
this.walletSummary$ = this.apiService.getWallet$(walletName).pipe(
|
||||
catchError(e => {
|
||||
return of(null);
|
||||
}),
|
||||
map((walletTransactions) => {
|
||||
const transactions = Object.values(walletTransactions).flatMap(wallet => wallet.transactions);
|
||||
return this.deduplicateWalletTransactions(transactions);
|
||||
}),
|
||||
switchMap(initial => this.stateService.walletTransactions$.pipe(
|
||||
startWith(null),
|
||||
scan((summary, walletTransactions) => {
|
||||
if (walletTransactions) {
|
||||
const transactions: AddressTxSummary[] = [...summary, ...Object.values(walletTransactions).flat()];
|
||||
return this.deduplicateWalletTransactions(transactions);
|
||||
}
|
||||
return summary;
|
||||
}, initial)
|
||||
)),
|
||||
share(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
deduplicateWalletTransactions(walletTransactions: AddressTxSummary[]): AddressTxSummary[] {
|
||||
const transactions = new Map<string, AddressTxSummary>();
|
||||
for (const tx of walletTransactions) {
|
||||
if (transactions.has(tx.txid)) {
|
||||
transactions.get(tx.txid).value += tx.value;
|
||||
} else {
|
||||
transactions.set(tx.txid, tx);
|
||||
}
|
||||
}
|
||||
return Array.from(transactions.values()).sort((a, b) => {
|
||||
if (a.height === b.height) {
|
||||
return b.tx_position - a.tx_position;
|
||||
}
|
||||
return b.height - a.height;
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(): void {
|
||||
if (window.innerWidth >= 992) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<img [src]="enterpriseInfo.img" class="subdomain_logo" [class]="{'rounded': enterpriseInfo.rounded_corner}">
|
||||
}
|
||||
@if (enterpriseInfo?.header_img) {
|
||||
<img *ngIf="enterpriseInfo.header_img" [src]="enterpriseInfo?.header_img" alt="enterpriseInfo.title" height="60px" class="mr-3">
|
||||
<img *ngIf="enterpriseInfo.header_img" [src]="enterpriseInfo?.header_img" [alt]="enterpriseInfo.title" height="60px" class="mr-3">
|
||||
} @else {
|
||||
<app-svg-images *ngIf="!officialMempoolSpace" name="mempoolSpace" viewBox="0 0 500 126" width="500" height="126" class="mempool-logo" style="width: 200px; height: 50px"></app-svg-images>
|
||||
<app-svg-images *ngIf="officialMempoolSpace" name="officialMempoolSpace" style="width: 200px; height: 50px" width="500" height="126" viewBox="0 0 500 126"></app-svg-images>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<a class="navbar-brand d-none d-md-flex" [ngClass]="{'dual-logos': subdomain}" [routerLink]="['/' | relativeUrl]" (click)="brandClick($event)">
|
||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||
@if (enterpriseInfo?.header_img) {
|
||||
<img [src]="enterpriseInfo?.header_img" alt="enterpriseInfo.title" height="48px" class="mr-3">
|
||||
<img [src]="enterpriseInfo?.header_img" [alt]="enterpriseInfo.title" height="48px" class="mr-3">
|
||||
} @else {
|
||||
<ng-template [ngIf]="subdomain && enterpriseInfo">
|
||||
<div class="subdomain_container">
|
||||
|
@ -39,7 +39,7 @@
|
|||
<!-- Mobile -->
|
||||
<a class="navbar-brand d-flex d-md-none justify-content-center" [ngClass]="{'dual-logos': subdomain, 'mr-0': subdomain}" [routerLink]="['/' | relativeUrl]" (click)="brandClick($event)">
|
||||
@if (enterpriseInfo?.header_img) {
|
||||
<img [src]="enterpriseInfo?.header_img" alt="enterpriseInfo.title" height="42px">
|
||||
<img [src]="enterpriseInfo?.header_img" [alt]="enterpriseInfo.title" height="42px">
|
||||
} @else {
|
||||
<ng-template [ngIf]="subdomain && enterpriseInfo">
|
||||
<div class="subdomain_container">
|
||||
|
@ -49,7 +49,7 @@
|
|||
</ng-template>
|
||||
<ng-container *ngIf="{ val: connectionState$ | async } as connectionState">
|
||||
@if (enterpriseInfo?.header_img) {
|
||||
<img [src]="enterpriseInfo?.header_img" alt="enterpriseInfo.title" height="36px">
|
||||
<img [src]="enterpriseInfo?.header_img" [alt]="enterpriseInfo.title" height="36px">
|
||||
} @else {
|
||||
<app-svg-images *ngIf="!officialMempoolSpace" name="mempoolSpace" viewBox="0 0 500 126" class="mempool-logo" [ngStyle]="{'opacity': connectionState.val === 2 ? 1 : 0.5 }"></app-svg-images>
|
||||
<app-svg-images *ngIf="officialMempoolSpace" name="officialMempoolSpace" viewBox="0 0 500 126"></app-svg-images>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="nav-header">
|
||||
@if (enterpriseInfo?.header_img) {
|
||||
<a class="d-flex" [routerLink]="['/' | relativeUrl]">
|
||||
<img *ngIf="enterpriseInfo.header_img" [src]="enterpriseInfo?.header_img" alt="enterpriseInfo.title" height="42px">
|
||||
<img *ngIf="enterpriseInfo.header_img" [src]="enterpriseInfo?.header_img" [alt]="enterpriseInfo.title" height="42px">
|
||||
</a>
|
||||
} @else if (enterpriseInfo?.img || enterpriseInfo?.imageMd5) {
|
||||
<a [routerLink]="['/' | relativeUrl]">
|
||||
|
|
|
@ -164,6 +164,7 @@ export interface AddressTxSummary {
|
|||
height: number;
|
||||
time: number;
|
||||
price?: number;
|
||||
tx_position?: number;
|
||||
}
|
||||
|
||||
export interface ChainStats {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Block, Transaction } from "./electrs.interface";
|
||||
import { AddressTxSummary, Block, Transaction } from "./electrs.interface";
|
||||
|
||||
export interface OptimizedMempoolStats {
|
||||
added: number;
|
||||
|
@ -471,3 +471,8 @@ export interface TxResult {
|
|||
};
|
||||
error?: string;
|
||||
}
|
||||
export interface WalletAddress {
|
||||
address: string;
|
||||
active: boolean;
|
||||
transactions?: AddressTxSummary[];
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ export interface WebsocketResponse {
|
|||
'track-rbf'?: string;
|
||||
'track-rbf-summary'?: boolean;
|
||||
'track-accelerations'?: boolean;
|
||||
'track-wallet'?: string;
|
||||
'watch-mempool'?: boolean;
|
||||
'refresh-blocks'?: boolean;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators, PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights,
|
||||
RbfTree, BlockAudit, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo, TestMempoolAcceptResult,
|
||||
SubmitPackageResult} from '../interfaces/node-api.interface';
|
||||
RbfTree, BlockAudit, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume, AccelerationInfo, TestMempoolAcceptResult, WalletAddress, SubmitPackageResult } from '../interfaces/node-api.interface';
|
||||
import { BehaviorSubject, Observable, catchError, filter, map, of, shareReplay, take, tap } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
|
@ -518,6 +517,12 @@ export class ApiService {
|
|||
);
|
||||
}
|
||||
|
||||
getWallet$(walletName: string): Observable<Record<string, WalletAddress>> {
|
||||
return this.httpClient.get<Record<string, WalletAddress>>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/wallet/${walletName}`
|
||||
);
|
||||
}
|
||||
|
||||
getAccelerationsByPool$(slug: string): Observable<AccelerationInfo[]> {
|
||||
return this.httpClient.get<AccelerationInfo[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/accelerations/pool/${slug}`
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
|
||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable } from 'rxjs';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { AddressTxSummary, Transaction } from '../interfaces/electrs.interface';
|
||||
import { AccelerationDelta, HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockUpdate, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, isMempoolState } from '../interfaces/websocket.interface';
|
||||
import { Acceleration, AccelerationPosition, BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
|
@ -159,6 +159,7 @@ export class StateService {
|
|||
mempoolRemovedTransactions$ = new Subject<Transaction>();
|
||||
multiAddressTransactions$ = new Subject<{ [address: string]: { mempool: Transaction[], confirmed: Transaction[], removed: Transaction[] }}>();
|
||||
blockTransactions$ = new Subject<Transaction>();
|
||||
walletTransactions$ = new Subject<Record<string, AddressTxSummary[]>>();
|
||||
isLoadingWebSocket$ = new ReplaySubject<boolean>(1);
|
||||
isLoadingMempool$ = new BehaviorSubject<boolean>(true);
|
||||
vbytesPerSecond$ = new ReplaySubject<number>(1);
|
||||
|
|
|
@ -34,6 +34,8 @@ export class WebsocketService {
|
|||
private isTrackingAddress: string | false = false;
|
||||
private isTrackingAddresses: string[] | false = false;
|
||||
private isTrackingAccelerations: boolean = false;
|
||||
private isTrackingWallet: boolean = false;
|
||||
private trackingWalletName: string;
|
||||
private trackingMempoolBlock: number;
|
||||
private stoppingTrackMempoolBlock: any | null = null;
|
||||
private latestGitCommit = '';
|
||||
|
@ -137,6 +139,9 @@ export class WebsocketService {
|
|||
if (this.isTrackingAccelerations) {
|
||||
this.startTrackAccelerations();
|
||||
}
|
||||
if (this.isTrackingWallet) {
|
||||
this.startTrackingWallet(this.trackingWalletName);
|
||||
}
|
||||
this.stateService.connectionState$.next(2);
|
||||
}
|
||||
|
||||
|
@ -196,6 +201,18 @@ export class WebsocketService {
|
|||
this.isTrackingAddresses = false;
|
||||
}
|
||||
|
||||
startTrackingWallet(walletName: string) {
|
||||
this.websocketSubject.next({ 'track-wallet': walletName });
|
||||
this.isTrackingWallet = true;
|
||||
this.trackingWalletName = walletName;
|
||||
}
|
||||
|
||||
stopTrackingWallet() {
|
||||
this.websocketSubject.next({ 'track-wallet': 'stop' });
|
||||
this.isTrackingWallet = false;
|
||||
this.trackingWalletName = '';
|
||||
}
|
||||
|
||||
startTrackAsset(asset: string) {
|
||||
this.websocketSubject.next({ 'track-asset': asset });
|
||||
}
|
||||
|
@ -452,6 +469,10 @@ export class WebsocketService {
|
|||
}
|
||||
}
|
||||
|
||||
if (response['wallet-transactions']) {
|
||||
this.stateService.walletTransactions$.next(response['wallet-transactions']);
|
||||
}
|
||||
|
||||
if (response['accelerations']) {
|
||||
if (response['accelerations'].accelerations) {
|
||||
this.stateService.accelerations$.next({
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="col-md-12 branding mt-2">
|
||||
<div class="main-logo" [class]="{'services': isServicesPage}">
|
||||
@if (enterpriseInfo?.footer_img) {
|
||||
<img [src]="enterpriseInfo?.footer_img" alt="enterpriseInfo.title" height="60px" class="mr-3">
|
||||
<img [src]="enterpriseInfo?.footer_img" [alt]="enterpriseInfo.title" height="60px" class="mr-3">
|
||||
} @else {
|
||||
<app-svg-images *ngIf="officialMempoolSpace" name="officialMempoolSpace" viewBox="0 0 500 126"></app-svg-images>
|
||||
<app-svg-images *ngIf="!officialMempoolSpace" name="mempoolSpace" viewBox="0 0 500 126"></app-svg-images>
|
||||
|
|
|
@ -23,7 +23,7 @@ export class FiatCurrencyPipe implements PipeTransform {
|
|||
const digits = args[0] || 1;
|
||||
const currency = args[1] || this.currency || 'USD';
|
||||
|
||||
if (num >= 1000) {
|
||||
if (Math.abs(num) >= 1000) {
|
||||
return new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 0 }).format(num);
|
||||
} else {
|
||||
return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
|
||||
|
|
Loading…
Add table
Reference in a new issue