Files
amethyst/commons
Claude 8fa636bbe8 refactor(model): lazy-pinned addressable notes on User via UserContext
Pins each per-user replaceable note to the User's lifetime so weak-ref
eviction from LocalCache.addressables can't lose them — same fix the
NIP-65 / DM relay list notes already had, generalised so adding new
pinned kinds is a one-liner.

Background: LocalCache.addressables is a LargeSoftCache<Address,
AddressableNote> backed by WeakReference. Without a strong reference
somewhere, an addressable note shell (and any event loaded into it) can
be cleared on any GC cycle even though it was successfully delivered.
The User constructor already held nip65RelayListNote / dmRelayListNote
fields exactly to defeat this for kinds 10002 and 10050. kind:10019
(NutzapInfoEvent) had no such pin, so the zap picker's "does this user
accept nutzaps?" check would silently return null for an evicted note —
the chip never showed even when the recipient had actually published.

This refactor:
1. Adds `UserContext` — a one-method `fun interface` exposing
   `addressableNote(addr): Note`. User holds it for life; LocalCache
   implements it via a single instance bound to ::getOrCreateAddressableNoteInternal.
2. Converts the three per-user pinned notes (nip65 / dm / nutzapInfo)
   to `by lazy` fields backed by the context. Each is resolved the
   first time it's read and then held by the User's strong reference
   until the User itself is collected. `by lazy`'s default SYNCHRONIZED
   mode handles concurrent reads from the zap picker + wallet state.
3. Adds typed accessors on User: nutzapInfo(), acceptsNutzaps(),
   nutzapMints(), nutzapP2pkPubkey() — mirrors the existing
   authorRelayList() / dmInboxRelayList() shape.
4. CashuWalletState.peekNutzapTarget now reads via
   `cache.getOrCreateUser(recipientPubKey).nutzapInfo()` instead of
   touching the cache's addressable map directly.

Tradeoffs vs the eager-constructor approach:
- No upfront allocation for kinds the screen never reads.
- Adding a new pinned kind (mute list, blocked relays, bookmark list)
  is one `by lazy { context.addressableNote(...) }` line in User —
  no constructor-signature churn across call sites.
- User now depends on a narrow `UserContext` interface; test fakes are
  a one-liner: `User(hex) { addr -> Note(addr.toValue()) }`.

Migration:
- Single User constructor call site (LocalCache.getOrCreateUser) updated.
- Two existing test fakes (NoteOnchainZapTest, SearchResultSorterTest)
  switched to the SAM-lambda form.
- No external behaviour change — the public `nip65RelayListNote` /
  `dmRelayListNote` fields keep the same names and types, so the few
  consumers (RelayFeedViewModel, ChatNewMessageViewModel) need no edits.

https://claude.ai/code/session_01MdWddiar819f8XYt5N8BjP
2026-05-27 15:17:43 +00:00
..