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') { +
+
Treasury
+ +
+ } + @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) { - enterpriseInfo.title + } @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) { - enterpriseInfo.title + } @else {
@@ -39,7 +39,7 @@ @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else {
@@ -49,7 +49,7 @@ @if (enterpriseInfo?.header_img) { - enterpriseInfo.title + } @else { diff --git a/frontend/src/app/components/tracker/tracker.component.html b/frontend/src/app/components/tracker/tracker.component.html index 4e222479b..2d9bd4982 100644 --- a/frontend/src/app/components/tracker/tracker.component.html +++ b/frontend/src/app/components/tracker/tracker.component.html @@ -4,7 +4,7 @@