diff --git a/frontend/src/app/components/address-graph/address-graph.component.scss b/frontend/src/app/components/address-graph/address-graph.component.scss
index a118549fb..3752203c1 100644
--- a/frontend/src/app/components/address-graph/address-graph.component.scss
+++ b/frontend/src/app/components/address-graph/address-graph.component.scss
@@ -45,23 +45,8 @@
display: flex;
flex: 1;
width: 100%;
- padding-bottom: 20px;
+ padding-bottom: 10px;
padding-right: 10px;
- @media (max-width: 992px) {
- padding-bottom: 25px;
- }
- @media (max-width: 829px) {
- padding-bottom: 50px;
- }
- @media (max-width: 767px) {
- padding-bottom: 25px;
- }
- @media (max-width: 629px) {
- padding-bottom: 55px;
- }
- @media (max-width: 567px) {
- padding-bottom: 55px;
- }
}
.chart-widget {
width: 100%;
diff --git a/frontend/src/app/components/address-graph/address-graph.component.ts b/frontend/src/app/components/address-graph/address-graph.component.ts
index 26a1bd408..a5db9602d 100644
--- a/frontend/src/app/components/address-graph/address-graph.component.ts
+++ b/frontend/src/app/components/address-graph/address-graph.component.ts
@@ -1,6 +1,6 @@
-import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnChanges, SimpleChanges } from '@angular/core';
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
import { echarts, EChartsOption } from '../../graphs/echarts';
-import { Observable, of } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription, combineLatest, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AddressTxSummary, ChainStats } from '../../interfaces/electrs.interface';
import { ElectrsApiService } from '../../services/electrs-api.service';
@@ -32,7 +32,7 @@ const periodSeconds = {
`],
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class AddressGraphComponent implements OnChanges {
+export class AddressGraphComponent implements OnChanges, OnDestroy {
@Input() address: string;
@Input() isPubkey: boolean = false;
@Input() stats: ChainStats;
@@ -46,6 +46,9 @@ export class AddressGraphComponent implements OnChanges {
data: any[] = [];
hoverData: any[] = [];
+ subscription: Subscription;
+ redraw$: BehaviorSubject
= new BehaviorSubject(false);
+
chartOptions: EChartsOption = {};
chartInitOptions = {
renderer: 'svg',
@@ -70,24 +73,38 @@ export class AddressGraphComponent implements OnChanges {
if (!this.address || !this.stats) {
return;
}
- (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);
- }),
- )).subscribe(addressSummary => {
- if (addressSummary) {
- this.error = null;
- this.prepareChartOptions(addressSummary);
+ if (changes.address || changes.isPubkey || changes.addressSummary$) {
+ if (this.subscription) {
+ this.subscription.unsubscribe();
}
- this.isLoading = false;
- this.cd.markForCheck();
- });
+ 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);
+ }),
+ ))
+ ]).subscribe(([redraw, addressSummary]) => {
+ if (addressSummary) {
+ this.error = null;
+ this.prepareChartOptions(addressSummary);
+ }
+ this.isLoading = false;
+ this.cd.markForCheck();
+ });
+ } else {
+ // re-trigger subscription
+ this.redraw$.next(true);
+ }
}
prepareChartOptions(summary): void {
+ if (!summary || !this.stats) {
+ return;
+ }
let total = (this.stats.funded_txo_sum - this.stats.spent_txo_sum);
this.data = summary.map(d => {
const balance = total;
@@ -104,8 +121,8 @@ export class AddressGraphComponent implements OnChanges {
);
}
- 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);
+ 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);
this.chartOptions = {
color: [
@@ -230,6 +247,10 @@ export class AddressGraphComponent implements OnChanges {
this.chartInstance.on('click', 'series', this.onChartClick.bind(this));
}
+ ngOnDestroy(): void {
+ this.subscription.unsubscribe();
+ }
+
isMobile() {
return (window.innerWidth <= 767.98);
}
diff --git a/frontend/src/app/components/address/address.component.html b/frontend/src/app/components/address/address.component.html
index 531b97464..661e84869 100644
--- a/frontend/src/app/components/address/address.component.html
+++ b/frontend/src/app/components/address/address.component.html
@@ -53,10 +53,20 @@
2">
+
+
Balance History
+
diff --git a/frontend/src/app/components/address/address.component.scss b/frontend/src/app/components/address/address.component.scss
index 7107c73f2..78ca0e80d 100644
--- a/frontend/src/app/components/address/address.component.scss
+++ b/frontend/src/app/components/address/address.component.scss
@@ -109,3 +109,19 @@ h1 {
flex-grow: 0.5;
}
}
+
+.widget-toggler {
+ font-size: 12px;
+ position: absolute;
+ top: -20px;
+ right: 3px;
+ text-align: right;
+}
+
+.toggler-option {
+ text-decoration: none;
+}
+
+.inactive {
+ color: var(--transparent-fg);
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts
index 95abe4ac1..19712a702 100644
--- a/frontend/src/app/components/address/address.component.ts
+++ b/frontend/src/app/components/address/address.component.ts
@@ -38,6 +38,8 @@ export class AddressComponent implements OnInit, OnDestroy {
txCount = 0;
received = 0;
sent = 0;
+ now = Date.now() / 1000;
+ balancePeriod: 'all' | '1m' = 'all';
private tempTransactions: Transaction[];
private timeTxIndexes: number[];
@@ -174,6 +176,10 @@ export class AddressComponent implements OnInit, OnDestroy {
this.transactions = this.tempTransactions;
this.isLoadingTransactions = false;
+
+ if (!this.showBalancePeriod()) {
+ this.setBalancePeriod('all');
+ }
},
(error) => {
console.log(error);
@@ -296,6 +302,18 @@ export class AddressComponent implements OnInit, OnDestroy {
this.txCount = this.address.chain_stats.tx_count + this.address.mempool_stats.tx_count;
}
+ setBalancePeriod(period: 'all' | '1m'): boolean {
+ this.balancePeriod = period;
+ return false;
+ }
+
+ showBalancePeriod(): boolean {
+ return this.transactions?.length && (
+ !this.transactions[0].status?.confirmed
+ || this.transactions[0].status.block_time > (this.now - (60 * 60 * 24 * 30))
+ );
+ }
+
ngOnDestroy() {
this.mainSubscription.unsubscribe();
this.mempoolTxSubscription.unsubscribe();