Commit Graph
14450 Commits
Author SHA1 Message Date
Claude fca99b68be chore: scope DM diagnostics to gift-wrap relays only
The connection listener fires for every relay the app dials (hundreds, under
the outbox model), so logging connect/auth/notice unconditionally drowned the
DMPagination tag in unrelated relay traffic.

Restrict connect / disconnect / cannotConnect / AUTH / NOTICE / OK(fail) lines
to relays on the gift-wrap path — learned the first time we send a kind:1059/1060
REQ to a relay or receive a gift wrap from it. EVENT/EOSE/CLOSED were already
scoped by kind/subId.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 17:10:27 +00:00
Claude 3ca40ca5ef chore: add DM relay diagnostics timeline (tag: DMPagination)
The reported symptom — messages inside the 7-day window never appear, and the
first EOSE takes ~136s on a single-event account — points at the relay/connection
path, not the time filter. Nothing currently logs where that time goes or whether
a relay is silently rejecting the query (CLOSED "auth-required"/"restricted").

Add DmRelayDiagnosticsLogger, a debug-only RelayConnectionListener that folds the
gift-wrap loading timeline into the DMPagination tag with elapsed-time prefixes:

- connecting / connected (ping) / disconnected / cannotConnect per relay
- REQ sent for gift-wrap subscriptions (kind:1059/1060), with the command
- AUTH challenge, NOTICE, and CLOSED (for gift-wrap subs) — the silent-failure tells
- gift-wrap EVENT arrivals (relay, sub, createdAt) and their EOSE

Wired in AppModules next to the other debug loggers.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 16:28:50 +00:00
Claude 65b5ad48f6 chore: log cold-boot DM load start and completion
Previously only the boot window open and per-assembly filter were logged; the
completion of the initial cold-boot load was effectively invisible (the EOSE
log was gated behind the scroll-only loadingMore flag, and newEose can't tell a
real EOSE from a live event because the base listener funnels both into it).

Install a custom SubscriptionListener in newSub so we can distinguish a real
EOSE and count arriving gift wraps:

- "cold boot: … opening gift-wrap subscription, starting to load messages"
- "cold boot: … initial load complete — first EOSE from <relay> after Nms,
  M gift wrap(s) received so far"

Boot timing/count state is reset per subscription and cleared on endSub.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 15:48:14 +00:00
Claude b6e3716711 chore: add DMPagination debug logs for the DM time window
Adds Log.d("DMPagination") tracing so the boot window and scroll-driven
backfill can be watched live in logcat:

- initial window opened per account (with depth in days)
- each updateFilter assembly (window `since` + depth + relays)
- loadMore widening the window (old -> new floor, depth before/after)
- EOSE clearing the loadingMore flag (transition only, not every event)
- the rooms list reaching its end (triggered vs skipped-already-loading)

Filter logcat by tag `DMPagination` to follow the whole flow.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 15:27:41 +00:00
Claude 5436ac07be Merge remote-tracking branch 'origin/main' into claude/relay-message-pagination-LMqSQ 2026-05-30 15:15:27 +00:00
Claude 43744e53ad feat: bound DM boot loading to a time window with scroll-to-load-more
The always-on gift-wrap subscription (AccountGiftWrapsEoseManager) had no
lower bound on first boot: `since` came purely from the per-relay EOSE
cursor, which is null on a cold start, so every DM relay dumped the account's
entire NIP-17 history at once — all of which then had to be unwrapped and
NIP-44 decrypted before the messages list felt usable.

Replace that with a per-account time window:

- New `TimeWindowPagination` primitive (commons): tracks a moving `since`
  floor, opens a small window at boot, widens backward one step per
  `loadMore()`. The subscription stays open so live messages still stream in
  regardless of the window.
- `AccountGiftWrapsEoseManager` now requests gift wraps from the window floor
  instead of the EOSE cursor, exposes `loadMore(user)` and a `loadingMore`
  flag, and clears the flag on EOSE.
- The rooms list (`ChatroomListFeedView`) widens the window when scrolled near
  the end and shows a loading footer while the next window loads. It
  re-evaluates as the list grows so a near-empty first screen keeps filling.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 14:43:45 +00:00
Vitor PamplonaandGitHub ff4b4323d1 Merge pull request #3108 from vitorpamplona/claude/amazing-volta-UJrwo
Unify zap amount chips and add cashu reload flow
2026-05-30 10:33:33 -04:00
Claude d6640be62c refactor(topup/zap): address audit cleanups — pin send mint, dedupe scrub, share clipboard, scope rebuilds
R2 — sendNutzap gains an optional preferredMintUrl; the Top-up screen passes the
just-funded selectedTarget so the nutzap spends from THAT mint instead of whichever
shared mint holds the most (which could leave the top-up sitting idle). Falls back
to the best-balance pick when the preferred mint isn't a valid shared target.

R5 — meltToLightning gains skipScrub; rebalance() (which already scrubbed the
source for its coverage check) passes it to drop the redundant second NUT-07
/checkstate round-trip. The Top-up screen's wallet collector now projects to the
per-mint balance map + distinctUntilChanged, so unrelated wallet activity (an
inbound redeem, a scrub, a token for another mint) no longer re-runs the whole
balances/targets/sources rebuild on every global tokenEntries emission.

R7 — replace ReloadMintScreen's hand-rolled copyToClipboard with the shared
Clipboard.setText helper (drops the android ClipData/ClipboardManager imports);
document the keys-only observed values in the reactive railCapability block so a
future reader doesn't delete them as "unused" and silently break the live rail
loading + relay fetch.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 14:13:26 +00:00
Claude 93648085f9 fix(zap settings): de-dupe preset amounts so drag-reorder + chip keys stay valid
addAmount() allowed adding the same amount twice; two preset chips then share the
same key(amount) (Compose duplicate-key hazard) and the drag-reorder's
indexOf(amount) resolves to the wrong chip. De-dupe on add.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 02:16:39 +00:00
Claude 380122bcb5 fix(topup): checkpoint the instant funds leave the wallet, not after the whole flow
Audit found the toppedUp checkpoint was still too coarse: it was set only AFTER
rebalance() / the LN mint fully returned. But funds leave the wallet mid-flow —
the source melt (rebalance) and the invoice payment (Lightning) both happen before
the poll/completeMintFromLightning steps that can throw. A failure there left
toppedUp=false, so "Try again" re-ran the whole move and spent a second time.

- CashuWalletState.rebalance gains an onFundsMoved callback fired immediately after
  the melt succeeds; the VM sets toppedUp there.
- The Lightning path sets toppedUp the moment the invoice is confirmed paid, before
  issuing ecash.

Either way, once money has moved a retry can only re-send / resume — it can never
move funds again. (The paid-but-unissued quote remains recoverable via the pending
quote banner.)

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 02:07:43 +00:00
Claude 2c02795e45 feat(zap popup): react to lnAddress + cashu/nutzap info loading so rails appear live
The zap popup computed rail availability once from a synchronous snapshot, so a
recipient whose lnAddress (kind:0) or nutzap info (kind:10019) hadn't loaded yet —
or before our own cashu wallet finished loading — showed no Lightning/cashu logo
and never updated.

Now it observes those inputs and recomputes railCapability as they arrive:
- observeUserInfo(author) → the Lightning logo appears when the lnAddress loads.
- observeNoteEvent<NutzapInfoEvent>(author.nutzapInfoNote) + the cashu wallet's
  mints/tokenEntries flows → the cashu logo appears when the recipient's kind:10019
  and our proofs load.

The observers also trigger the relay fetch, so a not-yet-seen lnAddress / kind:10019
gets pulled in while the popup is open.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 01:02:31 +00:00
Claude 1e9b9fd295 fix(zap settings): fix chip drag (pointerInput outside the drag transform), regroup chip, header amount
- Drag-and-drop now works: pointerInput was nested inside the graphicsLayer
  translation, so the layer moving under the finger corrupted the per-frame drag
  deltas. Moved the gesture outside the transform.
- Preset chip regrouped to match the popup: outlined track, default rail + amount
  in a highlighted thumb, alternatives as quiet mono icons, then the X — instead
  of everything mashed together.
