Merge branch 'master' into shrink-og-avatar
This commit is contained in:
@@ -157,3 +157,41 @@ export const specialBlocks = {
|
||||
labelEventCompleted: 'Block Subsidy has halved to 3.125 BTC per block',
|
||||
}
|
||||
};
|
||||
|
||||
export const fiatCurrencies = {
|
||||
AUD: {
|
||||
name: 'Australian Dollar',
|
||||
code: 'AUD',
|
||||
indexed: true,
|
||||
},
|
||||
CAD: {
|
||||
name: 'Canadian Dollar',
|
||||
code: 'CAD',
|
||||
indexed: true,
|
||||
},
|
||||
CHF: {
|
||||
name: 'Swiss Franc',
|
||||
code: 'CHF',
|
||||
indexed: true,
|
||||
},
|
||||
EUR: {
|
||||
name: 'Euro',
|
||||
code: 'EUR',
|
||||
indexed: true,
|
||||
},
|
||||
GBP: {
|
||||
name: 'Pound Sterling',
|
||||
code: 'GBP',
|
||||
indexed: true,
|
||||
},
|
||||
JPY: {
|
||||
name: 'Japanese Yen',
|
||||
code: 'JPY',
|
||||
indexed: true,
|
||||
},
|
||||
USD: {
|
||||
name: 'US Dollar',
|
||||
code: 'USD',
|
||||
indexed: true,
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,7 @@ import { StorageService } from './services/storage.service';
|
||||
import { HttpCacheInterceptor } from './services/http-cache.interceptor';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { FiatShortenerPipe } from './shared/pipes/fiat-shortener.pipe';
|
||||
import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe';
|
||||
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
|
||||
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
|
||||
import { AppPreloadingStrategy } from './app.preloading-strategy';
|
||||
@@ -34,6 +35,7 @@ const providers = [
|
||||
LanguageService,
|
||||
ShortenStringPipe,
|
||||
FiatShortenerPipe,
|
||||
FiatCurrencyPipe,
|
||||
CapAddressPipe,
|
||||
AppPreloadingStrategy,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<ng-container *ngIf="!noFiat && (viewFiat$ | async) && (conversions$ | async) as conversions; else viewFiatVin">
|
||||
<span class="fiat">{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ conversions.USD * (satoshis / 100000000) | currency:'USD':'symbol':'1.2-2' }}</span>
|
||||
<span class="fiat">{{ addPlus && satoshis >= 0 ? '+' : '' }}{{ (conversions ? conversions[currency] : 0) * satoshis / 100000000 | fiatCurrency : digitsInfo : currency }}</span>
|
||||
</ng-container>
|
||||
<ng-template #viewFiatVin>
|
||||
<ng-template [ngIf]="(network === 'liquid' || network === 'liquidtestnet') && (satoshis === undefined || satoshis === null)" [ngIfElse]="default">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, Input, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
|
||||
@@ -10,10 +10,12 @@ import { Observable, Subscription } from 'rxjs';
|
||||
})
|
||||
export class AmountComponent implements OnInit, OnDestroy {
|
||||
conversions$: Observable<any>;
|
||||
currency: string;
|
||||
viewFiat$: Observable<boolean>;
|
||||
network = '';
|
||||
|
||||
stateSubscription: Subscription;
|
||||
currencySubscription: Subscription;
|
||||
|
||||
@Input() satoshis: number;
|
||||
@Input() digitsInfo = '1.8-8';
|
||||
@@ -22,7 +24,13 @@ export class AmountComponent implements OnInit, OnDestroy {
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
private cd: ChangeDetectorRef,
|
||||
) {
|
||||
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.currency = fiat;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.viewFiat$ = this.stateService.viewFiat$.asObservable();
|
||||
@@ -34,6 +42,7 @@ export class AmountComponent implements OnInit, OnDestroy {
|
||||
if (this.stateSubscription) {
|
||||
this.stateSubscription.unsubscribe();
|
||||
}
|
||||
this.currencySubscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
|
||||
import { EChartsOption, graphic } from 'echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { MiningService } from '../../services/mining.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe';
|
||||
import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe';
|
||||
import { fiatCurrencies } from '../../app.constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block-fees-graph',
|
||||
@@ -44,6 +47,9 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
currencySubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private seoService: SeoService,
|
||||
@@ -51,11 +57,21 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private storageService: StorageService,
|
||||
private miningService: MiningService,
|
||||
private stateService: StateService,
|
||||
private route: ActivatedRoute,
|
||||
private fiatShortenerPipe: FiatShortenerPipe,
|
||||
private fiatCurrencyPipe: FiatCurrencyPipe,
|
||||
) {
|
||||
this.radioGroupForm = this.formBuilder.group({ dateSpan: '1y' });
|
||||
this.radioGroupForm.controls.dateSpan.setValue('1y');
|
||||
|
||||
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
if (fiat && fiatCurrencies[fiat]?.indexed) {
|
||||
this.currency = fiat;
|
||||
} else {
|
||||
this.currency = 'USD';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -84,7 +100,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
tap((response) => {
|
||||
this.prepareChartOptions({
|
||||
blockFees: response.body.map(val => [val.timestamp * 1000, val.avgFees / 100000000, val.avgHeight]),
|
||||
blockFeesUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val.USD, val.avgHeight]),
|
||||
blockFeesFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgFees / 100000000 * val[this.currency], val.avgHeight]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
@@ -157,7 +173,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
if (tick.seriesIndex === 0) {
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
|
||||
} else if (tick.seriesIndex === 1) {
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency) }<br>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +200,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
icon: 'roundRect',
|
||||
},
|
||||
{
|
||||
name: 'Fees USD',
|
||||
name: 'Fees ' + this.currency,
|
||||
inactiveColor: 'rgb(110, 112, 121)',
|
||||
textStyle: {
|
||||
color: 'white',
|
||||
@@ -216,7 +232,7 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
axisLabel: {
|
||||
color: 'rgb(110, 112, 121)',
|
||||
formatter: function(val) {
|
||||
return this.fiatShortenerPipe.transform(val);
|
||||
return this.fiatShortenerPipe.transform(val, null, this.currency);
|
||||
}.bind(this)
|
||||
},
|
||||
splitLine: {
|
||||
@@ -243,8 +259,8 @@ export class BlockFeesGraphComponent implements OnInit {
|
||||
legendHoverLink: false,
|
||||
zlevel: 1,
|
||||
yAxisIndex: 1,
|
||||
name: 'Fees USD',
|
||||
data: data.blockFeesUSD,
|
||||
name: 'Fees ' + this.currency,
|
||||
data: data.blockFeesFiat,
|
||||
type: 'line',
|
||||
smooth: 0.25,
|
||||
symbol: 'none',
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core';
|
||||
import { EChartsOption, graphic } from 'echarts';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map, share, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { formatCurrency, formatNumber, getCurrencySymbol } from '@angular/common';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { download, formatterXAxis, formatterXAxisLabel, formatterXAxisTimeCategory } from '../../shared/graphs.utils';
|
||||
import { MiningService } from '../../services/mining.service';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FiatShortenerPipe } from '../../shared/pipes/fiat-shortener.pipe';
|
||||
import { FiatCurrencyPipe } from '../../shared/pipes/fiat-currency.pipe';
|
||||
import { fiatCurrencies } from '../../app.constants';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block-rewards-graph',
|
||||
@@ -44,16 +47,28 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
timespan = '';
|
||||
chartInstance: any = undefined;
|
||||
|
||||
currencySubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private seoService: SeoService,
|
||||
private apiService: ApiService,
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private miningService: MiningService,
|
||||
private stateService: StateService,
|
||||
private storageService: StorageService,
|
||||
private route: ActivatedRoute,
|
||||
private fiatShortenerPipe: FiatShortenerPipe,
|
||||
private fiatCurrencyPipe: FiatCurrencyPipe,
|
||||
) {
|
||||
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
if (fiat && fiatCurrencies[fiat]?.indexed) {
|
||||
this.currency = fiat;
|
||||
} else {
|
||||
this.currency = 'USD';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -82,7 +97,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
tap((response) => {
|
||||
this.prepareChartOptions({
|
||||
blockRewards: response.body.map(val => [val.timestamp * 1000, val.avgRewards / 100000000, val.avgHeight]),
|
||||
blockRewardsUSD: response.body.filter(val => val.USD > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val.USD, val.avgHeight]),
|
||||
blockRewardsFiat: response.body.filter(val => val[this.currency] > 0).map(val => [val.timestamp * 1000, val.avgRewards / 100000000 * val[this.currency], val.avgHeight]),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}),
|
||||
@@ -157,7 +172,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
if (tick.seriesIndex === 0) {
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${formatNumber(tick.data[1], this.locale, '1.3-3')} BTC<br>`;
|
||||
} else if (tick.seriesIndex === 1) {
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${formatCurrency(tick.data[1], this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0')}<br>`;
|
||||
tooltip += `${tick.marker} ${tick.seriesName}: ${this.fiatCurrencyPipe.transform(tick.data[1], null, this.currency)}<br>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +199,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
icon: 'roundRect',
|
||||
},
|
||||
{
|
||||
name: 'Rewards USD',
|
||||
name: 'Rewards ' + this.currency,
|
||||
inactiveColor: 'rgb(110, 112, 121)',
|
||||
textStyle: {
|
||||
color: 'white',
|
||||
@@ -228,7 +243,7 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
axisLabel: {
|
||||
color: 'rgb(110, 112, 121)',
|
||||
formatter: function(val) {
|
||||
return this.fiatShortenerPipe.transform(val);
|
||||
return this.fiatShortenerPipe.transform(val, null, this.currency);
|
||||
}.bind(this)
|
||||
},
|
||||
splitLine: {
|
||||
@@ -251,8 +266,8 @@ export class BlockRewardsGraphComponent implements OnInit {
|
||||
legendHoverLink: false,
|
||||
zlevel: 1,
|
||||
yAxisIndex: 1,
|
||||
name: 'Rewards USD',
|
||||
data: data.blockRewardsUSD,
|
||||
name: 'Rewards ' + this.currency,
|
||||
data: data.blockRewardsFiat,
|
||||
type: 'line',
|
||||
smooth: 0.25,
|
||||
symbol: 'none',
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div [formGroup]="fiatForm" class="text-small text-center">
|
||||
<select formControlName="fiat" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 200px;" (change)="changeFiat()">
|
||||
<option *ngFor="let currency of currencies" [value]="currency[1].code">{{ currency[1].name + " (" + currency[1].code + ")" }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { fiatCurrencies } from '../../app.constants';
|
||||
import { StateService } from '../../services/state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-fiat-selector',
|
||||
templateUrl: './fiat-selector.component.html',
|
||||
styleUrls: ['./fiat-selector.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FiatSelectorComponent implements OnInit {
|
||||
fiatForm: UntypedFormGroup;
|
||||
currencies = Object.entries(fiatCurrencies).sort((a: any, b: any) => {
|
||||
if (a[1].name < b[1].name) {
|
||||
return -1;
|
||||
}
|
||||
if (a[1].name > b[1].name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private stateService: StateService,
|
||||
private storageService: StorageService,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.fiatForm = this.formBuilder.group({
|
||||
fiat: ['USD']
|
||||
});
|
||||
this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.fiatForm.get('fiat')?.setValue(fiat);
|
||||
});
|
||||
}
|
||||
|
||||
changeFiat() {
|
||||
const newFiat = this.fiatForm.get('fiat')?.value;
|
||||
this.storageService.setValue('fiat-preference', newFiat);
|
||||
this.stateService.fiatCurrency$.next(newFiat);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<div [formGroup]="languageForm" class="text-small text-center">
|
||||
<select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 130px;" (change)="changeLanguage()">
|
||||
<select formControlName="language" class="custom-select custom-select-sm form-control-secondary form-control mx-auto" style="width: 200px;" (change)="changeLanguage()">
|
||||
<option *ngFor="let lang of languages" [value]="lang.code">{{ lang.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -74,4 +74,24 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pref-selectors">
|
||||
<div class="selector">
|
||||
<app-language-selector></app-language-selector>
|
||||
</div>
|
||||
<div class="selector">
|
||||
<app-fiat-selector></app-fiat-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terms-of-service">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
|
||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
|
|
||||
<a [routerLink]="['/tx/push' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</a>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -103,4 +103,23 @@
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.terms-of-service {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.pref-selectors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
|
||||
.selector {
|
||||
margin-left: .5em;
|
||||
margin-bottom: .5em;
|
||||
&:first {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,10 +235,10 @@
|
||||
</span>
|
||||
</td>
|
||||
<td class="reward text-right">
|
||||
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2"></app-amount>
|
||||
<app-amount [satoshis]="block.extras.reward" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
|
||||
</td>
|
||||
<td class="fees text-right">
|
||||
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-2"></app-amount>
|
||||
<app-amount [satoshis]="block.extras.totalFees" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
|
||||
</td>
|
||||
<td class="txs text-right">
|
||||
{{ block.tx_count | number }}
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
<thead>
|
||||
<th class="table-cell-txid" i18n="dashboard.latest-transactions.txid">TXID</th>
|
||||
<th class="table-cell-satoshis" i18n="dashboard.latest-transactions.amount">Amount</th>
|
||||
<th class="table-cell-fiat" *ngIf="(network$ | async) === ''" i18n="dashboard.latest-transactions.USD">USD</th>
|
||||
<th class="table-cell-fiat" *ngIf="(network$ | async) === ''">{{ currency }}</th>
|
||||
<th class="table-cell-fees" i18n="transaction.fee|Transaction fee">Fee</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -144,7 +144,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-language-selector></app-language-selector>
|
||||
<div class="pref-selectors">
|
||||
<div class="selector">
|
||||
<app-language-selector></app-language-selector>
|
||||
</div>
|
||||
<div class="selector">
|
||||
<app-fiat-selector></app-fiat-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terms-of-service">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
||||
@@ -323,4 +323,19 @@
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.pref-selectors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
|
||||
.selector {
|
||||
margin-left: .5em;
|
||||
margin-bottom: .5em;
|
||||
&:first {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, Inject, LOCALE_ID, OnInit } from '@angular/core';
|
||||
import { combineLatest, merge, Observable, of } from 'rxjs';
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { combineLatest, merge, Observable, of, Subscription } from 'rxjs';
|
||||
import { filter, map, scan, share, switchMap, tap } from 'rxjs/operators';
|
||||
import { BlockExtended, OptimizedMempoolStats } from '../interfaces/node-api.interface';
|
||||
import { MempoolInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
@@ -7,7 +7,6 @@ import { ApiService } from '../services/api.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
import { WebsocketService } from '../services/websocket.service';
|
||||
import { SeoService } from '../services/seo.service';
|
||||
import { StorageService } from '../services/storage.service';
|
||||
|
||||
interface MempoolBlocksData {
|
||||
blocks: number;
|
||||
@@ -32,7 +31,7 @@ interface MempoolStatsData {
|
||||
styleUrls: ['./dashboard.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
export class DashboardComponent implements OnInit, OnDestroy {
|
||||
featuredAssets$: Observable<any>;
|
||||
network$: Observable<string>;
|
||||
mempoolBlocksData$: Observable<MempoolBlocksData>;
|
||||
@@ -47,16 +46,20 @@ export class DashboardComponent implements OnInit {
|
||||
transactionsWeightPerSecondOptions: any;
|
||||
isLoadingWebSocket$: Observable<boolean>;
|
||||
liquidPegsMonth$: Observable<any>;
|
||||
currencySubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) private locale: string,
|
||||
public stateService: StateService,
|
||||
private apiService: ApiService,
|
||||
private websocketService: WebsocketService,
|
||||
private seoService: SeoService,
|
||||
private storageService: StorageService,
|
||||
private seoService: SeoService
|
||||
) { }
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.currencySubscription.unsubscribe();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.isLoadingWebSocket$ = this.stateService.isLoadingWebSocket$;
|
||||
this.seoService.resetTitle();
|
||||
@@ -213,6 +216,10 @@ export class DashboardComponent implements OnInit {
|
||||
share(),
|
||||
);
|
||||
}
|
||||
|
||||
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.currency = fiat;
|
||||
});
|
||||
}
|
||||
|
||||
handleNewMempoolData(mempoolStats: OptimizedMempoolStats[]) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
<span class="green-color">{{ (conversions$ | async)?.USD * value / 100000000 | currency:'USD':'symbol':digitsInfo }}</span>
|
||||
<span class="green-color" *ngIf="(conversions$ | async) as conversions">{{ (conversions ? conversions[currency] : 0) * value / 100000000 | fiatCurrency : digitsInfo : currency }}</span>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { StateService } from '../services/state.service';
|
||||
|
||||
@Component({
|
||||
@@ -8,18 +8,30 @@ import { StateService } from '../services/state.service';
|
||||
styleUrls: ['./fiat.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FiatComponent implements OnInit {
|
||||
export class FiatComponent implements OnInit, OnDestroy {
|
||||
conversions$: Observable<any>;
|
||||
currencySubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
@Input() value: number;
|
||||
@Input() digitsInfo = '1.2-2';
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
) { }
|
||||
private cd: ChangeDetectorRef,
|
||||
) {
|
||||
this.currencySubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.currency = fiat;
|
||||
this.cd.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.conversions$ = this.stateService.conversions$.asObservable();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.currencySubscription.unsubscribe();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</div>
|
||||
<div class="box-right">
|
||||
<div class="second-line"><ng-container *ngTemplateOutlet="xChannels; context: {$implicit: channel.channels }"></ng-container></div>
|
||||
<div class="second-line"><app-amount [satoshis]="channel.capacity" digitsInfo="1.2-2"></app-amount></div>
|
||||
<div class="second-line"><app-amount [satoshis]="channel.capacity" digitsInfo="1.2-2" [noFiat]="true"></app-amount></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<span i18n="shared.sats">sats</span>
|
||||
</div>
|
||||
<span class="fiat" *ngIf="statistics.previous">
|
||||
<app-change [current]="statistics.latest?.avg_capacity" [previous]="statistics.previous?.avg_capacity"></app-change>
|
||||
<app-fiat [value]="statistics.latest?.avg_capacity" digitsInfo="1.0-0" ></app-fiat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,7 +63,7 @@
|
||||
<span i18n="shared.sats">sats</span>
|
||||
</div>
|
||||
<span class="fiat" *ngIf="statistics.previous">
|
||||
<app-change [current]="statistics.latest?.med_capacity" [previous]="statistics.previous?.med_capacity"></app-change>
|
||||
<app-fiat [value]="statistics.latest?.med_capacity" digitsInfo="1.0-0" ></app-fiat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -84,8 +84,22 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="text-small text-center mt-1" *ngIf="officialMempoolSpace">
|
||||
<a [routerLink]="['/lightning/group/the-mempool-open-source-project' | relativeUrl]">Connect to our nodes</a>
|
||||
<div class="pref-selectors">
|
||||
<div class="selector">
|
||||
<app-language-selector></app-language-selector>
|
||||
</div>
|
||||
<div class="selector">
|
||||
<app-fiat-selector></app-fiat-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="terms-of-service">
|
||||
<a [routerLink]="['/terms-of-service']" i18n="shared.terms-of-service|Terms of Service">Terms of Service</a>
|
||||
|
|
||||
<a [routerLink]="['/privacy-policy']" i18n="shared.privacy-policy|Privacy Policy">Privacy Policy</a>
|
||||
|
|
||||
<a *ngIf="officialMempoolSpace" [routerLink]="['/lightning/group/the-mempool-open-source-project' | relativeUrl]">Connect to our nodes</a>
|
||||
<a *ngIf="!officialMempoolSpace" [routerLink]="['/tx/push' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Broadcast Transaction</a>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -103,4 +103,23 @@
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.terms-of-service {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.pref-selectors {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
|
||||
.selector {
|
||||
margin-left: .5em;
|
||||
margin-bottom: .5em;
|
||||
&:first {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@
|
||||
<div class="card-text" i18n-ngbTooltip="mining.percentage-change-last-week" ngbTooltip="Percentage change past week"
|
||||
[disableTooltip]="!statistics.previous" placement="bottom">
|
||||
<div class="fee-text" [class]="!statistics.previous ? 'no-border' : ''">
|
||||
<app-amount [satoshis]="statistics.latest?.total_capacity" digitsInfo="1.2-2"></app-amount>
|
||||
<app-amount [satoshis]="statistics.latest?.total_capacity" digitsInfo="1.2-2" [noFiat]="true"></app-amount>
|
||||
</div>
|
||||
<span class="fiat" *ngIf="statistics.previous">
|
||||
<app-change [current]="statistics.latest?.total_capacity" [previous]="statistics.previous?.total_capacity">
|
||||
</app-change>
|
||||
<app-fiat [value]="statistics.latest?.total_capacity" digitsInfo="1.0-0" ></app-fiat>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -121,6 +121,7 @@ export class StateService {
|
||||
timeLtr: BehaviorSubject<boolean>;
|
||||
hideFlow: BehaviorSubject<boolean>;
|
||||
hideAudit: BehaviorSubject<boolean>;
|
||||
fiatCurrency$: BehaviorSubject<string>;
|
||||
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: any,
|
||||
@@ -186,6 +187,9 @@ export class StateService {
|
||||
this.hideAudit.subscribe((hide) => {
|
||||
this.storageService.setValue('audit-preference', hide ? 'hide' : 'show');
|
||||
});
|
||||
|
||||
const fiatPreference = this.storageService.getValue('fiat-preference');
|
||||
this.fiatCurrency$ = new BehaviorSubject<string>(fiatPreference || 'USD');
|
||||
}
|
||||
|
||||
setNetworkBasedonUrl(url: string) {
|
||||
|
||||
28
frontend/src/app/shared/pipes/fiat-currency.pipe.ts
Normal file
28
frontend/src/app/shared/pipes/fiat-currency.pipe.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { formatCurrency, getCurrencySymbol } from '@angular/common';
|
||||
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'fiatCurrency'
|
||||
})
|
||||
export class FiatCurrencyPipe implements PipeTransform {
|
||||
fiatSubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.fiatSubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.currency = fiat;
|
||||
});
|
||||
}
|
||||
|
||||
transform(num: number, ...args: any[]): unknown {
|
||||
const digits = args[0] || 1;
|
||||
const currency = args[1] || this.currency || 'USD';
|
||||
|
||||
return new Intl.NumberFormat(this.locale, { style: 'currency', currency }).format(num);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,30 @@
|
||||
import { formatCurrency, getCurrencySymbol } from '@angular/common';
|
||||
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'fiatShortener'
|
||||
})
|
||||
export class FiatShortenerPipe implements PipeTransform {
|
||||
fiatSubscription: Subscription;
|
||||
currency: string;
|
||||
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string
|
||||
) {}
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.fiatSubscription = this.stateService.fiatCurrency$.subscribe((fiat) => {
|
||||
this.currency = fiat;
|
||||
});
|
||||
}
|
||||
|
||||
transform(num: number, ...args: any[]): unknown {
|
||||
const digits = args[0] || 1;
|
||||
const unit = args[1] || undefined;
|
||||
const currency = args[1] || this.currency || 'USD';
|
||||
|
||||
if (num < 1000) {
|
||||
return num.toFixed(digits);
|
||||
return new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 1 }).format(num);
|
||||
}
|
||||
|
||||
const lookup = [
|
||||
@@ -30,8 +40,8 @@ export class FiatShortenerPipe implements PipeTransform {
|
||||
const item = lookup.slice().reverse().find((item) => num >= item.value);
|
||||
|
||||
let result = item ? (num / item.value).toFixed(digits).replace(rx, '$1') : '0';
|
||||
result = formatCurrency(parseInt(result, 10), this.locale, getCurrencySymbol('USD', 'narrow'), 'USD', '1.0-0');
|
||||
|
||||
result = new Intl.NumberFormat(this.locale, { style: 'currency', currency, maximumFractionDigits: 0 }).format(item ? num / item.value : 0);
|
||||
|
||||
return result + item.symbol;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { RelativeUrlPipe } from './pipes/relative-url/relative-url.pipe';
|
||||
import { ScriptpubkeyTypePipe } from './pipes/scriptpubkey-type-pipe/scriptpubkey-type.pipe';
|
||||
import { BytesPipe } from './pipes/bytes-pipe/bytes.pipe';
|
||||
import { WuBytesPipe } from './pipes/bytes-pipe/wubytes.pipe';
|
||||
import { FiatCurrencyPipe } from './pipes/fiat-currency.pipe';
|
||||
import { BlockchainComponent } from '../components/blockchain/blockchain.component';
|
||||
import { TimeSinceComponent } from '../components/time-since/time-since.component';
|
||||
import { TimeUntilComponent } from '../components/time-until/time-until.component';
|
||||
@@ -34,6 +35,7 @@ import { TxFeaturesComponent } from '../components/tx-features/tx-features.compo
|
||||
import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { LanguageSelectorComponent } from '../components/language-selector/language-selector.component';
|
||||
import { FiatSelectorComponent } from '../components/fiat-selector/fiat-selector.component';
|
||||
import { ColoredPriceDirective } from './directives/colored-price.directive';
|
||||
import { NoSanitizePipe } from './pipes/no-sanitize.pipe';
|
||||
import { MempoolBlocksComponent } from '../components/mempool-blocks/mempool-blocks.component';
|
||||
@@ -93,6 +95,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
||||
TxFeaturesComponent,
|
||||
TxFeeRatingComponent,
|
||||
LanguageSelectorComponent,
|
||||
FiatSelectorComponent,
|
||||
ScriptpubkeyTypePipe,
|
||||
RelativeUrlPipe,
|
||||
NoSanitizePipe,
|
||||
@@ -107,6 +110,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
||||
CapAddressPipe,
|
||||
Decimal2HexPipe,
|
||||
FeeRoundingPipe,
|
||||
FiatCurrencyPipe,
|
||||
ColoredPriceDirective,
|
||||
BlockchainComponent,
|
||||
MempoolBlocksComponent,
|
||||
@@ -199,6 +203,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
||||
TxFeaturesComponent,
|
||||
TxFeeRatingComponent,
|
||||
LanguageSelectorComponent,
|
||||
FiatSelectorComponent,
|
||||
ScriptpubkeyTypePipe,
|
||||
RelativeUrlPipe,
|
||||
Hex2asciiPipe,
|
||||
@@ -207,6 +212,7 @@ import { GeolocationComponent } from '../shared/components/geolocation/geolocati
|
||||
BytesPipe,
|
||||
VbytesPipe,
|
||||
WuBytesPipe,
|
||||
FiatCurrencyPipe,
|
||||
CeilPipe,
|
||||
ShortenStringPipe,
|
||||
CapAddressPipe,
|
||||
|
||||
Reference in New Issue
Block a user