Skip to content

feat: a count on the app icon of money received and not yet looked at - #1151

Merged
cryptskii merged 8 commits into
mainfrom
feat/money-received-badge
Oct 26, 2026
Merged

cryptskii merged 8 commits into
mainfrom
feat/money-received-badge

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

What

An email-style unread count for money coming in, on the app icon and on the in-app b0x button (owner asked 2026-10-22; owner ruled 2026-10-23 that the count clears when the b0x inbox is opened).

  • Rust decides the number. Each verified received transfer gets a notice, written in the same SQLite transaction as its history row (ensure_received_history), keyed by the transfer's id so it is counted once however many passes write it. How far the user has looked is stored beside it. A look never goes back and never past a notice that exists, so a transfer that lands while the inbox is open still counts.
  • Routes: inbox.moneyWaiting (query) and inbox.moneySeen (invoke) answer MoneyWaitingResponse: the waiting transfers, each as the history reports it, plus the newest notice number. sdk::money_waiting::publish hands Android the count and posts money.waiting to the page whenever the count can have changed.
  • Android: MoneyReceivedNotice draws the count as one low-importance notification ("Money received", no amount, no sender) with setNumber, removed at zero. Launchers badge the icon from it.
  • Frontend: the b0x button shows Rust's count. Opening the b0x lists the received transfers and reports them seen. The 8-second transient tally it replaces is removed, along with the test that asserted it.
  • The wallet.history row conversion moved into history_info / contact_aliases, so the inbox list and the history render rows the same way.

Not in this PR

  • No push service, no Firebase. The phone already receives online transfers in the background (Backgrounded::Listening, shipped in perf: parallel inbox reads and deliveries, kept final reads, background listening #1111). If the process is dead (reboot, force stop, phone-maker battery killer), the count updates on the next open.
  • The background listener's battery cost is not measured here. Whether a push doorbell should replace the listener is a separate decision.
  • Offline (BLE) receives are not counted. The notice is written on the online receive path only.

Verification

  • Rust, release, Postgres-backed storage nodes: money_waiting_tests 2/0, run together with the history and poller tests (52 passed, 0 failed). Control run 5 of 5 green. Four mutants, all from one build, each turn a named assertion red:
    • no notice written
    • the notice rewritten on a re-store
    • a look allowed past the newest notice
    • a look allowed to go backwards
  • Android: MoneyReceivedNoticeTest 2/0 (Robolectric).
  • Frontend: 68 suites / 425 tests pass; type-check and production lint are clean.
  • make lint exit 0. cargo ndk arm64 check (jni, bluetooth) exit 0. Real-code guard clean; baseline untouched.
  • Not run locally: the full CI board. Not checked on a phone: the icon badge.
  • Pins: intent pins may need a repin from CI's code map if this moved a pinned closure (wallet.history's handler body changed).

🤖 Generated with Claude Code

The owner asked for an email-style unread badge for money coming in
(2026-10-22) and ruled that the count clears when the b0x inbox is opened
(2026-10-23). No push service: the phone already receives online
transfers in the background (Backgrounded::Listening), and the count is
drawn from what Rust verified.

Rust (dsm_sdk):
- client_db::money_received: a notice per verified received transfer,
  written in the same SQLite transaction as its history row
  (ensure_received_history), keyed by the transfer's id so a transfer is
  noticed once however many passes write it; and how far the user has
  looked. A look never goes back and never past a written notice.
- inbox.moneyWaiting (query) and inbox.moneySeen (invoke) answer
  MoneyWaitingResponse: the received transfers waiting, each as the
  history reports it, and the newest notice number.
- sdk::money_waiting::publish hands the host the count and posts
  money.waiting to the page whenever it can have changed: a received
  transfer stored, a look, the poller started in a new process.
- The wallet.history row conversion is now history_info /
  contact_aliases, shared with the inbox list.

Android: MoneyReceivedNotice draws Rust's count as one low-importance
notification ("Money received", no amount, no sender) with setNumber,
taken down at zero; launchers badge the icon from it.

Frontend: the b0x button's count is Rust's; opening the b0x lists the
received transfers and reports them seen. The 8-second transient notice
tally it replaces is removed, with the test that asserted it.

Tests (release, Postgres-backed nodes): money_waiting_tests 2/0 with the
history and poller tests (52/0); control 5/5 green; four mutants each red
at a named assertion (no notice, notice rewritten, look past the newest,
look going back). MoneyReceivedNoticeTest 2/0 (Robolectric). Frontend
68 suites / 425 tests, type-check and lint clean. make lint exit 0;
cargo ndk arm64 check (jni,bluetooth) exit 0; real-code guard clean.
…lines

scripts/sphincs_source_map.py: four lexical references in
storage_routes.rs and wallet_routes.rs moved lines when the history row
conversion became history_info. No reference was added or removed.
Code only: CI's code map of 2b868d0 read every one PIN_STALE in its
code class (the MoneyWaitingResponse arm of the Envelope moved each
closure that touches it). Their 323 evidence tests ran at 2b868d0 in
release and passed; repinned with ci/intent_pins.py against that map.
make requirement-map-intent INTENT_BUILT=android,node: 0 failing rows,
0 failing pins.
…adge

# Conflicts:
#	dsm_client/deterministic_state_machine/dsm_sdk/src/handlers/app_router_impl.rs
#	specs/requirements/INTENT_PINS.tsv
Code only: CI's code map of 443d1e4 (the merge of main into this
branch) read every one PIN_STALE in its code class. Their 324 evidence
tests ran at 443d1e4 in release and passed; repinned with
ci/intent_pins.py against that map. make requirement-map-intent
INTENT_BUILT=android,node: 0 failing rows, 0 failing pins (633).
…adge

# Conflicts:
#	dsm_client/deterministic_state_machine/dsm_sdk/src/handlers/wallet_routes.rs
#	dsm_client/frontend/src/proto/dsm_app_pb.ts
#	specs/requirements/INTENT_PINS.tsv
#	specs/requirements/SPHINCS_CALLERS.tsv
cargo fmt over wallet_routes.rs: history_info now holds main's
AmountDirection body, moved out of the closure and still indented for
it. No code change.
Code only: CI's code map of 190b8fd read every one PIN_STALE in its
code class. Their 324 evidence tests ran at 190b8fd in release and
passed; repinned with ci/intent_pins.py against that map.
make requirement-map-intent INTENT_BUILT=android,node: 0 failing rows,
0 failing pins (633).
@cryptskii
cryptskii merged commit 6b2eb2d into main Oct 26, 2026
27 checks passed
@cryptskii
cryptskii deleted the feat/money-received-badge branch October 26, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant