Commit Graph
15076 Commits
Author SHA1 Message Date
nrobi144andClaude Opus 4.6 746dab51b4 fix(desktop): fix NWC relay connection, disconnect crash, and balance error handling
- Remove premature ensureRelayConnected check — NostrClient connects
  on subscribe/publish via sendOrConnectAndSync
- Fix disconnect crash: use appScope instead of rememberCoroutineScope
  to survive recomposition when nwcConnection goes null
- Surface balance errors/timeouts as snackbars instead of silent swallow
- Add ensureRelayConnected helper to RelayConnectionManager
- Add Phase 2 embedded wallet research doc

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 06:23:05 +03:00
Claude a338574f44 fix(nwc): surface rejected spoof replies in timeout message
When a kind-23195 event arrives signed by someone other than the wallet
service we sent the request to, we now count it on the pending entry and
leave the entry in place so the legitimate reply can still resolve. But
if no legitimate reply arrives and the 30s timeout fires, the user used
to see a generic "Wallet request timed out" — indistinguishable from
"the wallet is just slow", even when an active attacker was forging
replies and dropping the real ones.

Carry the per-request spoof count through to the timeout error message:

  - NwcPaymentTracker.PendingRequest gains an AtomicInteger spoofAttempts.
    onResponseReceived increments it on WrongAuthor.
  - New tracker method spoofAttemptsFor(requestId) reads the count.
  - Account exposes nwcSpoofAttempts() and cleanupNwcRequest() so the
    UI doesn't need to reach into LocalCache.
  - Account.sendNwcRequestToWallet now returns the request event id so
    callers can identify the pending entry.
  - WalletViewModel.launchTimeout takes a () -> HexKey? provider and
    fetches the spoof count when the timeout fires. The error becomes
    "Wallet request timed out — N replies were rejected because they
    were signed by an unexpected key. Your relay may be untrusted."
    Also calls cleanupNwcRequest on timeout to avoid leaking the entry.

Silent on the happy path: a forged reply followed by the real one does
not trigger any user-facing message — the spoof count is discarded with
the matched entry.
2026-05-21 22:29:55 +00:00
Claude b17bb8339e feat(nip82): render bundled assets inside the release card
Replace the placeholder "N assets bundled" line with a real list of
compact rows for each `e`-tagged Software Asset in the release.

Each row loads the referenced asset event id through
`LoadAssetNote` (uses LocalCache first, falls back to
`checkGetOrCreateNote` for ids never seen) and then
`observeNoteEvent<SoftwareAssetEvent>` — which both observes the
LocalCache flow and registers the note with `EventFinder` so the
relay round-trips the missing asset event. When the asset arrives
the row recomposes with MIME, version, optional variant, size,
platform chips, and a Download link to the asset url.

The standalone `RenderSoftwareAsset` card (kind 3063 in a feed or
thread) is unchanged; this only fills out the release detail view.
2026-05-21 21:41:52 +00:00
Claude 3f91cb1689 fix: actually fetch ChannelCreateEvent (kind 40) and widen the relay set
filterMissingChannelsById had an inverted isEmpty() check that emitted
zero filters, so kind 40 was never requested from any relay. Channels
discovered from kind 42 messages stayed as empty stubs unless the
creator also happened to publish a kind 41 metadata update findable on
the same relay — which is why most cards in the Public Chats feed
loaded with no name or picture.

Fix:
- Drop the inverted condition; mapOfSet guarantees non-empty values, so
  emit a RelayBasedFilter for every (relay, channelIds) entry.
- Widen the relay set per channel to include the user's search and
  indexer relay lists. Falls back to DefaultSearchRelayList /
  DefaultIndexerRelayList when those lists are empty.
- Plumb the Account through ChannelFinderQueryState so the assembler
  can read the search/indexer flows. Mirrors EventFinderQueryState.
2026-05-21 21:35:27 +00:00
Claude 73f1e6ae9c fix(onchain-zaps): harden against spoofing, fix re-verify lifecycle, audit cleanup
Addresses the 15 findings from the high-effort code review on top of the
optimistic-attach fix. Notable behavior changes:

- Per-source removal: `Note.removeOnchainZapForSource(txid, pubkey)` only
  drops an entry whose source matches, preventing a spoofed kind:8333 with
  the same txid but a bystander recipient from erasing a legitimate
  CONFIRMED entry. Rejected (txid, sender) pairs are recorded so a fresh
  event id from the same attacker no longer re-flickers into the gallery.

- Sender-only optimistic attach: only the user's own outgoing zap (relay ==
  null path) gets the optimistic UNVERIFIED entry. Incoming zaps render
  only after on-chain verification, so an attacker-controlled `amount` tag
  can't briefly mislead viewers. `claimedSats` is clamped >= 0.

- Reverification across every screen: the chain-tip poller moves from the
  thread screen into `LocalCache.onchainTipHeightFlow` (lazy, shared,
  WhileSubscribed). The onchain-zap gallery itself drives reverification
  whenever it composes with non-CONFIRMED entries — covers home feed,
  notifications, profile, channel and single-note views. The gallery
  observes the tip flow and the note's zap state, so new arrivals while
  the gallery is on screen are picked up too.

- Verifier fan-out + parallelism: re-arrivals skip the verifier launch
  when every target note already holds a CONFIRMED entry for the txid.
  `reverifyOnchainZapsForNote` now runs verifier calls in parallel,
  capped by a 4-permit semaphore.

- Monotonic upgrade based on explicit `OnchainZapStatus.level` instead of
  `ordinal`, with a unit test locking the order. Same-level entries with a
  larger `verifiedSats` are accepted so a stale indexer estimate isn't
  permanent.

- Cancellation propagation: `catch (Throwable)` rethrows
  `CancellationException` in `verifyAndUpgradeOnchainZap` so screen-scoped
  callers tear down cleanly.

- Memory visibility: `Note.onchainZaps` is `@Volatile` since the
  reverification driver reads it on Main while the IO scope writes.
2026-05-21 21:34:36 +00:00
Claude 8ce0edeb2c fix(nwc): verify response author against expected wallet-service pubkey
The previous commit dropped `authors` and `#p` from the relay subscription
filter to match Primal's interop shape. Without those, the relay will
deliver any signed kind-23195 event that carries our request id in `#e`,
so an attacker who can observe the request on the relay could forge a
"response" with their own keypair: Amethyst would happily derive a shared
secret from `event.pubKey` (the attacker), decrypt the payload, and
display attacker-controlled balance/transaction data. Even worse,
`paymentTracker.onResponseReceived` removed the pending entry on first
match — so the legitimate wallet reply that followed was silently dropped.

Move the author check from the relay layer into NwcPaymentTracker:

  - `registerRequest` now requires the expected wallet-service pubkey
    (read from the request's `p` tag). LocalCache extracts it during
    `consume(LnZapPaymentRequestEvent)` and refuses to register if the
    request has no `p` tag.
  - `onResponseReceived` takes the response author and returns a sealed
    MatchResult of NoMatch / WrongAuthor / Matched. A WrongAuthor result
    leaves the pending entry in the map so the legitimate response can
    still resolve it.
  - Android LocalCache and DesktopLocalCache both adopt the new API and
    log a warning on suspected spoof attempts.

End-to-end the response is still encrypted under the per-connection shared
secret, so this is a second layer of defence rather than the only one,
but matching the author keeps a forged kind-23195 from consuming the
pending slot and DoSing the legitimate reply.
2026-05-21 21:30:16 +00:00
Claude 9ac32a3a8e feat(nip82): software application visualization + apps feed
Wire NIP-82 Software Applications (kind 32267), Releases (kind 30063)
and Assets (kind 3063) into the Quartz event model and surface them
through a dedicated rendering path in Amethyst.

Quartz: extend the existing experimental NIP-82 builders with topic
(`t`) and NIP-34 app-link (`a`) helpers, and add a small detector
(`isNip82SoftwareRelease`/`asSoftwareRelease`) so kind 30063 events
can be disambiguated from NIP-51 ReleaseArtifactSetEvent at the
renderer layer. Pin behavior with unit tests covering build paths,
disambiguation, and the real-world Amethyst NIP-82 description event.

Amethyst: add modern card visualizations for each kind — application
header with icon/screenshots/platforms/topics/links, release header
with channel pill and bundled-asset count, and asset row with MIME,
size and platforms — and dispatch to them from both `NoteCompose`
and `NoteMaster` (`ThreadFeedView`).

A new "Apps" feed (left nav drawer) mirrors the Picture Feeds shape:
`SoftwareAppsFeedFilter` reads kind 32267 from `LocalCache`, a
`PerUserEoseManager`-backed subscription pulls applications and
releases from outbox relays, and a dedicated screen renders them in
a `LazyColumn` of `RenderSoftwareApplication` cards.
2026-05-21 21:10:18 +00:00
Claude ee0b658401 feat: keep Public Chats scrolled to top when new top items arrive
When the first item of either the pinned or unpinned block changes,
animate-scroll back to index 0 if the user was at or near the very top.
This mirrors the pattern from ChatFeedView so a new chat bubbling up
doesn't leave the user one row below it.
2026-05-21 21:09:49 +00:00
Claude 5cdf69bb83 fix: move pin badge to cover-image corner and add section gap
- Pin glyph moved from top-right (overlapping LikeReaction/ZapReaction)
  to top-left, inside a surface-tinted circular badge that overlays the
  cover image corner. Reads against any cover image and frees the
  reaction buttons from being eclipsed and hit-tested through.
- 8dp Spacer item inserted between the pinned items{} and unpinned
  itemsIndexed{} blocks, only when both sides have content, so the
  section boundary reads as a section break instead of just another row.
2026-05-21 20:52:55 +00:00
Claude aca4529d72 fix(nwc): drop authors and #p from response subscription filter
Some wallet services and relays don't produce or index the `p` tag on NIP-47
response events the way the spec implies for ephemeral kinds, which made
Amethyst's strict relay-side filter (kinds + authors + #e + #p) match
nothing while looser clients (Primal uses just kinds + #e) work against the
same connection string.

Reduce the relay filter to the same shape Primal uses. The request event id
in #e is a unique 32-byte identifier, so the false-positive rate is
effectively zero, and the wallet's identity is still authenticated end-to-end
by NIP-04 decryption against the per-connection shared secret — the relay
filter was never the security boundary.

NWCPaymentQueryState no longer needs `fromServiceHex` or `toUserHex`; remove
them and propagate the simpler ctor through callers.
2026-05-21 20:49:31 +00:00
Claude ba43edef57 feat: show pin icon on followed Public Chat cards
Overlay a small push-pin glyph at the top-right of pinned channel cards
so the user can tell at a glance why those rows are at the top. The
underlying ChannelCardCompose is untouched; only the row wrapper changes
to a Box to host the overlay.
2026-05-21 20:45:31 +00:00
Vitor PamplonaandGitHub 7a6155db05 Merge pull request #3032 from vitorpamplona/claude/jolly-cray-6vKga
Surface NIP-47 wallet response errors to user
2026-05-21 16:45:11 -04:00
Claude dd203a5537 fix(onchain-zaps): attach optimistically and re-verify on tip change
Outgoing onchain zaps never appeared in the sender's thread view because
LocalCache.consume(OnchainZapEvent) ran the chain verifier milliseconds
after the broadcast — before the backend's indexer had picked up the
transaction. The resulting TX_NOT_FOUND rejection skipped addOnchainZap,
and the duplicate guard blocked re-verification when the same event
later echoed back from relays.

Attach kind:8333 entries optimistically as UNVERIFIED with the claimed
amount so the sender sees their zap on the thread immediately, then
upgrade to PENDING/CONFIRMED as the chain catches up. Hard rejections
(zero-paid-to-recipient, missing tags) drop the entry; transient
TX_NOT_FOUND keeps it UNVERIFIED for a later retry. ThreadScreen now
re-verifies non-confirmed entries on view and again whenever the chain
tip advances.
2026-05-21 20:07:46 +00:00
Claude a1cca4a748 fix(nwc): flush REQ before publishing event and surface silent errors
Two problems caused wallet timeouts that affected only Amethyst users:

1. The NWC subscription went through a 500 ms BundledUpdate debounce while
   the request event was published immediately. Kind 23195 responses are
   ephemeral, so if the wallet replied faster than the REQ reached the
   relay, the reply was dropped with no replay. Add a synchronous
   subscribeAndFlush() that bypasses the bundler so the REQ is queued on
   the WebSocket before the EVENT.

2. Three failure paths were swallowed: decryption returning null, an
   unknown response subtype, and a response arriving with no matching
   pending request. Users saw "Wallet request timed out" with no clue.
   Surface a specific error in WalletViewModel for the first two, and
   log a warning in LocalCache for the third.
2026-05-21 20:05:02 +00:00
Vitor PamplonaandGitHub a42dee5e12 Merge pull request #3029 from davotoula/i18n-cs-de-pt-sv-and-correct-0-entries-for-english
Calendar Translations  (cs, de, pt-BR, sv) and skill update
2026-05-21 15:56:58 -04:00
Vitor PamplonaandGitHub 766b161363 Merge pull request #3030 from vitorpamplona/claude/intelligent-bohr-Lakt0
Enforce minimum on-chain zap amount and update default zap presets
2026-05-21 15:56:45 -04:00
Claude 9dcce46f67 feat(zaps): lower default zap amounts and add 1k-sat onchain floor
- Lightning quick-zap defaults: 100/500/1000 → 21/50/100 sats
- Onchain quick-zap default: 10k → 5k sats
- Block onchain zaps below 1000 sats in OnchainZapSendDialog so users
  aren't quietly creating transactions where miner fees dwarf the gift.
  The protocol dust threshold (330 sats) still applies underneath.
2026-05-21 19:43:08 +00:00
Claude 13c0e26de3 refactor: filter followed chats out of the main list before rendering
Avoids creating empty LazyColumn item slots for followed channels that
were skipped at render time.
2026-05-21 19:28:30 +00:00
nrobi144andClaude Opus 4.6 704c8a3e87 fix(desktop): center wallet empty state vertically in column
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 13:50:46 +03:00
davotoula ec2f621772 i18n(sv): apply Swedish translation review
Native-speaker review surfaced consistency and wording fixes:

- Calendar collection naming: use "kalender" for action labels
  (New / Edit / Delete) matching the English source's user-facing
  simplification; use "kalenderlista" only in the empty-state body
  and route header (matches route_calendar_collections =
  "Kalenderlistor" and English's "Calendar lists"). Same hybrid as
  Czech, pt-BR, and German.
- Capitalize "Nostr" in calendar_share_nostr.
- "on-chain-backend" over "chain-backend" in wallet_onchain_no_backend
  (rest of batch already uses the on-chain- compound).
- "blockchainen" (definite form, matches existing ots_info_description)
  over bare "blockchain" in wallet_onchain_public_dialog_body.
- Tighten Lightning comparison: "större än för Lightning" over
  "större än Lightning" (the bare comparison was elliptical).
- wallet_onchain_public_dialog_body: clearer privacy wording
  ("fyll på från icke-privata konton och skicka tillbaka medel dit"
  over "fyll på och töm från och till"); make antecedent of "spendera"
  explicit ("spendera medlen" over ambiguous "spendera dem").
- Symmetric RSVP pair: "Kommer" / "Kanske" / "Kommer inte"
  (instead of mixed "Kommer" / "Kanske" / "Kan inte"). Matches the
  same fix applied for German and pt-BR.

Note: calendar_rsvp_section / calendar_rsvp_none already use the
established Swedish "OSA" acronym (matching kind_appt_rsvp =
"OSA för möte"), so no rename needed unlike de/pt-BR where the
section noun ("Zusagen"/"Confirmações") had to be replaced.
2026-05-21 09:52:51 +02:00
davotoula 44f8d1bfa8 i18n(de): apply German translation review
Native-speaker review surfaced consistency and wording fixes:

- Calendar collection naming: use "Kalender" for action labels
  (New / Edit / Delete) matching the English source's user-facing
  simplification; use "Kalenderliste" only in the empty-state body
  and route header (matches route_calendar_collections =
  "Kalenderlisten" and English's "Calendar lists"). Same hybrid as
  Czech and pt-BR.
- On-Chain hyphenation: "On-Chain-Backend" over "Chain-Backend"
  in wallet_onchain_no_backend; "Stattdessen On-Chain senden" over
  "Stattdessen on-chain senden" in send_onchain_instead. Matches the
  rest of the batch's German compound-noun convention.
- "Nowhere Event" over "Nowhere Veranstaltung" — aligns with the
  English source's all-English Nowhere brand prefixes (Drop / Forum /
  Petition were already English).
- "RSVPs" / "Antworten" over "Zusagen" for calendar_rsvp_section and
  calendar_rsvp_none — section also includes "Vielleicht" and "Komme
  nicht", so "Zusagen" was misleading. Aligns with the existing
  "RSVP für Termin" usage in kind_appt_rsvp.
- Symmetric RSVP pair: "Komme" / "Vielleicht" / "Komme nicht"
  (instead of mixed "Komme" / "Vielleicht" / "Kann nicht").
- wallet_onchain_public_dialog_body: clearer privacy wording
  ("lade auf und sende zurück" over "befülle und entleere von und
  zu"); make antecedent of "ausgeben" explicit ("die Mittel
  ausgeben" over ambiguous "es").
- contact_list_containing_label: rephrase to dodge German declension
  for n=1 ("Folgenliste (%1$d Nutzer):" over "Folgenliste mit %1$d
  Nutzern:" which is dative-plural-only).
2026-05-21 09:50:09 +02:00
davotoula db8791f5fc i18n(pt-BR): apply Brazilian Portuguese translation review
Native-speaker review surfaced consistency and wording fixes:

- Capitalize "Nostr" in calendar_share_nostr.
- "no blockchain" (masculine, matches ots_info_description) over
  "na blockchain" in wallet_onchain_public_dialog_body.
- Calendar collection naming: use "calendário" for action labels
  (New / Edit / Delete) matching the English source's user-facing
  simplification; use "lista de calendário" only in the empty-state
  body and route header (matches route_calendar_collections = "Listas
  de calendário" and English's "Calendar lists"). Same hybrid we
  resolved for Czech.
- Symmetric RSVP pair: "Vou" / "Talvez" / "Não vou" (instead of mixed
  "Vou" / "Talvez" / "Não posso").
- "RSVPs" over "Confirmações" for calendar_rsvp_section and
  calendar_rsvp_none — the section also includes "Talvez" / "Não vou"
  responses, so "Confirmações" was misleading. Aligns with the
  existing "RSVP do Compromisso" usage in kind_appt_rsvp.
- wallet_onchain_public_dialog_body: clearer privacy wording
  ("abasteça a partir de contas não privadas e envie os fundos de
  volta" over "abasteça e esvazie a partir de e para"); make antecedent
  of "gastar" explicit ("gastar os fundos" over ambiguous "-lo").
- calendar_empty_feed: reorder to drop trailing "ainda" — "Ainda não
  há eventos…" reads more naturally than "…ainda" at end.
- calendar_collection_delete_confirm_message: align tenses (future +
  future "será removido / não serão excluídos" over future + present).
- "backend on-chain" over "backend de chain" in wallet_onchain_no_backend.

Note: contact_list_* strings use the semantically-correct "seguidos"
(people you follow, per kind:3), even though existing
follow_list_selection / follow_sets use the inverted "seguidores".
Recommended follow-up: clean up legacy strings to match.
2026-05-21 09:45:09 +02:00
davotoula a877ef7ec7 i18n(cs): apply Czech translation review
Native-speaker review surfaced several consistency and wording fixes
in the Czech translations from this branch:

- Use "Kanál" for "feed" (matches existing feed/home_feed/global_feed).
- Use "sats" not "satoshi" (matches new_amount_in_sats / amount_in_sats).
- Match Lightning sibling string's "odstraníte" (vs "odeberete") on
  quick_zap_amounts_onchain_explainer.
- Capitalize "Nostr" in calendar_share_nostr.
- Calendar collection naming: use "kalendář" for actions (New / Edit /
  Delete) matching the English source's user-facing simplification;
  keep the disambiguating "seznamy" wording for the empty-state body
  and title (matches route_calendar_collections = "Seznamy kalendářů"
  and English's "No calendar collections yet").
- Rephrase contact_list_containing_label to dodge Czech count
  inflection ("Seznam sledování (%1$d):" rather than "obsahující %1$d
  uživatelů" which is only correct for n ≥ 5).
- Wallet privacy dialog: "vkládejte do peněženky a vybírejte z ní"
  (deposit/withdraw verbs) over "plňte a vybírejte"; "váš nsec" (masc.
  inanimate) over "vaše nsec"; "nesoukromých účtů" over "neprivátních".
- "on-chain backend" over "chain backend" in wallet_onchain_no_backend.
2026-05-21 09:19:12 +02:00
davotoula 2d3a2582ee docs(skill): catch dead quantity="zero" entries in find-missing-translations 2026-05-21 08:45:38 +02:00
davotoula 898dec73b9 i18n: special-case count=0 at call site for calendar day a11y label 2026-05-21 08:45:37 +02:00
nrobi144andClaude Opus 4.6 4d95b7ed8a fix(desktop): adapt wallet screen to upstream AccountManager API changes
clearNwcConnection and setNwcConnection now require npub param and are
suspend functions. ConnectWalletDialog validates URI prefix inline
before firing the async connect callback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:36:55 +03:00
davotoula 4d4d81edbd translate calendar + on-chain wallet strings to cs, de, pt-BR, svclose remaining translation gaps in cs, de, pt-BR, sv
Translate the plurals and strings missed by the first pass
2026-05-21 08:33:56 +02:00
nrobi144andClaude Opus 4.6 5bf4db4582 fix(desktop): wire zap types, relay hints, and add testing sheet
- Map Desktop ZapType (Public/Private/Anonymous) through to
  LnZapEvent.ZapType and pass to ZapAction.fetchZapInvoice
- Add relayHint + authorRelayHint params to NoteActionsRow;
  FeedScreen now passes Note.relayHintUrl() for reactions/reposts
- Replace metadata preload TODO with design rationale comment
- Add 70-test manual testing sheet for wallet & zapping features

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:39 +03:00
nrobi144andClaude Opus 4.6 a15ce33807 refactor(desktop): use dialogs for wallet actions, center home content
Send, Receive, and Connect are now AlertDialogs instead of full-screen
sub-pages. The wallet home content is centered with widthIn(max=360.dp)
for a polished desktop look. Dialogs are the native desktop pattern
(consistent with zap dialog, bookmark dialog, tor settings dialog).

- ConnectWalletDialog: NWC URI input with paste + validation
- SendDialog: BOLT11 invoice input with paste + progress
- ReceiveDialog: amount/description input, transitions to show
  generated invoice with copy button
- Home content: centered balance card + action buttons + connection info

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:39 +03:00
nrobi144andClaude Opus 4.6 420a10df50 fix(desktop): subscribe before publish in NWC RPC to avoid race condition
getBalance and makeInvoice were publishing the request event before
subscribing for the response. Fast wallet responses (like get_balance)
would arrive before the subscription was active, causing timeouts.

Fix: subscribe first via onSubscribed callback in waitForGenericResponse,
then publish. The original payInvoice wasn't affected because Lightning
routing takes long enough for the subscription to be ready.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:38 +03:00
nrobi144andClaude Opus 4.6 ed06bb2267 test(desktop): add wallet and zapping test coverage
- ZapDialogLogicTest: 12 tests for formatSats, DEFAULT_ZAP_AMOUNTS,
  ZapType enum (labels, descriptions, entries)
- NwcPaymentHandlerTest: 18 tests for PaymentResult, BalanceResult,
  InvoiceResult sealed types, NWC connection validation, response
  event structure
- NwcRpcIntegrationTest: 5 full round-trip tests using real crypto
  (pay_invoice, get_balance, make_invoice request/response cycles,
  NWC URI -> client pipeline)
- Make formatSats and DEFAULT_ZAP_AMOUNTS internal for test access

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:38 +03:00
nrobi144andClaude Opus 4.6 7bd5fb3122 feat(desktop): wire NWC wallet operations and AccountManager integration
- Wire AccountManager.setNwcConnection() and clearNwcConnection() into
  wallet column for persistent connect/disconnect
- Implement NwcPaymentHandler.getBalance() via NIP-47 get_balance RPC
- Implement NwcPaymentHandler.makeInvoice() via NIP-47 make_invoice RPC
- Add generic waitForGenericResponse() helper for NWC RPC operations
- Auto-fetch balance on wallet column load via LaunchedEffect
- Wire receive screen to generate real invoices via NWC
- All wallet column features now functional (no blocking TODOs)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:38 +03:00
nrobi144andClaude Opus 4.6 8313f0cf12 feat(desktop): add wallet column and enhanced zapping UX
Phase 1: Wallet deck column with NWC integration
- Add DeckColumnType.Wallet with full deck system integration
  (AppDrawer, ColumnHeader, DeckState persistence, MenuBar)
- WalletColumnScreen with 4 sub-screens: Home (balance + actions),
  Connect (NWC URI paste), Send (pay BOLT11), Receive (create invoice)
- Uses existing NwcPaymentHandler for payment execution

Phase 2: Zapping UX improvements
- Upgrade ZapAmountDialog with zap type selection
  (PUBLIC/PRIVATE/ANONYMOUS via FilterChips)
- Add custom amount input alongside preset chips
- One-click zap: left-click sends default amount via NWC,
  right-click opens custom zap dialog
- Configurable zap amounts parameter (no longer hardcoded)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 09:31:38 +03:00
Claude 3915e2b36f refactor: render pinned followed chats as a separate LazyColumn section
Instead of sorting followed chats to the top of the existing feed (which
hides them when the feed filter or TopFilter excludes them), render them
in a dedicated items{} block above the rest of the feed and skip them in
the main itemsIndexed{} block. This keeps the feed filter, feedKey, and
screen watchers untouched.
2026-05-21 02:20:40 +00:00
Claude 68f0ded7d5 feat: pin followed public chats to the top of the Public Chats feed 2026-05-21 00:45:01 +00:00
Vitor PamplonaandGitHub 6cd4de51aa Merge pull request #3028 from vitorpamplona/claude/fix-send-split-mode-AE6gg
fix: keep Send button visible in onchain zap split mode
2026-05-20 20:37:47 -04:00
Claude 465f3687c1 fix: keep Send button visible in onchain zap split mode
The OnchainZapSendDialog promised a sticky bottom Send button, but the
inner scrollable Column had no weight and the outer Column did not fill
the sheet height. In split mode, SplitsRecipientSection grew the content
past the sheet viewport and pushed the Send button off-screen.
2026-05-21 00:02:38 +00:00
Vitor PamplonaandGitHub 6efee9f1f2 Merge pull request #3027 from vitorpamplona/claude/add-mention-notifications-PIhPf
feat(notifications): include public chat mentions in feed
2026-05-20 19:51:18 -04:00
Claude 39eedeeb17 feat(notifications): include public chat mentions in feed
Add ChannelMessageEvent.KIND (NIP-28 kind 42) to the Notifications feed's
allow-list so channel messages that p-tag the user surface in the in-app
Notifications screen, alongside text-note mentions and DMs.

The relay subscription already requests kind 42 with #p (see
FilterNotificationsToPubkey.NotificationsPerKeyKinds), and push notifications
already route ChannelMessageEvent through notifyMention. Only the in-app
feed filter was rejecting them at the kind check; the existing
tagsAnEventByUser gate handles the per-kind "is this for me" rule via the
BaseNoteEvent branch (reply-to-me, cited-author, citation-in-content).
2026-05-20 23:21:47 +00:00
Vitor Pamplona e0abc4a224 v1.11.0 v1.11.0 2026-05-20 18:59:05 -04:00
Vitor PamplonaandGitHub ee67ba6408 Merge pull request #3025 from vitorpamplona/claude/plan-onchain-zap-events-t1cPq
feat: tighten the zap UI
2026-05-20 18:28:50 -04:00
Vitor Pamplona 7415b1db39 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-20 18:27:15 -04:00
Vitor PamplonaandGitHub f19a47007e Merge pull request #3024 from vitorpamplona/claude/fix-illegal-argument-exception-H4omh
fix(ui): restore outlined visual on EditPostView + ForwardZapTo TextF…
2026-05-20 18:26:45 -04:00
Claude 55e536dfd0 feat: tighten the zap UI
ReactionsRow zap popup
- Merge the Lightning and on-chain chips into a single FlowRow so all
  amounts wrap together instead of stacking on two lines.
- Drop the on-chain row's own Tune (settings) button; the Lightning
  row's Tune is the single entry point to the settings screen.

UpdateZapAmountDialog
- Reorder sections: Quick Zap Amounts, Zap Privacy, Quick On-chain Zap
  Amounts, Nostr Wallet Connect — privacy now sits next to the
  Lightning amounts it actually applies to, and on-chain (which has no
  privacy concept) is grouped further down.

OnchainZapSendDialog
- Move the preset amount chips above the sats text field in
  AmountSection so the user sees the quick picks first and the
  free-form input second.

Defaults
- DefaultOnchainZapAmounts is now [10_000] (just one chip) so a fresh
  install / first-upgrade shows 4 chips total in the popup (3
  Lightning defaults + 1 on-chain default), not 6. kotlinx
  serialization defaults handle the back-compat for existing users
  who never set their on-chain choices explicitly.
2026-05-20 22:26:34 +00:00
Claude 05d958d182 fix(ui): restore outlined visual on EditPostView + ForwardZapTo TextFields
Migrating the mention TextFields to BasicTextField(state) routed them
through ThinPaddingTextField, which uses TextFieldDefaults.DecorationBox
(filled, surfaceVariant container). That replaced the original
OutlinedTextField look — visible rounded border + transparent inside —
with a filled gray rectangle. On ForwardZapTo the change was especially
visible since the original outlined border was the only visual cue that
the search row was an input.

Add OutlinedThinPaddingTextField, a sibling of ThinPaddingTextField that
wraps the same TextFieldState pipeline with
OutlinedTextFieldDefaults.DecorationBox + Container. Same thin padding,
same onTextChanged/inputTransformation/outputTransformation surface, but
genuinely outlined (notched label, transparent inside).

  - ForwardZapTo: switch to the new component, drop the
    focused/unfocusedIndicatorColor=Transparent overrides — defaults give
    the proper outlined border the original OutlinedTextField had.
  - EditPostView.MessageField: switch to the new component and use
    OutlinedTextFieldDefaults.colors(focusedBorderColor=Transparent,
    unfocusedBorderColor=Transparent) to keep the inner border invisible
    so the existing 1dp Modifier.border(surface, RoundedCornerShape(8.dp))
    remains the only visible frame, matching the original.
2026-05-20 22:21:20 +00:00
David KasparandGitHub a4310317a0 Merge pull request #3017 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-21 00:04:24 +02:00
Crowdin Bot 40eb0f0b61 New Crowdin translations by GitHub Action 2026-05-20 22:02:32 +00:00
davotoula b04cf3aedc i18n: convert calendar_reminder_settings_lead_choice to <plurals>
The lead-time chip label "%1$d min" pairs a count with a noun, which
inflects in Slavic / Baltic languages. Switch it from <string> to
<plurals> in the default locale and in the three locales that already
have a translation (pl-rPL, zh-rCN, hu-rHU), and update the single
caller in CalendarReminderSettingsScreen to use pluralStringResource.

The abbreviation "min" / "min." / "perc" / "分钟" doesn't decline with
the count in any of these specific languages, but the resource shape
is now correct for future locales that do (e.g. Russian, Ukrainian).
2026-05-20 23:59:28 +02:00
Vitor Pamplona 924101c3f4 Fixes follow list rendering in the top note 2026-05-20 17:56:54 -04:00
Vitor PamplonaandGitHub 0cc435aa02 Merge pull request #3023 from vitorpamplona/claude/add-notecompose-contactlist-sFMff
Add Contact List display and navigation screen
2026-05-20 17:49:40 -04:00