2024-06-12 20:01:48 +02:00
|
|
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, NgZone, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
2024-12-09 08:54:26 +01:00
|
|
|
import { echarts, EChartsOption } from '@app/graphs/echarts';
|
2024-04-29 20:54:56 +00:00
|
|
|
import { BehaviorSubject, Observable, Subscription, combineLatest, of } from 'rxjs';
|
2024-06-12 16:57:19 +02:00
|
|
|
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
2024-12-09 08:54:26 +01:00
|
|
|
import { AddressTxSummary, ChainStats } from '@interfaces/electrs.interface';
|
|
|
|
import { ElectrsApiService } from '@app/services/electrs-api.service';
|
|
|
|
import { AmountShortenerPipe } from '@app/shared/pipes/amount-shortener.pipe';
|
2024-03-21 09:02:17 +00:00
|
|
|
import { Router } from '@angular/router';
|
2024-12-09 08:54:26 +01:00
|
|
|
import { RelativeUrlPipe } from '@app/shared/pipes/relative-url/relative-url.pipe';
|
|
|
|
import { StateService } from '@app/services/state.service';
|
|
|
|
import { PriceService } from '@app/services/price.service';
|
|
|
|
import { FiatCurrencyPipe } from '@app/shared/pipes/fiat-currency.pipe';
|
2024-03-18 08:19:56 +00:00
|
|
|
|
2024-04-26 17:53:44 +00:00
|
|
|
const periodSeconds = {
|
|
|
|
'1d': (60 * 60 * 24),
|
|
|
|
'3d': (60 * 60 * 24 * 3),
|
|
|
|
'1w': (60 * 60 * 24 * 7),
|
|
|
|
'1m': (60 * 60 * 24 * 30),
|
|
|
|
'6m': (60 * 60 * 24 * 180),
|
|
|
|
'1y': (60 * 60 * 24 * 365),
|
|
|
|
};
|
|
|
|
|
2024-03-18 08:19:56 +00:00
|
|
|
@Component({
|
|
|
|
selector: 'app-address-graph',
|
|
|
|
templateUrl: './address-graph.component.html',
|
|
|
|
styleUrls: ['./address-graph.component.scss'],
|
|
|
|
styles: [`
|
|
|
|
.loadingGraphs {
|
|
|
|
position: absolute;
|
|
|
|
top: 50%;
|
|
|
|
left: calc(50% - 15px);
|
2024-12-09 08:54:26 +01:00
|
|
|
z-index: 99;
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|
|
|
|
`],
|
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
|
})
|
2024-04-29 20:54:56 +00:00
|
|
|
export class AddressGraphComponent implements OnChanges, OnDestroy {
|
2024-03-18 08:19:56 +00:00
|
|
|
@Input() address: string;
|
2024-03-20 10:02:47 +00:00
|
|
|
@Input() isPubkey: boolean = false;
|
2024-03-18 08:19:56 +00:00
|
|
|
@Input() stats: ChainStats;
|
2024-04-26 19:16:09 +00:00
|
|
|
@Input() addressSummary$: Observable<AddressTxSummary[]> | null;
|
2024-04-26 17:53:44 +00:00
|
|
|
@Input() period: '1d' | '3d' | '1w' | '1m' | '6m' | '1y' | 'all' = 'all';
|
2024-04-25 20:20:41 +00:00
|
|
|
@Input() height: number = 200;
|
2024-03-18 08:19:56 +00:00
|
|
|
@Input() right: number | string = 10;
|
|
|
|
@Input() left: number | string = 70;
|
2024-04-25 20:20:41 +00:00
|
|
|
@Input() widget: boolean = false;
|
2024-12-09 08:54:26 +01:00
|
|
|
@Input() defaultFiat: boolean = false;
|
2024-03-18 08:19:56 +00:00
|
|
|
|
2024-03-21 09:02:17 +00:00
|
|
|
data: any[] = [];
|
2024-06-12 16:57:19 +02:00
|
|
|
fiatData: any[] = [];
|
2024-03-21 09:02:17 +00:00
|
|
|
hoverData: any[] = [];
|
2024-06-12 11:47:57 +02:00
|
|
|
conversions: any;
|
2024-06-12 12:57:13 +02:00
|
|
|
allowZoom: boolean = false;
|
2024-06-13 15:03:22 +02:00
|
|
|
initialRight = this.right;
|
|
|
|
initialLeft = this.left;
|
|
|
|
selected = { [$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`]: true, 'Fiat': false };
|
2024-03-21 09:02:17 +00:00
|
|
|
|
2024-04-29 20:54:56 +00:00
|
|
|
subscription: Subscription;
|
|
|
|
redraw$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
|
|
|
|
2024-03-18 08:19:56 +00:00
|
|
|
chartOptions: EChartsOption = {};
|
|
|
|
chartInitOptions = {
|
|
|
|
renderer: 'svg',
|
|
|
|
};
|
|
|
|
|
|
|
|
error: any;
|
|
|
|
isLoading = true;
|
|
|
|
chartInstance: any = undefined;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
@Inject(LOCALE_ID) public locale: string,
|
2024-04-25 20:20:41 +00:00
|
|
|
public stateService: StateService,
|
2024-03-18 08:19:56 +00:00
|
|
|
private electrsApiService: ElectrsApiService,
|
2024-03-21 09:02:17 +00:00
|
|
|
private router: Router,
|
2024-03-18 08:19:56 +00:00
|
|
|
private amountShortenerPipe: AmountShortenerPipe,
|
|
|
|
private cd: ChangeDetectorRef,
|
2024-04-07 21:04:23 +08:00
|
|
|
private relativeUrlPipe: RelativeUrlPipe,
|
2024-06-12 11:47:57 +02:00
|
|
|
private priceService: PriceService,
|
|
|
|
private fiatCurrencyPipe: FiatCurrencyPipe,
|
2024-06-12 20:01:48 +02:00
|
|
|
private zone: NgZone,
|
2024-03-21 09:02:17 +00:00
|
|
|
) {}
|
2024-03-18 08:19:56 +00:00
|
|
|
|
|
|
|
ngOnChanges(changes: SimpleChanges): void {
|
|
|
|
this.isLoading = true;
|
2024-07-25 22:34:52 +00:00
|
|
|
if (!this.addressSummary$ && (!this.address || !this.stats)) {
|
2024-04-25 20:20:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-12-09 08:54:26 +01:00
|
|
|
if (changes.defaultFiat) {
|
|
|
|
this.selected['Fiat'] = !!this.defaultFiat;
|
|
|
|
}
|
2024-05-08 00:17:35 +00:00
|
|
|
if (changes.address || changes.isPubkey || changes.addressSummary$ || changes.stats) {
|
2024-04-29 20:54:56 +00:00
|
|
|
if (this.subscription) {
|
|
|
|
this.subscription.unsubscribe();
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|
2024-04-29 20:54:56 +00:00
|
|
|
this.subscription = combineLatest([
|
|
|
|
this.redraw$,
|
|
|
|
(this.addressSummary$ || (this.isPubkey
|
|
|
|
? this.electrsApiService.getScriptHashSummary$((this.address.length === 66 ? '21' : '41') + this.address + 'ac')
|
|
|
|
: this.electrsApiService.getAddressSummary$(this.address)).pipe(
|
|
|
|
catchError(e => {
|
|
|
|
this.error = `Failed to fetch address balance history: ${e?.status || ''} ${e?.statusText || 'unknown error'}`;
|
|
|
|
return of(null);
|
|
|
|
}),
|
2024-06-12 11:47:57 +02:00
|
|
|
)),
|
|
|
|
this.stateService.conversions$
|
2024-06-12 16:57:19 +02:00
|
|
|
]).pipe(
|
|
|
|
switchMap(([redraw, addressSummary, conversions]) => {
|
2024-06-13 15:03:22 +02:00
|
|
|
this.conversions = conversions;
|
2024-06-12 16:57:19 +02:00
|
|
|
if (addressSummary) {
|
2024-06-13 15:03:22 +02:00
|
|
|
let extendedSummary = this.extendSummary(addressSummary);
|
|
|
|
return this.priceService.getPriceByBulk$(extendedSummary.map(d => d.time), 'USD').pipe(
|
2024-06-12 16:57:19 +02:00
|
|
|
tap((prices) => {
|
2024-06-13 15:03:22 +02:00
|
|
|
if (prices.length !== extendedSummary.length) {
|
|
|
|
extendedSummary = extendedSummary.map(item => ({ ...item, price: 0 }));
|
2024-06-12 16:57:19 +02:00
|
|
|
} else {
|
2024-06-13 15:03:22 +02:00
|
|
|
extendedSummary = extendedSummary.map((item, index) => {
|
2024-06-12 16:57:19 +02:00
|
|
|
let price = 0;
|
|
|
|
if (prices[index].price) {
|
|
|
|
price = prices[index].price['USD'];
|
|
|
|
} else if (this.conversions && this.conversions['USD']) {
|
|
|
|
price = this.conversions['USD'];
|
|
|
|
}
|
|
|
|
return { ...item, price: price }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}),
|
2024-06-13 15:03:22 +02:00
|
|
|
map(() => [redraw, extendedSummary, conversions])
|
2024-06-12 16:57:19 +02:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
return of([redraw, addressSummary, conversions]);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
).subscribe(([redraw, addressSummary, conversions]) => {
|
2024-04-29 20:54:56 +00:00
|
|
|
if (addressSummary) {
|
|
|
|
this.error = null;
|
2024-06-12 12:57:13 +02:00
|
|
|
this.allowZoom = addressSummary.length > 100 && !this.widget;
|
2024-04-29 20:54:56 +00:00
|
|
|
this.prepareChartOptions(addressSummary);
|
|
|
|
}
|
|
|
|
this.isLoading = false;
|
|
|
|
this.cd.markForCheck();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// re-trigger subscription
|
|
|
|
this.redraw$.next(true);
|
|
|
|
}
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|
|
|
|
|
2024-06-12 11:47:57 +02:00
|
|
|
prepareChartOptions(summary: AddressTxSummary[]) {
|
2024-07-25 22:34:52 +00:00
|
|
|
if (!summary) {
|
2024-04-29 20:54:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-12-09 08:54:26 +01:00
|
|
|
|
2024-07-25 22:34:52 +00:00
|
|
|
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;
|
2024-06-12 16:57:19 +02:00
|
|
|
const processData = summary.map(d => {
|
2024-07-25 22:34:52 +00:00
|
|
|
const balance = runningTotal;
|
|
|
|
const fiatBalance = runningTotal * d.price / 100_000_000;
|
|
|
|
runningTotal -= d.value;
|
2024-06-12 16:57:19 +02:00
|
|
|
return {
|
|
|
|
time: d.time * 1000,
|
|
|
|
balance,
|
|
|
|
fiatBalance,
|
|
|
|
d
|
|
|
|
};
|
2024-03-18 08:19:56 +00:00
|
|
|
}).reverse();
|
2024-12-09 08:54:26 +01:00
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
this.data = processData.filter(({ d }) => d.txid !== undefined).map(({ time, balance, d }) => [time, balance, d]);
|
|
|
|
this.fiatData = processData.map(({ time, fiatBalance, balance, d }) => [time, fiatBalance, d, balance]);
|
2024-03-18 08:19:56 +00:00
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
const now = Date.now();
|
2024-04-26 17:53:44 +00:00
|
|
|
if (this.period !== 'all') {
|
|
|
|
const start = now - (periodSeconds[this.period] * 1000);
|
|
|
|
this.data = this.data.filter(d => d[0] >= start);
|
2024-06-22 15:01:42 +09:00
|
|
|
const startFiat = this.data[0]?.[0] ?? start; // Make sure USD data starts at the same time as BTC data
|
|
|
|
this.fiatData = this.fiatData.filter(d => d[0] >= startFiat);
|
2024-04-26 17:53:44 +00:00
|
|
|
}
|
2024-06-13 15:03:22 +02:00
|
|
|
this.data.push(
|
2024-07-25 22:34:52 +00:00
|
|
|
{value: [now, total], symbol: 'none', tooltip: { show: false }}
|
2024-06-13 15:03:22 +02:00
|
|
|
);
|
2024-04-26 17:53:44 +00:00
|
|
|
|
2024-04-29 20:54:56 +00:00
|
|
|
const maxValue = this.data.reduce((acc, d) => Math.max(acc, Math.abs(d[1] ?? d.value[1])), 0);
|
|
|
|
const minValue = this.data.reduce((acc, d) => Math.min(acc, Math.abs(d[1] ?? d.value[1])), maxValue);
|
2024-03-18 08:19:56 +00:00
|
|
|
|
2024-12-09 08:54:26 +01:00
|
|
|
this.right = this.selected['Fiat'] ? +this.initialRight + 40 : this.initialRight;
|
|
|
|
this.left = this.selected[$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`] ? this.initialLeft : +this.initialLeft - 40;
|
|
|
|
|
2024-03-18 08:19:56 +00:00
|
|
|
this.chartOptions = {
|
|
|
|
color: [
|
|
|
|
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
{ offset: 0, color: '#FDD835' },
|
|
|
|
{ offset: 1, color: '#FB8C00' },
|
|
|
|
]),
|
2024-06-12 11:47:57 +02:00
|
|
|
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
{ offset: 0, color: '#4CAF50' },
|
|
|
|
{ offset: 1, color: '#1B5E20' },
|
|
|
|
]),
|
2024-03-18 08:19:56 +00:00
|
|
|
],
|
|
|
|
animation: false,
|
|
|
|
grid: {
|
|
|
|
top: 20,
|
2024-06-12 12:57:13 +02:00
|
|
|
bottom: this.allowZoom ? 65 : 20,
|
2024-03-18 08:19:56 +00:00
|
|
|
right: this.right,
|
|
|
|
left: this.left,
|
|
|
|
},
|
2024-06-23 18:59:50 +09:00
|
|
|
legend: !this.stateService.isAnyTestnet() ? {
|
2024-06-12 11:47:57 +02:00
|
|
|
data: [
|
|
|
|
{
|
|
|
|
name: $localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`,
|
|
|
|
inactiveColor: 'var(--grey)',
|
|
|
|
textStyle: {
|
|
|
|
color: 'white',
|
|
|
|
},
|
|
|
|
icon: 'roundRect',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Fiat',
|
|
|
|
inactiveColor: 'var(--grey)',
|
|
|
|
textStyle: {
|
|
|
|
color: 'white',
|
|
|
|
},
|
|
|
|
icon: 'roundRect',
|
|
|
|
}
|
|
|
|
],
|
2024-06-13 15:03:22 +02:00
|
|
|
selected: this.selected,
|
2024-06-12 11:47:57 +02:00
|
|
|
formatter: function (name) {
|
|
|
|
return name === 'Fiat' ? 'USD' : 'BTC';
|
|
|
|
}
|
2024-06-23 18:59:50 +09:00
|
|
|
} : undefined,
|
2024-03-18 08:19:56 +00:00
|
|
|
tooltip: {
|
|
|
|
show: !this.isMobile(),
|
|
|
|
trigger: 'axis',
|
|
|
|
axisPointer: {
|
|
|
|
type: 'line'
|
|
|
|
},
|
|
|
|
backgroundColor: 'rgba(17, 19, 31, 1)',
|
|
|
|
borderRadius: 4,
|
|
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
|
|
textStyle: {
|
|
|
|
color: '#b1b1b1',
|
|
|
|
align: 'left',
|
|
|
|
},
|
|
|
|
borderColor: '#000',
|
2024-06-13 15:03:22 +02:00
|
|
|
formatter: function (data) {
|
|
|
|
const btcData = data.filter(d => d.seriesName !== 'Fiat');
|
|
|
|
const fiatData = data.filter(d => d.seriesName === 'Fiat');
|
|
|
|
data = btcData.length ? btcData : fiatData;
|
|
|
|
if ((!btcData.length || !btcData[0]?.data?.[2]?.txid) && !fiatData.length) {
|
2024-04-26 17:53:44 +00:00
|
|
|
return '';
|
|
|
|
}
|
2024-06-13 15:03:22 +02:00
|
|
|
let tooltip = '<div>';
|
|
|
|
|
|
|
|
const hasTx = data[0].data[2].txid;
|
2024-12-09 08:54:26 +01:00
|
|
|
const date = new Date(data[0].data[0]).toLocaleTimeString(this.locale, { year: 'numeric', month: 'short', day: 'numeric' });
|
|
|
|
|
|
|
|
tooltip += `<div>
|
|
|
|
<div style="text-align: right;">
|
|
|
|
<div><b>${date}</b></div>`;
|
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
if (hasTx) {
|
|
|
|
const header = data.length === 1
|
2024-03-18 08:19:56 +00:00
|
|
|
? `${data[0].data[2].txid.slice(0, 6)}...${data[0].data[2].txid.slice(-6)}`
|
|
|
|
: `${data.length} transactions`;
|
2024-12-09 08:54:26 +01:00
|
|
|
tooltip += `<div><b>${header}</b></div>`;
|
2024-06-13 15:03:22 +02:00
|
|
|
}
|
2024-12-09 08:54:26 +01:00
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
const formatBTC = (val, decimal) => (val / 100_000_000).toFixed(decimal);
|
|
|
|
const formatFiat = (val) => this.fiatCurrencyPipe.transform(val, null, 'USD');
|
2024-12-09 08:54:26 +01:00
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
const btcVal = btcData.reduce((total, d) => total + d.data[2].value, 0);
|
|
|
|
const fiatVal = fiatData.reduce((total, d) => total + d.data[2].value * d.data[2].price / 100_000_000, 0);
|
|
|
|
const btcColor = btcVal === 0 ? '' : (btcVal > 0 ? 'var(--green)' : 'var(--red)');
|
|
|
|
const fiatColor = fiatVal === 0 ? '' : (fiatVal > 0 ? 'var(--green)' : 'var(--red)');
|
|
|
|
const btcSymbol = btcVal > 0 ? '+' : '';
|
|
|
|
const fiatSymbol = fiatVal > 0 ? '+' : '';
|
|
|
|
|
|
|
|
if (btcData.length && fiatData.length) {
|
|
|
|
tooltip += `<div style="display: flex; justify-content: space-between; color: ${btcColor}">
|
|
|
|
<span style="text-align: left; margin-right: 10px;">${btcSymbol} ${formatBTC(btcVal, 4)} BTC</span>
|
|
|
|
<span style="text-align: right;">${fiatSymbol} ${formatFiat(fiatVal)}</span>
|
|
|
|
</div>
|
|
|
|
<div style="display: flex; justify-content: space-between;">
|
|
|
|
<span style="text-align: left; margin-right: 10px;">${formatBTC(btcData[0].data[1], 4)} BTC</span>
|
|
|
|
<span style="text-align: right;">${formatFiat(fiatData[0].data[1])}</span>
|
|
|
|
</div>`;
|
|
|
|
} else if (btcData.length) {
|
|
|
|
tooltip += `<span style="color: ${btcColor}">${btcSymbol} ${formatBTC(btcVal, 8)} BTC</span><br>
|
|
|
|
<span>${formatBTC(data[0].data[1], 8)} BTC</span>`;
|
2024-06-12 11:47:57 +02:00
|
|
|
} else {
|
2024-06-13 15:03:22 +02:00
|
|
|
if (this.selected[$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`]) {
|
|
|
|
tooltip += `<div style="display: flex; justify-content: space-between;">
|
|
|
|
<span style="text-align: left; margin-right: 10px;">${formatBTC(data[0].data[3], 4)} BTC</span>
|
|
|
|
<span style="text-align: right;">${formatFiat(data[0].data[1])}</span>
|
|
|
|
</div>`;
|
|
|
|
} else {
|
|
|
|
tooltip += `${hasTx ? `<span style="color: ${fiatColor}">${fiatSymbol} ${formatFiat(fiatVal)}</span><br>` : ''}
|
|
|
|
<span>${formatFiat(data[0].data[1])}</span>`;
|
|
|
|
}
|
2024-06-12 11:47:57 +02:00
|
|
|
}
|
2024-06-13 15:03:22 +02:00
|
|
|
|
2024-12-09 08:54:26 +01:00
|
|
|
tooltip += `</div></div>`;
|
2024-06-13 15:03:22 +02:00
|
|
|
return tooltip;
|
2024-03-18 08:19:56 +00:00
|
|
|
}.bind(this)
|
|
|
|
},
|
|
|
|
xAxis: {
|
|
|
|
type: 'time',
|
|
|
|
splitNumber: this.isMobile() ? 5 : 10,
|
|
|
|
axisLabel: {
|
|
|
|
hideOverlap: true,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
yAxis: [
|
|
|
|
{
|
|
|
|
type: 'value',
|
|
|
|
position: 'left',
|
|
|
|
axisLabel: {
|
|
|
|
color: 'rgb(110, 112, 121)',
|
|
|
|
formatter: (val): string => {
|
2024-04-26 17:53:44 +00:00
|
|
|
let valSpan = maxValue - (this.period === 'all' ? 0 : minValue);
|
|
|
|
if (valSpan > 100_000_000_000) {
|
2024-12-09 08:54:26 +01:00
|
|
|
return `${this.amountShortenerPipe.transform(Math.round(val / 100_000_000), 0, undefined, true)} BTC`;
|
2024-04-26 17:53:44 +00:00
|
|
|
}
|
|
|
|
else if (valSpan > 1_000_000_000) {
|
2024-12-09 08:54:26 +01:00
|
|
|
return `${this.amountShortenerPipe.transform(Math.round(val / 100_000_000), 2, undefined, true)} BTC`;
|
2024-04-26 17:53:44 +00:00
|
|
|
} else if (valSpan > 100_000_000) {
|
2024-03-21 02:49:31 +00:00
|
|
|
return `${(val / 100_000_000).toFixed(1)} BTC`;
|
2024-04-26 17:53:44 +00:00
|
|
|
} else if (valSpan > 10_000_000) {
|
2024-03-18 08:19:56 +00:00
|
|
|
return `${(val / 100_000_000).toFixed(2)} BTC`;
|
2024-04-26 17:53:44 +00:00
|
|
|
} else if (valSpan > 1_000_000) {
|
2024-12-09 08:54:26 +01:00
|
|
|
if (maxValue > 100_000_000_000) {
|
|
|
|
return `${this.amountShortenerPipe.transform(Math.round(val / 100_000_000), 3, undefined, true)} BTC`;
|
|
|
|
}
|
2024-03-21 02:49:31 +00:00
|
|
|
return `${(val / 100_000_000).toFixed(3)} BTC`;
|
2024-03-18 08:19:56 +00:00
|
|
|
} else {
|
2024-12-09 08:54:26 +01:00
|
|
|
return `${this.amountShortenerPipe.transform(val, 0, undefined, true)} sats`;
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
splitLine: {
|
|
|
|
show: false,
|
|
|
|
},
|
2024-04-26 17:53:44 +00:00
|
|
|
min: this.period === 'all' ? 0 : 'dataMin'
|
2024-03-18 08:19:56 +00:00
|
|
|
},
|
2024-06-12 11:47:57 +02:00
|
|
|
{
|
|
|
|
type: 'value',
|
|
|
|
axisLabel: {
|
|
|
|
color: 'rgb(110, 112, 121)',
|
|
|
|
formatter: function(val) {
|
2024-12-09 08:54:26 +01:00
|
|
|
return `$${this.amountShortenerPipe.transform(val, 0, undefined, true)}`;
|
2024-06-12 11:47:57 +02:00
|
|
|
}.bind(this)
|
|
|
|
},
|
|
|
|
splitLine: {
|
|
|
|
show: false,
|
|
|
|
},
|
|
|
|
min: this.period === 'all' ? 0 : 'dataMin'
|
|
|
|
},
|
2024-03-18 08:19:56 +00:00
|
|
|
],
|
|
|
|
series: [
|
|
|
|
{
|
2024-04-08 16:01:26 +09:00
|
|
|
name: $localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`,
|
2024-06-12 11:47:57 +02:00
|
|
|
yAxisIndex: 0,
|
|
|
|
showSymbol: false,
|
|
|
|
symbol: 'circle',
|
|
|
|
symbolSize: 8,
|
2024-06-12 16:57:19 +02:00
|
|
|
data: this.data,
|
2024-06-12 11:47:57 +02:00
|
|
|
areaStyle: {
|
|
|
|
opacity: 0.5,
|
|
|
|
},
|
|
|
|
triggerLineEvent: true,
|
|
|
|
type: 'line',
|
|
|
|
smooth: false,
|
|
|
|
step: 'end'
|
2024-06-23 18:59:50 +09:00
|
|
|
}, !this.stateService.isAnyTestnet() ?
|
2024-06-12 11:47:57 +02:00
|
|
|
{
|
|
|
|
name: 'Fiat',
|
|
|
|
yAxisIndex: 1,
|
2024-03-18 08:19:56 +00:00
|
|
|
showSymbol: false,
|
|
|
|
symbol: 'circle',
|
|
|
|
symbolSize: 8,
|
2024-06-12 16:57:19 +02:00
|
|
|
data: this.fiatData,
|
2024-03-18 08:19:56 +00:00
|
|
|
areaStyle: {
|
|
|
|
opacity: 0.5,
|
|
|
|
},
|
2024-03-21 09:02:17 +00:00
|
|
|
triggerLineEvent: true,
|
2024-03-18 08:19:56 +00:00
|
|
|
type: 'line',
|
|
|
|
smooth: false,
|
|
|
|
step: 'end'
|
2024-06-23 18:59:50 +09:00
|
|
|
} : undefined
|
2024-03-18 08:19:56 +00:00
|
|
|
],
|
2024-06-12 12:57:13 +02:00
|
|
|
dataZoom: this.allowZoom ? [{
|
|
|
|
type: 'inside',
|
|
|
|
realtime: true,
|
|
|
|
zoomLock: true,
|
|
|
|
maxSpan: 100,
|
|
|
|
minSpan: 5,
|
|
|
|
moveOnMouseMove: false,
|
|
|
|
}, {
|
|
|
|
showDetail: false,
|
|
|
|
show: true,
|
|
|
|
type: 'slider',
|
|
|
|
brushSelect: false,
|
|
|
|
realtime: true,
|
|
|
|
left: this.left,
|
|
|
|
right: this.right,
|
|
|
|
selectedDataBackground: {
|
|
|
|
lineStyle: {
|
|
|
|
color: '#fff',
|
|
|
|
opacity: 0.45,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}] : undefined
|
2024-03-18 08:19:56 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-03-21 09:02:17 +00:00
|
|
|
onChartClick(e) {
|
|
|
|
if (this.hoverData?.length && this.hoverData[0]?.[2]?.txid) {
|
2024-06-12 20:01:48 +02:00
|
|
|
this.zone.run(() => {
|
|
|
|
const url = this.relativeUrlPipe.transform(`/tx/${this.hoverData[0][2].txid}`);
|
|
|
|
if (e.event.event.shiftKey || e.event.event.ctrlKey || e.event.event.metaKey) {
|
|
|
|
window.open(url);
|
|
|
|
} else {
|
|
|
|
this.router.navigate([url]);
|
|
|
|
}
|
|
|
|
});
|
2024-03-21 09:02:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onTooltip(e) {
|
|
|
|
this.hoverData = (e?.dataByCoordSys?.[0]?.dataByAxis?.[0]?.seriesDataIndices || []).map(indices => this.data[indices.dataIndex]);
|
|
|
|
}
|
|
|
|
|
2024-06-12 11:47:57 +02:00
|
|
|
onLegendSelectChanged(e) {
|
2024-06-13 15:03:22 +02:00
|
|
|
this.selected = e.selected;
|
|
|
|
this.right = this.selected['Fiat'] ? +this.initialRight + 40 : this.initialRight;
|
|
|
|
this.left = this.selected[$localize`:@@7e69426bd97a606d8ae6026762858e6e7c86a1fd:Balance`] ? this.initialLeft : +this.initialLeft - 40;
|
|
|
|
|
|
|
|
this.chartOptions = {
|
|
|
|
grid: {
|
|
|
|
right: this.right,
|
|
|
|
left: this.left,
|
|
|
|
},
|
|
|
|
legend: {
|
|
|
|
selected: this.selected,
|
|
|
|
},
|
|
|
|
dataZoom: this.allowZoom ? [{
|
|
|
|
left: this.left,
|
|
|
|
right: this.right,
|
|
|
|
}, {
|
|
|
|
left: this.left,
|
|
|
|
right: this.right,
|
|
|
|
}] : undefined
|
|
|
|
};
|
2024-12-09 08:54:26 +01:00
|
|
|
|
2024-06-13 15:03:22 +02:00
|
|
|
if (this.chartInstance) {
|
|
|
|
this.chartInstance.setOption(this.chartOptions);
|
|
|
|
}
|
2024-06-12 11:47:57 +02:00
|
|
|
}
|
|
|
|
|
2024-03-18 08:19:56 +00:00
|
|
|
onChartInit(ec) {
|
|
|
|
this.chartInstance = ec;
|
2024-03-21 09:02:17 +00:00
|
|
|
this.chartInstance.on('showTip', this.onTooltip.bind(this));
|
|
|
|
this.chartInstance.on('click', 'series', this.onChartClick.bind(this));
|
2024-06-12 11:47:57 +02:00
|
|
|
this.chartInstance.on('legendselectchanged', this.onLegendSelectChanged.bind(this));
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|
|
|
|
|
2024-04-29 20:54:56 +00:00
|
|
|
ngOnDestroy(): void {
|
2024-05-08 00:17:35 +00:00
|
|
|
if (this.subscription) {
|
|
|
|
this.subscription.unsubscribe();
|
|
|
|
}
|
2024-04-29 20:54:56 +00:00
|
|
|
}
|
|
|
|
|
2024-03-18 08:19:56 +00:00
|
|
|
isMobile() {
|
|
|
|
return (window.innerWidth <= 767.98);
|
|
|
|
}
|
2024-06-13 15:03:22 +02:00
|
|
|
|
|
|
|
extendSummary(summary) {
|
|
|
|
let extendedSummary = summary.slice();
|
|
|
|
|
|
|
|
// Add a point at today's date to make the graph end at the current time
|
|
|
|
extendedSummary.unshift({ time: Date.now() / 1000, value: 0 });
|
|
|
|
extendedSummary.reverse();
|
|
|
|
|
|
|
|
let oneHour = 60 * 60;
|
|
|
|
// Fill gaps longer than interval
|
|
|
|
for (let i = 0; i < extendedSummary.length - 1; i++) {
|
|
|
|
let hours = Math.floor((extendedSummary[i + 1].time - extendedSummary[i].time) / oneHour);
|
|
|
|
if (hours > 1) {
|
|
|
|
for (let j = 1; j < hours; j++) {
|
|
|
|
let newTime = extendedSummary[i].time + oneHour * j;
|
|
|
|
extendedSummary.splice(i + j, 0, { time: newTime, value: 0 });
|
|
|
|
}
|
|
|
|
i += hours - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return extendedSummary.reverse();
|
|
|
|
}
|
2024-03-18 08:19:56 +00:00
|
|
|
}
|