feat(addressbook): opt-in save dialog for new external recipients#238
Merged
Conversation
Sending to an address not yet in the Address Book now pops a dialog after broadcast: warns that funds are leaving the wallet, shows the external address, and offers to save + label it. Saving is explicit (R4 opt-in) — "Not now" leaves the address out of the book. - recordOutbound now leaves auto-recorded external rows with saved_at=null (history-only) and reports `unsaved`; the book + send picker only show explicitly-saved contacts (listAddressBook savedOnly). - addExternalEntry stamps saved_at on explicit save; migration backfills existing labeled / manually-added rows so they stay visible. - Address Book rows display the date a contact was added. - Replaces the prior subtle inline label prompt with the SaveRecipientDialog.
…en wallets) The Address Book is public, non-secret data — your watch-only wallet addresses + saved contacts — so it should not be tied to which wallet is active. Previously every address-book RPC blanket-returned empty in a passphrase/hidden session, so the book showed nothing (no device wallets, no contacts) and sends couldn't be saved. - listAddressBook no longer gates on passphrase; own (cross-device) and external (cross-wallet) entries load in any session. Only seedOwnFromCache (a write) stays standard-only. - add/update/delete/history RPCs de-gated; mutations act globally by id (wallet_id is retained as provenance/dedup, no longer a read/edit scope). - broadcastTx: split api_log (kept standard-only for tx deniability) from the address-book capture, so recipients can be saved from hidden-wallet sends. Note: a contact saved while in a hidden wallet is therefore visible from the standard wallet — the address book is intentionally outside hidden-wallet deniability (api_log/balances writes remain gated).
Collaborator
Author
Follow-up commit: Address Book is now wallet-agnostic (
|
Move address-book detection to where it's useful — while entering the
recipient — instead of after the funds have already left.
- New matchAddress RPC + matchAddressBook() (exact networkId equality, R5):
returns the matching own wallet / saved contact, or null for a new address.
- SendForm runs a debounced matchAddress as the recipient is entered:
- known → show the contact's identicon + label (gold chip)
- new valid address → rose "New address" chip + auto-open the save dialog
(once per address; also reachable via the chip's Save link)
- Saving now happens at form-fill (before sending); the post-broadcast save
dialog + its confirmation are removed. recordOutbound still records
outbound history on broadcast.
Dialog warning reworded for the pre-send context (verify the address).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a manual send goes to an address that isn't already a saved contact, a dialog now pops up after broadcast:
This makes saving opt-in. Previously every external recipient was silently auto-saved (shown as "Unlabeled" in Saved recipients) and a subtle inline label box was the only prompt.
How it works
recordOutboundstill anchors an external row on send to carry outbound tx-history, but now leaves itsaved_at = NULL(history-only, hidden from the book/picker) and reports whether the recipient isunsaved.listAddressBook) only surface explicitly-saved contacts (savedOnly).saved_at. The Address Book row shows "Added <date>".saved_atcolumn via guardedALTER TABLE; a one-time backfill marks existing labeled or manually-added rows as saved so nothing visible disappears on upgrade. Unlabeled auto-recorded recipients become history-only.Files
bun/db.ts—saved_atcolumn + migration/backfill,recordOutboundopt-in semantics,addExternalEntrystampssaved_at,getAddressBookListsavedOnlyfilter, row mapping.bun/index.ts—broadcastTxreturnsrecipientUnsaved;listAddressBookexternal = saved-only.shared/types.ts—savedAt,savedOnly,recipientUnsaved.mainview/components/SaveRecipientDialog.tsx— new modal.mainview/components/SendForm.tsx— trigger dialog post-broadcast (replaces inline prompt).mainview/components/AddressBookView.tsx— "Added <date>" line.i18n/locales/en/addressbook.json— new source strings.Test plan