Skip to content

qml: allow hiding balances and amounts#10636

Open
f321x wants to merge 4 commits into
spesmilo:masterfrom
f321x:qml_hide_amounts
Open

qml: allow hiding balances and amounts#10636
f321x wants to merge 4 commits into
spesmilo:masterfrom
f321x:qml_hide_amounts

Conversation

@f321x

@f321x f321x commented May 8, 2026

Copy link
Copy Markdown
Member

This allows hiding the wallet balance and transaction amounts by long-pressing the balance summary of the main view in QML (or toggling the config in the Preferences).

When enabled the amounts will be replaced by a fixed **** string and the units and fiat are hidden.

Screencast_20260512_165512.webm

@f321x f321x added the gui-qml label May 8, 2026
@f321x f321x marked this pull request as draft May 8, 2026 15:44
@f321x f321x marked this pull request as draft May 8, 2026 15:44
Comment thread electrum/util.py Outdated
Comment on lines +882 to +885
def redact_amount_string(s: str) -> str:
"""Replace ASCII digits in a formatted-amount string with '*'."""
return re.sub(r'[0-9]', '*', s)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The length of the amount still exposes some information (*** sat vs ** *** *** sat), but i think that's a good tradeoff between usability and privacy.

I disagree. What is the goal of the feature? If someone has a thousand coins, it does not gain them much that a shoulder-surfer does not see whether it is 1000 or 9999, does it?

Just make it always be (say) four stars, both for shorter and longer "true" values.

That way you can still keep displaying the units (e.g. BTC vs sat) - I imagine hiding the unit would add more complexity.

So e.g.:

36.204 84 mBTC --> **** mBTC
3 620 484 sat --> **** sat 

but if it is simple, we could also hide the units, so e.g.:

36.204 84 mBTC --> ****
3 620 484 sat --> ****

btw, I just tested and this last example seems to align with what phoenix is doing

@f321x f321x May 11, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thinking was that showing the exact amount of digits provides the benefit of being able to at least roughly estimate the balance as a user, improving the usability of the wallet with the mode enabled.

So e.g. when standing in line to pay for a drink in public you're still able to infer if it you'll be able to pay for the drink instead of having a.) deactivate the obfuscation in public or b.) attempt the payment and then awkwardly fail because the balance was too low. In the end the feature is only beneficial if it doesn't break the main usecase of a wallet, making transactions.

On the other hand I assumed the human mind is optimized to parse digits/numbers very quickly, but has a hard time counting * quickly, so a quick shoulder surfer likely isn't able to get much information as they are not used to the UI and counting * characters from distance.

But OTOH this mostly affects the "sat" unit, if set to "BTC" it will anyways most of the time be *.*** *** ** BTC, so we might as well just show **** uniformly.

What is the goal of the feature?

My motivation was actually not preventing shoulder surfers (though this is probably what the majority of users would benefit from through this feature), but instead being able to demonstrate the app and its features to others without having to use a dummy wallet or reveal my exact balance. For this i agree showing the exact digits is not sufficient but i found it better than not having the obfuscation at all and considered the tradeoffs above for the shoulder surfer usecase.

I'll change it to just use ****.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to show **** without unit in relevant places (main view, channels and utxo list), while still showing amounts in the other views (e.g. when confirming a payment or doing a swap).

@f321x f321x force-pushed the qml_hide_amounts branch 2 times, most recently from 5061a8a to 9b1a1c8 Compare May 12, 2026 14:04
@f321x f321x marked this pull request as ready for review May 12, 2026 14:58
f321x added 4 commits June 2, 2026 14:03
Implement hiding the wallet balance and transaction amounts when
the user long presses the balance details field in the wallet history
view.
Wrap the balanceLayout, syncLabel and statusLabel in an Item
which adjusts its width based on the contents width.
Previously when loading a wallet with a small balanceLayout
the syncLabel ("Synchronizing (X/Y)" string) would exceed the width
of the balancePane and it looks broken.
Un-focuses the search box when clicking elsewhere, moves the
zoom icon correctly into the search box instead of overlapping
with the search box border.
Adds a toggle for setting the hideAmounts config to the Preferences
so users don't have to discover that long pressing the balance
toggles it. Also shows an hint to guide the user to the more
conveniant way of toggling it by long pressing the balance.
@f321x f321x force-pushed the qml_hide_amounts branch from 9b1a1c8 to 7d0f920 Compare June 2, 2026 12:04
Comment on lines +379 to +382
@pyqtSlot(QEAmount, bool, bool, result=str)
def formatSats(self, satoshis, with_unit=False, hide_amounts: bool = False):
if hide_amounts:
return '****'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing Config.hideAmounts if you want the configured setting, you could define hide_amounts as a tri-state bool|None and fall back to Config.hideAmounts if it is None. This allows to only specify hide_amounts if it deviates from the configured setting.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would work but I think the default case we want, is showing the amounts. If the default case (not passing Config.hideAmounts) would be to read the config, we would actually have to pass False as argument in a lot of places simply to not hide the amounts. This would change more lines than the current approach I think?

Also not passing Config.hideAmounts from QML would remove the QML binding to the config, so if the config changes and the label doesn't get reloaded otherwise it wouldn't update when changing the config.

@accumulator accumulator Jun 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would work but I think the default case we want, is showing the amounts. If the default case (not passing Config.hideAmounts) would be to read the config, we would actually have to pass False as argument in a lot of places simply to not hide the amounts. This would change more lines than the current approach I think?

You might be right, it depends what % of amount occurrences we want to hide by config. In the most extreme case, we might only want to force-show amounts for requests and invoices (those not in listviews).

Also not passing Config.hideAmounts from QML would remove the QML binding to the config, so if the config changes and the label doesn't get reloaded otherwise it wouldn't update when changing the config.

I'm quite sure binding doesn't work for Config.formatSats(..., Config.hideAmounts) . Using a property as a parameter in a slot-call is not a binding and doesn't update. This is probably not apparent in the Delegates as they are rerendered often (?) by the ListViews, and in places where updates are triggered using a Connections block + update function (e.g. BalanceSummary).

let hide = Config.hideAmounts
root.formattedConfirmedBalance = Config.formatSats(Daemon.currentWallet.confirmedBalance, false, hide)
root.formattedTotalBalance = Config.formatSats(Daemon.currentWallet.totalBalance, false, hide)
root.formattedLightningBalance = Config.formatSats(Daemon.currentWallet.lightningBalance, false, hide)

@accumulator accumulator Jun 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. here

Label {
font.family: FixedFont
text: Config.formatSats(model.balance, false)
text: Config.formatSats(model.balance, false, Config.hideAmounts)

@accumulator accumulator Jun 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants