Skip to content

Commit 8a89c2a

Browse files
authored
Add wallets overview page (#272)
1 parent 16408d4 commit 8a89c2a

8 files changed

Lines changed: 119 additions & 7 deletions

File tree

docs/release-notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Feature: Dynamically start and manage Specter's Tor Hidden Service from the UI (#257) (@ben-kaufman)
1111
- Feature: Allow user to customize the Bitcoin Core data-dir path (#260) (@ben-kaufman)
1212
- Feature: Automatically derive key origin for depth 0 and 1 (#264) (@hodlwave)
13+
- UI: Add Wallets Overview page showing the combined balance and transactions history of all user's wallets. (#272) (@ben-kaufman)
1314
- UI: Add Bitcoin Core node info dashboard (#267) (@ben-kaufman)
1415
- UI: New landing page and multiple UI fixes. (#269) (@ben-kaufman)
1516
- UI: Make sidebar wallets and devices lists foldable (#263) (@ben-kaufman)

src/cryptoadvance/specter/controller.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@ def new_wallet(wallet_type):
857857
rand=rand
858858
)
859859

860+
860861
@app.route('/wallets/<wallet_alias>/')
861862
@login_required
862863
def wallet(wallet_alias):
@@ -871,11 +872,27 @@ def wallet(wallet_alias):
871872
else:
872873
return redirect("/wallets/%s/tx/" % wallet_alias)
873874

875+
876+
@app.route('/wallets_overview/')
877+
@login_required
878+
def wallets_overview():
879+
app.specter.check()
880+
idx = int(request.args.get('idx', default=0))
881+
return render_template(
882+
"wallet/wallets_overview.jinja",
883+
idx=idx,
884+
history=True,
885+
specter=app.specter,
886+
rand=rand
887+
)
888+
889+
874890
@app.route('/wallets/<wallet_alias>/tx/')
875891
@login_required
876892
def wallet_tx(wallet_alias):
877893
return redirect("/wallets/%s/tx/history" % wallet_alias)
878894

895+
879896
@app.route('/wallets/<wallet_alias>/tx/history/')
880897
@login_required
881898
def wallet_tx_history(wallet_alias):

src/cryptoadvance/specter/templates/includes/sidebar/components/sidebar_btn.jinja

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
- url_path: URL path the button should lead to (ie. 'new_wallet', 'new_device').
55
- text: The text of the button.
66
#}
7-
{% macro sidebar_btn(url_path, text) -%}
7+
{% macro sidebar_btn(url_path, text, icon=true) -%}
88
<div class="item">
99
<a href="/{{url_path}}/" class="btn">
10-
<svg width="20" height="20" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>{{ text }}
10+
{% if icon %}<svg width="20" height="20" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>{% endif %}{{ text }}
1111
</a>
1212
</div>
1313
{%- endmacro %}

src/cryptoadvance/specter/templates/includes/sidebar/sidebar.jinja

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
{% from 'includes/sidebar/components/sidebar_btn.jinja' import sidebar_btn %}
2+
<style>
3+
#wallets_overview_link {
4+
text-transform: none;
5+
text-decoration: none;
6+
float: right;
7+
margin-right: 10px;
8+
}
9+
10+
#wallets_overview_link:hover {
11+
text-decoration: underline;
12+
color: #fff;
13+
}
14+
</style>
215
<div id="side-content">
316
<nav class="side">
417
{% if specter.info["initialblockdownload"] %}
@@ -40,6 +53,11 @@
4053
{% endif %}
4154
<div class="separator">
4255
<span id="toggle_wallets_list" style="cursor: pointer;">Wallets &nbsp; &#9660;</span>
56+
{% if (specter.wallet_manager.wallets | length) > 1 %}
57+
<a class="note" id="wallets_overview_link" href="/wallets_overview/" style="cursor: pointer;">
58+
Wallets overview
59+
</a>
60+
{% endif %}
4361
</div>
4462
{% if (specter.device_manager.devices | length) != 0 and specter.chain %}
4563
{% from 'includes/sidebar/components/sidebar_wallet_list_item.jinja' import sidebar_wallet_list_item %}

src/cryptoadvance/specter/templates/wallet/history/components/tx_item.jinja

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
{{ explorer_link('tx', tx['txid'], tx['txid'], specter.explorer) }}
2222
</td>
2323
<td class="tx scroll">
24-
{{ explorer_link('address', tx['address'], wallet.get_address_name(tx['address'], -1), specter.explorer) }}
24+
{% if wallet == none %}
25+
{% set wallet = specter.wallet_manager.get_by_alias(tx['wallet_alias']) %}
26+
{% endif %}
27+
{{ explorer_link('address', tx['address'], tx['address'] if wallet == none else wallet.get_address_name(tx['address'], -1), specter.explorer) }}
2528
</td>
2629
<td>
2730
{{ tx['amount'] | btcamount }}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{% extends "base.jinja" %}
2+
{% block main %}
3+
<style>
4+
.arrow :hover {
5+
background-color: #ddd;
6+
color: black;
7+
}
8+
9+
.arrow {
10+
text-decoration: none;
11+
display: inline-block;
12+
margin: 0px 16px 16px;
13+
border-radius: 50%;
14+
color: #fff;
15+
font-size: 1.5em;
16+
}
17+
18+
.disabled {
19+
visibility: hidden;
20+
}
21+
</style>
22+
<h1>Wallets Overview</h1>
23+
<p>Here you can see the combined balance and transactions history of all your Specter wallets.</p>
24+
<h1>
25+
<small style="line-height:30px">Total balance:</small><br>
26+
<span style="color: #fff">
27+
{{ specter.wallet_manager.wallets.values() | sum(attribute='fullbalance') | btcamount }}
28+
{% if specter.chain !='main' %}t{%endif%}BTC
29+
</span>
30+
{% if specter.wallet_manager.wallets.values() | sum(attribute='balance.untrusted_pending') > 0 %}<br>
31+
<small>( {{ specter.wallet_manager.wallets.values() | sum(attribute='balance.trusted') | btcamount }} confirmed, {{ specter.wallet_manager.wallets.values() | sum(attribute='balance.untrusted_pending') | btcamount }} pending )</small>
32+
{% endif %}
33+
</h1>
34+
{% set transactions = specter.wallet_manager.full_txlist(idx) %}
35+
{% set next_transactions = specter.wallet_manager.full_txlist(idx + 1) %}
36+
{% if idx != 0 or next_transactions|length != 0 %}
37+
<div>
38+
<a href="./?idx={{ idx - 1 }}" class="arrow {% if idx == 0 %}disabled{% endif %}">&#8249;</a>
39+
{% if next_transactions|length > 0 or idx != 0 %}
40+
Page {{ idx + 1 }}
41+
{% endif %}
42+
<a href="./?idx={{ idx + 1 }}" class="arrow {% if next_transactions|length == 0 %}disabled{% endif %}">&#8250;</a>
43+
</div>
44+
{% endif %}
45+
<div class="table-holder">
46+
{% from 'wallet/history/components/tx_table.jinja' import tx_table %}
47+
{% from 'wallet/history/txs/components/full_tx_table.jinja' import full_tx_table %}
48+
{{
49+
tx_table(
50+
transactions == [],
51+
full_tx_table(transactions, none, specter)
52+
)
53+
}}
54+
</div>
55+
{% endblock %}

src/cryptoadvance/specter/wallet.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
# a gap of 20 addresses is what many wallets do
1313
WALLET_CHUNK = 20
14-
WALLET_TX_BATCH = 100
14+
wallet_tx_batch = 100
1515

1616
class Wallet():
1717
def __init__(
@@ -301,11 +301,11 @@ def save_pending_psbt(self, psbt):
301301
self.cli.lockunspent(False, psbt["tx"]["vin"])
302302
self.save_to_file()
303303

304-
def txlist(self, idx):
304+
def txlist(self, idx, wallet_tx_batch=100):
305305
try:
306-
cli_txs = self.cli.listtransactions("*", WALLET_TX_BATCH + 2, WALLET_TX_BATCH * idx, True) # get batch + 2 to make sure you have information about send
306+
cli_txs = self.cli.listtransactions("*", wallet_tx_batch + 2, wallet_tx_batch * idx, True) # get batch + 2 to make sure you have information about send
307307
cli_txs.reverse()
308-
transactions = cli_txs[:WALLET_TX_BATCH]
308+
transactions = cli_txs[:wallet_tx_batch]
309309
except:
310310
return []
311311
txids = []

src/cryptoadvance/specter/wallet_manager.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,21 @@ def rename_wallet(self, wallet, name):
268268
if self.working_folder is not None:
269269
wallet.save_to_file()
270270
self.update()
271+
272+
def full_txlist(self, idx):
273+
txlists = [
274+
[
275+
{
276+
**tx,
277+
'wallet_alias': wallet.alias
278+
} for tx in wallet.txlist(
279+
idx,
280+
wallet_tx_batch=100 // len(self.wallets)
281+
)
282+
] for wallet in self.wallets.values()
283+
]
284+
result = []
285+
for txlist in txlists:
286+
for tx in txlist:
287+
result.append(tx)
288+
return list(reversed(sorted(result, key=lambda tx: tx["time"])))

0 commit comments

Comments
 (0)