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 6d40a8ebb..229199aa2 100644
--- a/frontend/src/app/components/address-graph/address-graph.component.ts
+++ b/frontend/src/app/components/address-graph/address-graph.component.ts
@@ -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);
diff --git a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html
index c1c999d6f..ea055a96f 100644
--- a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html
+++ b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.html
@@ -12,7 +12,7 @@
-
|
+ |
|
|
diff --git a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts
index 998d269ba..83424791b 100644
--- a/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts
+++ b/frontend/src/app/components/address-transactions-widget/address-transactions-widget.component.ts
@@ -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 {
diff --git a/frontend/src/app/components/balance-widget/balance-widget.component.html b/frontend/src/app/components/balance-widget/balance-widget.component.html
index 4923a2c06..87f14de53 100644
--- a/frontend/src/app/components/balance-widget/balance-widget.component.html
+++ b/frontend/src/app/components/balance-widget/balance-widget.component.html
@@ -4,10 +4,10 @@
BTC Holdings
- {{ ((addressInfo.chain_stats.funded_txo_sum - addressInfo.chain_stats.spent_txo_sum) / 100_000_000) | number: '1.2-2' }} BTC
+ {{ ((total) / 100_000_000) | number: '1.2-2' }} BTC
diff --git a/frontend/src/app/components/balance-widget/balance-widget.component.ts b/frontend/src/app/components/balance-widget/balance-widget.component.ts
index 8e1d3f442..f830587cc 100644
--- a/frontend/src/app/components/balance-widget/balance-widget.component.ts
+++ b/frontend/src/app/components/balance-widget/balance-widget.component.ts
@@ -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;
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
index bf72aab69..65f0dc0ab 100644
--- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
+++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.html
@@ -257,6 +257,36 @@
}
+ @case ('walletBalance') {
+
+ }
+ @case ('wallet') {
+
+
+
+
+ Balance History
+
+
+
+
+
+ }
+ @case ('walletTransactions') {
+
+
+
+
+ Treasury Transactions
+
+
+
+
+
+ }
@case ('twitter') {
diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts
index fbaf7be74..622e6cf3a 100644
--- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts
+++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts
@@ -62,8 +62,10 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni
widgets;
addressSubscription: Subscription;
+ walletSubscription: Subscription;
blockTxSubscription: Subscription;
addressSummary$: Observable
;
+ walletSummary$: Observable;
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();
+ 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) {
diff --git a/frontend/src/app/components/master-page-preview/master-page-preview.component.html b/frontend/src/app/components/master-page-preview/master-page-preview.component.html
index 8f3204ec4..01995906f 100644
--- a/frontend/src/app/components/master-page-preview/master-page-preview.component.html
+++ b/frontend/src/app/components/master-page-preview/master-page-preview.component.html
@@ -6,7 +6,7 @@
}
@if (enterpriseInfo?.header_img) {
-
+
} @else {
diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html
index 1aa13e309..557529eef 100644
--- a/frontend/src/app/components/master-page/master-page.component.html
+++ b/frontend/src/app/components/master-page/master-page.component.html
@@ -19,7 +19,7 @@
@if (enterpriseInfo?.header_img) {
-
+
} @else {