- Top-up screen header now shows the zap amount between the cashu symbol and the
  arrow (you → cashu · N sats → recipient).

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:45:23 +00:00
Claude 67d6135ee7 fix(topup): never re-move funds on retry; wait for proofs before zapping
Bug: a top-up that moved funds but then failed at the nutzap (the freshly minted
proofs hadn't landed in local state yet → "No proofs available") left a Failed
state; tapping "Try again" re-ran the WHOLE pipeline and moved the funds a second
time — two transfers for one zap.

- Add a `toppedUp` checkpoint set the moment funds land at the target (after
  rebalance / completeMintFromLightning). confirm()/retry now skips the move
  entirely once topped up and only (re)sends the zap — funds can never move twice.
- awaitTargetFunded(): briefly poll the target balance after topping up so the
  follow-up nutzap sees the new proofs and succeeds on the first try instead of
  needing a manual retry. Best-effort with a timeout; the checkpoint guarantees no
  double-move even if it falls through.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:45:21 +00:00
Claude 77c183186c fix(wallet): show the cashu logo (not a generic wallet icon) on the cashu wallet row
CashuWalletRow used MaterialSymbols.AccountBalanceWallet; swap it for the
tintable CustomHashTagIcons.Cashu so the cashu wallet reads as cashu.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:31:28 +00:00
Claude a1fd21d456 fix(zap chip): soften the toggle outline and use brand orange for selected cashu
- Toggle border eased from `outline` to `outlineVariant` — present but no longer
  heavy.
- Selected cashu (and the reload variant) now tints with the same BitcoinOrange
  as the Lightning/on-chain rails instead of the purple accent, so the active
  rail colour is consistent across all three.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:29:34 +00:00
Claude fea00f5707 Merge remote-tracking branch 'origin/main' into claude/amazing-volta-UJrwo 2026-05-30 00:26:26 +00:00
Vitor PamplonaandGitHub b9bee7301f Merge pull request #3107 from vitorpamplona/claude/magical-faraday-VHJbK
Refactor RelayProxyClientConnector to use injected StateFlows
2026-05-29 20:25:32 -04:00
Claude 7bda12f047 fix(zap chip): outline the whole toggle, not the selected segment
Reverts the per-segment selected outline. The real ask was a clearer mark for the
entire 3-rail toggle: give the whole component a 1dp `outline` border so it reads
as one control. The selected segment keeps its subtle primaryContainer thumb.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:24:24 +00:00
Claude 96b689b1cb fix(zap chip): enlarge cashu glyph (+20%) and make the selected toggle state clear
- The new monochrome cashu outline read too small; bump it from 0.72× to 0.86×
  the symbol size so it matches the bolt/bitcoin marks optically.
- The selected segment's container fill sat too close to the track to read as
  "selected", so add an animated primary outline around the active segment — the
  state is now unmistakable (outline + fill + the amount label all land on it).

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:18:42 +00:00
Claude 3457bf4390 feat(zap chip): turn the rail picker into an animated segmented toggle (select, then send)
Replaces the "tap a rail icon = instant send" model (which read like a toggle but
moved money) with an explicit two-step control, so switching rails never sends:

- The rails are one connected segmented pill (shared surfaceVariant track) — they
  visibly belong together. A primary-container "thumb" animates to the selected
  segment.
- Only the selected segment shows the amount (+ a send arrow); it expands in on the
  chosen rail and shrinks away on the previous one, so the amount reads as
  travelling to the icon you tapped.
- Tapping an unselected rail only selects it (no payment). Tapping the selected,
  labelled segment is the single thing that sends. Selection starts on the
  amount-tier default, so the common case is still one tap. Long-press still edits
  the presets.

Removes the old leading-icon + trailing circular-button layout and the now-unused
RailButton.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-30 00:06:55 +00:00
Claude 099c71a183 Merge remote-tracking branch 'origin/main' into claude/amazing-volta-UJrwo
# Conflicts:
#	amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt
2026-05-29 23:48:34 +00:00
Claude 8c855a3fc6 fix(zap chip): symmetric padding for single-rail chips, quieter alternatives + amount
- When a chip has no alternative rails, match the right inset to the left so the
  icon+amount pill stays symmetric (was cramped against the right edge).
- Alternative-rail circles shrunk toward the amount's font height (28→22dp) with
  a smaller icon inside (ZapRailIcon gained a size param; alternatives render at
  14dp), so they stay quiet next to the bigger coloured preferred logo.
- The amount text is now neutral (onSurface) instead of taking the rail's brand
  colour — only the leading logo carries colour, so amounts don't shout across
  the feed. Dropped the now-unused zapRailAccent helper.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 23:42:27 +00:00
Claude 546007412d refactor: simplify Tor-bootstrap reconnect fix to a transport-change check
Replaces the per-relay backoff-token approach (opaque WebsocketBuilder
config token + BasicRelayClient bookkeeping) with a much smaller check at
the source.

The connector already holds the two OkHttp clients, and those are rebuilt
only when something connection-relevant changes (Tor's SOCKS port appears,
wifi<->cellular switch). Everything else it wakes on — Tor bootstrap status
churn, connectivity blips, self-heal restarts — leaves the clients
untouched. So instead of threading a config token through quartz, the
connector now forces a backoff-skipping reconnect (ignoreRetryDelays=true)
only when an OkHttp client instance actually changed; otherwise it lets
each relay's exponential backoff decide. That stops the
reconnect-fail-reconnect loop while Tor boots, and still reconnects every
relay (Tor and clearnet alike) the instant the transport changes.

Reverts the quartz/OkHttpWebSocket changes from the previous commit and
wires the connector to take the StateFlows it actually consumes (the two
client flows + connectivity/tor status) instead of the manager objects,
which also decouples it from DualHttpClientManagerForRelays/
ConnectivityManager/TorManager.

https://claude.ai/code/session_01SCz8kdYs2FwesEyzbhmRPY
2026-05-29 23:41:35 +00:00
Vitor PamplonaandGitHub 083e98f860 Merge pull request #3106 from vitorpamplona/claude/lucid-goldberg-YUBPw
Redesign Cashu icon as monochrome tintable glyph
2026-05-29 19:37:35 -04:00
Claude 93b77348ac refactor: enlarge the Cashu sunglasses further
Bump the pixel sunglasses scale (1.45 -> 1.70) so the shades read bolder and
overhang the slimmed, rounded cashew body more prominently. Body outline and
1.2 stroke weight unchanged.
2026-05-29 23:30:48 +00:00
Claude 92b1af8a23 feat(zap settings): drag-and-drop reordering of quick-zap preset chips
Long-press a preset chip to pick it up and drag it to a new position in the
FlowRow; release to drop. Hand-rolled (no new dependency):

- VM gains moveAmount(from, to) to reorder the preset list.
- Each chip records its bounds (boundsInParent) and is wrapped in key(amount) so
  reordering doesn't restart the in-flight drag gesture; the dragged chip follows
  the finger via graphicsLayer translation and lifts with zIndex.
- The drop target is the chip whose bounds contain the pointer; on cross-over the
  list reorders and the drag offset is rebased so the chip stays under the finger.
  Identity (drag state, bounds map, target) is keyed by the amount value, not the
  slot index, to avoid stale-index bugs across reorders.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 23:11:49 +00:00
Claude c91ef86d4d refactor(zap/wallet): move NWC setup out of zap-amount settings to the Add-NWC screen
The Nostr Wallet Connect section (connect button, paste, QR, manual pubkey/relay/
secret) is removed from the zap-amount settings content. NWC setup now lives only
in the wallet area:

- The `nostr+walletconnect` connect deep link (`dlnwc`) now opens Wallet → Add NWC
  with the URI prefilled (Route.WalletAddNwc gained an optional nip47 arg), instead
  of the old shared NIP-47 setup screen.
- UpdateZapAmountContent loses its nip47uri parameter and the whole wallet-connect
  block; both callers (zap settings + NIP-47 setup) updated. The NIP-47 setup
  screen keeps its Lightning-address and payment-targets sections (both also
  reachable from profile edit / EditPaymentTargets) and is no longer the deep-link
  target.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 23:06:29 +00:00
Claude df33b99b07 refactor: round the Cashu nut body into a smooth outline
Replace the blocky pixel-stepped cashew silhouette with a smooth rounded
outline (corner-cut and emitted as a compact Bézier spline) so the nut reads
as a clean curved shape instead of a staircase. Use a round stroke cap/join at
the same 1.2 weight; the pixel "deal-with-it" sunglasses stay a solid fill.
2026-05-29 23:01:34 +00:00
Claude 5d6a0e542d feat(zap settings): preset chips mirror the live feed chip (rails + default), drop bolt emoji
Quick-zap preset chips now render like the real feed chip instead of a plain
" N" InputChip: the amount with the rails a preset of that size could use —
the amount-tier default rail in colour on the left, the alternatives (cashu /
Lightning / on-chain) in monochrome — with an X to remove. The bolt emoji is
gone; Lightning is the Material Symbols bolt, matching the feed.

Refactor: ZapRail, ZapRailIcon, the tier thresholds, and a new
previewRailsFor / previewPreferredRail / zapRailAccent are now internal so both
the feed chip and the settings preview share one source of truth for rail
choice, ordering, colouring, and the default highlight.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 22:54:08 +00:00
Claude 71bed1cf0a refactor: slim Cashu body and enlarge the sunglasses
Squeeze the cashew outline horizontally (~0.72) and scale the pixel sunglasses
up (~1.45) so the shades overhang the body for a bolder, more recognizable
mark. Stroke weight stays at 1.2 to match the shared Zap outline icon.
2026-05-29 22:51:54 +00:00
Claude d257749908 feat(zap): top-up screen — avatar header, multiple LN wallets, separate top-up amount
- Header is now a visual: your avatar → cashu zap glyph → arrow → the recipient's
  avatar, centered, replacing the explanatory subtitle text.
- Multiple Lightning wallets: each configured NWC wallet is its own "Funds from"
  option (by name), defaulting to the configured default wallet; the chosen
  wallet's URI is used to auto-pay. Falls back to a single external-invoice option
  when no NWC wallet is set up.
- Separated the two amounts: the send (zap) amount is fixed; the editable field is
  now the top-up amount, defaulting to the shortfall and adjustable upward.
  Funding mints/moves the top-up; the nutzap always sends the fixed amount.
- Muted the selected source styling — the radio button carries the signal, so the
  card uses a faint primary wash + a thin low-opacity border instead of a heavy
  2dp outline and filled container.
- Bottom summary now reads "Top up X sats to <mint> and zap <name> Y sats · fee ≈ Z
  sats" (and "Zap <name> Y sats from <mint>" when no top-up is needed), using the
  recipient's display name.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 22:41:27 +00:00
Claude 9010e85411 feat: monochrome outline Cashu icon for Material Symbols compatibility
Convert the multi-tone "deez nuts" Cashu/nutzap logo into a single-color,
tintable outline icon so it behaves like a Material Symbol glyph: the cashew
body is a hollow stroke (1.2 weight, matching the shared Zap outline icon) and
the pixel sunglasses stay a solid fill so they read at small sizes.

Drop the `tint = Color.Unspecified` overrides at every call site (zap chips,
nutzap rows/gallery, redeem, wallet screens) so the icon now tints with the
surrounding content colour instead of being locked to the brand browns, and
remove the imports/comments that only existed to preserve the old multi-tone
rendering.
2026-05-29 22:38:43 +00:00
Claude e1a651e72e fix: honor relay backoff during Tor bootstrap via per-relay config check
Before Tor is ready, relays were disconnecting and immediately
reconnecting, ignoring BasicRelayClient's exponential backoff.

RelayProxyClientConnector calls reconnect(ignoreRetryDelays = true) on
every infrastructure change. That flag flows pool-wide into
connectAndSyncFiltersIfDisconnected and unconditionally bypassed the
backoff gate. While Tor is still bootstrapping its SOCKS port isn't
listening, so each unrelated infra event (connectivity transitions,
self-heal restarts, Tor status churn) forced an immediate reconnect that
failed again — the backoff was computed (delay kept doubling) but never
consulted.

Make the bypass per-relay and conditional on the relay's transport
config actually changing since its last attempt:

- WebsocketBuilder gains connectionConfig(url): an opaque,
  value-comparable token of the transport config (proxy + timeouts).
  Default null = untracked, preserving legacy always-bypass behavior for
  in-process/standalone/test/desktop builders.
- BasicRelayClient records the token at each attempt and only lets a
  forced reconnect skip the backoff when the token changed. A Tor relay
  whose SOCKS config is unchanged keeps honoring its backoff; the moment
  Tor flips to active (proxy port appears) the token changes and it
  reconnects immediately. Clearnet relays still retry immediately
  whenever their own client changes.
- OkHttpWebSocket.Builder reports the proxy+timeout fingerprint, reused
  by needsReconnect() to avoid drift.

Adds BasicRelayClientBackoffTest covering the honored/bypassed/untracked
cases.

https://claude.ai/code/session_01SCz8kdYs2FwesEyzbhmRPY
2026-05-29 22:30:53 +00:00
Claude 4ffb69c459 fix(zap): shrink cashu chip logo further (15→13dp)
Second pass on the cashu logo size — it still read large next to the Material
symbol rails; drop to 13dp and scale the reload "+" badge to 9dp to match.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 22:18:31 +00:00
Claude c801b8e580 feat(zap): merge preferred logo into the amount tap-target; circular option buttons
- The default action is now one tap target: the preferred rail's logo hugs the
  amount (no separate button), and a tap anywhere on icon+amount fires that rail.
- Alternative rails render as distinct circular buttons (filled surfaceVariant
  circle) to their right, so it's clear they're individually tappable.
- The amount takes the preferred rail's accent colour (BitcoinOrange for
  Lightning/on-chain, primary for reload; neutral for the multi-tone cashu logo,
  which has no single brand colour).
- Reworked the preview to exercise every rail combination across the amount tiers
  (5 / 100 / 5k / 100k sats): all-rails, cashu-only, lightning-only, and a
  funds-split reload scenario.

Refactor: split the per-rail logo (ZapRailIcon, click-free) from the tap target
so it can be reused both inside the default area and inside RailButton.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 22:10:54 +00:00
Claude 9386aa6eb2 feat(zap): amount-tiered default rail + preferred logo left of the number
- Default rail is now amount-tiered: under 10 sats prefers cashu (Lightning
  min/fees make tiny zaps awkward), over 10k sats prefers an on-chain
  transaction, and the middle defaults to Lightning — each falling back through
  the remaining rails when the tier's rail isn't available. The tap action and
  the colored leftmost logo both follow this single `preferred` choice.
- The preferred rail's logo now sits to the LEFT of the amount (in colour); the
  alternative rails follow on the right in monochrome.
- Shrank the cashu logo to 15dp (~17%) so it no longer dwarfs the Material
  symbol rails it sits beside; the reload "+" badge scaled to match.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 22:04:36 +00:00
Claude c714eba482 feat(zap): rework top-up screen (rename, editable amount, clearer source, live balances) + chip ordering
Top-up (formerly "Reload Mint") screen:
- Renamed throughout to "Top up mint" / "Top up & send zap".
- Amount is now editable (numeric field, seeded from the tapped preset). The VM
  recomputes shortfall/sources/fees on every edit; if the chosen amount already
  fits the target mint, the screen drops the funding step and just sends.
- Selected funding source is now obvious: radio button + 2dp primary border +
  primaryContainer fill, instead of a faint 12%-alpha tint.
- Each source's balance/description moved to its own line under the title, so
  the Lightning explanation wraps instead of clipping.
- Removed the redundant "Your balances" list (it duplicated the per-mint balance
  the Funds-from rows already show); the target's current balance now sits in the
  destination card.
- Summary reads "Send AAA sats from XXX to YYY, fee Z sats" (or "Send AAA sats to
  YYY" when no top-up is needed).
- Balances are now reactive: the VM observes the wallet's tokenEntries/mints
  flows and rebuilds as proofs arrive from relays, instead of a one-time snapshot
  that could show only the first-loaded mint.

Zap chip:
- The preferred rail (what a plain tap triggers) is rendered first and in full
  colour; the alternative rails follow to its right in monochrome.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 21:53:37 +00:00
Claude bbec99af75 fix(zap): shrink reload cashu logo to one rail's footprint
The NEEDS_RELOAD affordance rendered two full 18dp icons side by side (cashu +
"+"), making the chip nearly twice as wide as the other rails. Overlay the "+"
as a small 11dp corner badge on the dimmed cashu logo so it occupies a single
logo's footprint like Lightning/on-chain.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 21:40:45 +00:00
Vitor PamplonaandGitHub 1ec5f559e6 Merge pull request #3105 from vitorpamplona/claude/eager-davinci-WxjiM
Add dedicated podcast screen with episode list and inline player
2026-05-29 17:32:55 -04:00
Claude ba35a4e527 feat(podcasts): translatable descriptions on the NoteCompose feed cards
RenderPodcastMetadata and RenderPodcastEpisode rendered their description
tag as a plain Text. Both now use TranslatableRichTextViewer (rich text +
translation), matching the podcast screen header. The metadata card finally
uses its previously-ignored canPreview/backgroundColor params; the episode
card gives its short description a distinct id so it doesn't share remember
state with the markdown body below it.
2026-05-29 21:30:44 +00:00
Claude 063ebbebce feat(podcasts): translatable show description + synthetic playback waveform
- The show description on the podcast screen now renders through
  TranslatableRichTextViewer (rich text + translation), matching how a
  profile's About is shown, instead of a plain Text. PodcastHeader takes a
  nav for the rich content's links.
- Audio playback now shows a decorative per-episode waveform when the
  source has none, instead of a flat strip — same behaviour as music
  tracks. Extracted MusicTrack's private syntheticWaveformFor into a shared
  wavefront/SyntheticWaveform.kt used by both music and podcast players; the
  podcast player still prefers a real IMeta waveform when one is present.
2026-05-29 21:21:54 +00:00
Claude 116ff33cd8 test(zap): assert merge preserves preset order instead of sorting
Follow-up to dropping .sorted() from mergeZapAmounts: update ZapAmountMergeTest
to lock in first-seen order (Lightning list, then on-chain extras) rather than
ascending, so a user's saved ordering is provably preserved.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 20:30:58 +00:00
Claude 594f86d1ce Merge remote-tracking branch 'origin/main' into claude/amazing-volta-UJrwo 2026-05-29 20:23:22 +00:00
Claude b1961ea19c fix(zap): address reload-mint audit findings (double-submit, premature done, fee/poll robustness)
Correctness:
- ReloadMintViewModel.confirm() now guards against re-entry (only starts from
  Configuring/Failed and flips to Working synchronously), so a double-tap or a
  Failed-state Retry can't launch two pipelines that double-spend the source /
  double-mint the target.
- sendNutzapAndFinish awaits the real CashuWalletState.sendNutzap (throws on
  failure) and reports Done only on success — a send that fails after a
  successful reload now surfaces as Failed instead of popping the screen on a
  premature "done" and silently stranding the moved funds.
- The reload pipeline runs on the long-lived AccountViewModel scope; the VM now
  holds its Job and cancels it in onCleared(), so leaving the screen stops the
  (up to 3-minute) Lightning poll instead of hammering the mint unobserved.
- rebalance() poll budget widened to a steady ~60s so a merely-slow mint no
  longer strands funds that already left the source.
- Mint a small headroom buffer (RELOAD_FEE_BUFFER_SATS) above the bare shortfall
  so the follow-up nutzap's own swap fee doesn't leave the target a sat short;
  the source-feasibility gate accounts for it.

Regressions from the settings merge:
- mergeZapAmounts / the picker no longer .sorted() the amounts — a user's saved
  preset order is preserved instead of being silently reordered ascending.
- The on-chain send dialog falls back to DEFAULT_ONCHAIN_ZAP_SATS when the
  unified list has nothing above the on-chain minimum, restoring the guaranteed
  quick-pick preset.
- zapClick's one-tap (single-amount) path now checks rail capability and opens
  the picker when the recipient can't receive Lightning, instead of firing a
  doomed Lightning zap.

Cleanup:
- Centralized the mint-quote "settled" predicate as MintQuoteBolt11ResponseDto
  .isSettled(); rebalance, ReloadMintViewModel and CashuWalletViewModel now
  share it instead of three copies of paid==true || PAID || ISSUED.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 20:23:00 +00:00
Claude 07855c2240 Merge remote-tracking branch 'origin/main' into claude/eager-davinci-WxjiM 2026-05-29 20:18:56 +00:00
Vitor Pamplona 936c96e342 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-29 16:08:38 -04:00
Claude c504b465e1 refactor(podcasts): share the episode audio player; fix list player height
Audit follow-ups on the podcast screen:

- Extract the duplicated GetMediaItem -> GetVideoController -> RenderVoicePlayer
  block (and the FLAT_WAVEFORM constants) from both PodcastEpisode and
  PodcastEpisodeListItem into one PodcastEpisodeAudioPlayer composable. The
  border shape stays a parameter (bottom-rounded under the cover vs. fully
  rounded in the list).
- Fix the list row clipping the 75dp play button: the row hard-coded 72dp,
  below the voice player's 80dp minimum. Height now lives in the shared
  player so both call sites stay identical.
- Header no longer flashes "0 episodes" while the relay request is in flight:
  episodeCount is nullable and the count/divider only render once loaded.
- Drop the odd uppercasing of the episode date (looked wrong on relative
  values like "3d").
2026-05-29 20:08:11 +00:00
Vitor Pamplona dd92098343 updates all libs 2026-05-29 16:07:49 -04:00
Claude ce32dc4fc3 feat(zap): Reload Mint screen to fund a recipient's mint, then nutzap
When the cashu logo on an amount chip is NEEDS_RELOAD (you hold enough cashu,
but not in the recipient's mint), it now shows a dimmed logo with a "+" badge
that opens a full Reload Mint screen instead of silently falling back to
Lightning.

The screen tells the whole story and auto-sends the nutzap on success:
- Destination ("mint to reload") with its shortfall; selectable when the
  recipient accepts several mints we hold.
- All cashu balances for context.
- Source ("funds from"): another mint (mint-to-mint rebalance, no new sats) or
  Lightning. Mint rows are disabled when they can't cover shortfall + fee.

Funding paths:
- Rebalance: CashuWalletState.rebalance moves cashu between mints.
- Lightning: mint a fresh quote at the destination, auto-pay via NWC when a
  wallet is configured (else show the invoice to copy), poll, then issue.

Wiring: new Route.ReloadMint + AppNavigation entry; a ReloadMintRequest handed
off via an LruCache on AccountViewModel (mirrors the manual-zap pattern);
onReloadNutzap threaded through the zap popup overloads and the chip; callers
(ReusableZapButton, ReactionsRow, NestActionBar) navigate to the screen.

IMPOSSIBLE (not enough cashu anywhere) keeps falling back to Lightning — the
reload screen is only for moving existing cashu.

https://claude.ai/code/session_01HNE2z7CSYZ2G8KwC5fziJn
2026-05-29 19:32:30 +00:00
Claude 46e618aabe feat(podcasts): tap a podcast to open a show screen with all episodes
Each item in the Podcasts feed (kind 10154 show metadata) now opens a
dedicated podcast screen built around the show's pubkey — per NIP-F4 a
podcast is its own keypair, so its metadata and every episode (kind 54)
share one author.

- New Route.Podcast(pubkey) + AppNavigation binding.
- PodcastScreen: modern hero header (cover art, title, websites, tappable
  expand/collapse description, "N episodes" section) followed by every
  episode in a LazyColumn.
- PodcastEpisodeListItem: compact "show list" row (date, title, summary)
  with an inline audio player so episodes play without leaving the list.
- OnePodcast datasource (per-user EOSE manager) fetches the show metadata
  and episodes from the podcast key's outbox relays.
- OnePodcastEpisodesFeedFilter + OnePodcastFeedViewModel scope LocalCache
  to a single podcast's episodes.
- Podcasts feed metadata card is now clickable with a "View episodes"
  affordance.
2026-05-29 19:24:50 +00:00