Commit Graph
15076 Commits
Author SHA1 Message Date
Claude 466086c475 refactor: move FavoriteAlgoFeedsListDecryptionCache to commons
Pure quartz-only decryption cache — extract to
commons/model/nip51Lists/favoriteAlgoFeedsLists so Desktop/CLI/iOS can
reuse it (mirrors the TrustProviderListDecryptionCache move). Re-points
Account and the sibling FavoriteAlgoFeedsListState.

https://claude.ai/code/session_01JFbYZdVV4QmDC4eQYvEccb
2026-05-30 21:42:51 +00:00
Claude c619d71890 refactor: move NwcWalletEntry to commons/model/nip47WalletConnect
Pure data class (only quartz + a UUID id generator) — extract to
commons so Desktop/CLI/iOS can reuse the NWC wallet entry model.
Swaps java.util.UUID for the multiplatform kotlin.uuid.Uuid (matching
the quartz convention) and re-points the three callers
(LocalPreferences, AccountSettings, WalletViewModel).

https://claude.ai/code/session_01JFbYZdVV4QmDC4eQYvEccb
2026-05-30 21:40:54 +00:00
Vitor PamplonaandGitHub 34f6b2ba36 Merge pull request #3111 from vitorpamplona/claude/epic-hamilton-23225
NIP-32: Add hashtag labeling and label-based hashtag feed
2026-05-30 17:32:03 -04:00
Claude 3f4066c4b8 refactor: buildHashtagLabel takes an EventHintBundle
Replace the labeledEventId/relay/author trio with a single
EventHintBundle<out Event>, building the e-tag via the standard
EventHintBundle.toETag() idiom. The Account caller now passes the
note's event hint directly.

https://claude.ai/code/session_019gc3FipVBcndF9fmqCCfVX
2026-05-30 21:23:56 +00:00
Vitor PamplonaandGitHub ae85bfc358 Merge pull request #3110 from vitorpamplona/claude/dreamy-galileo-YOiWa
Move utility functions and models to commons module
2026-05-30 17:13:50 -04:00
Claude 80dc2f2d91 fix: stop scroll-to-end widening from cascading the DM window
Re-key the rooms list edge-detector on listState only, instead of
(listState, itemCount). Keying on item count re-armed the detector on
every widen: loadMore pulled older conversations, the list grew,
LaunchedEffect restarted, the edge-detector reset, and it fired again —
walking the window 7->14->21->...->112 days back in a few seconds on a
slow connection. Now distinctUntilChanged fires once per reach-the-end
gesture and does not re-fire while parked at the end.

Also surface initialLoadInFlight from both DM loaders (gift wraps +
NIP-04) and keep a spinner up on the rooms screen until the first relay
answers, so cold boot no longer flashes the empty state before the DMs
land.
2026-05-30 21:11:43 +00:00
Claude 8e7d169198 feat: route follow hashtag-labels through each follow's outbox relays
The kind-1985 label subscription for the hashtag feed now follows the
NIP-65 outbox model: instead of querying the flat hashtag relay set for
all labels and filtering to follows locally, it queries each follow's
outbox relays for that follow's own label events (authors restricted per
relay via account.followsPerRelay), tagged with the hashtag. This ensures
a follow's labels are picked up even when they never reach the hashtag's
own relays.

https://claude.ai/code/session_019gc3FipVBcndF9fmqCCfVX
2026-05-30 20:59:19 +00:00
Claude 789dd4f02b refactor: dedup ByteFormatter against commons util
amethyst/service/ByteFormatter duplicates
commons/util/countToHumanReadableBytes. Adopt the commons version as
canonical (it appends a " B" unit suffix for sub-1000 byte counts;
the amethyst copy returned a bare number) and re-point the three
callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:52:12 +00:00
Claude e930cc6ef0 feat: window NIP-04 DMs in lockstep with gift wraps in the rooms list
The rooms list merges NIP-04 (kind 4) and NIP-17 (gift wrap) conversations into
one time-sorted list, but only the gift-wrap loader was windowed — NIP-04
(`DMsFromUserFilterSubAssembler`) still used an EOSE-only `since` with no limit,
so it loaded all kind-4 history at boot.

That asymmetry broke scroll-to-load-more: gift wraps filled only the recent top
of the list while NIP-04 filled the whole tail, so reaching the list end (deep in
the NIP-04 tail) fired `giftWraps.loadMore()`, and the newly fetched 7-14d gift
wraps inserted in the *middle* of the feed instead of extending the end — and
could re-fire step after step while the user sat in the NIP-04 tail.

Apply the same TimeWindowPagination to the NIP-04 rooms-list loader and advance
both windows together from the scroll handler, so the merged list is bounded
uniformly and reaching the end extends the actual end. The loading footer now
reflects either protocol still loading.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 20:42:06 +00:00
Claude 6eee100010 refactor: dedup CountFormatter against commons util
amethyst/service/CountFormatter.countToHumanReadable(counter, str)
duplicates the identical 2-arg overloads already in
commons/util/NumberFormatters. Delete the duplicate and re-point the
two relay-row callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:37:20 +00:00
Claude 5d629faa05 refactor: dedup EmojiUtils against commons util
amethyst/service/EmojiUtils duplicates commons/util/EmojiUtils
(identical logic; commons uses the KMP codepoint helpers instead of
JVM Character calls). Delete the duplicate and re-point callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:37:12 +00:00
Claude b0f9b1621a refactor: move CashuToken to commons/model/nip60Cashu
CashuToken and Proof are pure data classes (only @Immutable +
kotlinx.serialization). Extract to commons so Desktop/CLI/iOS can reuse
them, and re-point all callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:37:04 +00:00
Claude d57f8c18c6 feat: first-class NIP-32 hashtag labels on posts and in the hashtag feed
Let users tag any post with a hashtag via a NIP-32 kind 1985 label event
(using the `#t` tag-association namespace), and surface follow-labeled
posts in the hashtag feed.

quartz:
- LabelEvent.buildHashtagLabel() + HASHTAG_NAMESPACE ("#t") and
  hashtagAssociations() to build/extract hashtag-association labels.

commons:
- Note now carries a `labels` reverse-reference map (hashtag -> labeler
  notes) with addLabel/removeLabel and a NoteFlowSet.labels flow,
  mirroring reactions/reports.

amethyst:
- LocalCache consumes LabelEvent, attaching hashtag labels to their
  target notes and re-notifying feed observers for already-cached
  targets.
- Account.createLabelHashtagEvent/labelHashtag/consumeLabelEvent and
  AccountViewModel.labelWithHashtag (tracked + direct broadcast).
- Overflow "⋯" menu gains an "Add hashtag" action backed by a new
  AddHashtagLabelDialog.
- HashtagFeedFilter also accepts posts a followed user labeled with the
  hashtag; a new label sub-assembler subscribes to kind 1985 by `#l`
  and fetches missing label targets.
- Hashtag feed shows an attribution banner ("#tag added by @user") above
  follow-labeled posts via a custom RefresheableFeedView onLoaded.

https://claude.ai/code/session_019gc3FipVBcndF9fmqCCfVX
2026-05-30 20:32:43 +00:00
Claude 19d6c9baaa refactor: move TrustProviderListDecryptionCache to commons, rename pkg to nip85TrustedAssertions
Extract the pure (quartz-only) decryption cache into commons. Converge
the commons trustedAssertions package onto the quartz NIP slug
'nip85TrustedAssertions' (per migration plan §10.2), moving the existing
TrustProviderListState interface + UserCardsCache with it and
re-pointing all callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:15:36 +00:00
Claude 47a70b4fa3 refactor: move OwnedEmojiPack to commons
Pure data class (only quartz + @Stable deps) — extract to
commons/model/nip30CustomEmojis so Desktop/CLI/iOS can reuse it.
Moves the unit test to commons commonTest and re-points callers.

https://claude.ai/code/session_01H66WwvUYm5KtAWBLgUMcod
2026-05-30 20:15:28 +00:00
davotoulaandClaude Opus 4.7 908eedb831 fix(amethyst): set git cwd in versionName branch detection so worktrees work
ProcessBuilder("git", …) inherited the daemon's working directory, which
in a git worktree (or when the Gradle daemon was started elsewhere) is
not the project root. git printed "fatal: not a git repository"; with
redirectErrorStream that landed in stdout, got cleaned to dashes, and
truncated to exactly "fatal--not-a-git-rep" — visible as the version
suffix in installed builds.

Pass rootDir to ProcessBuilder.directory() so git always runs at the
worktree root, and check the exit code so any future failure falls back
to "unknown" instead of leaking stderr into the version name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-30 21:59:35 +02:00
Vitor PamplonaandGitHub b6361f133c Merge pull request #3109 from vitorpamplona/claude/great-knuth-sJVYl
Reorganize commons packages: NIP-64 chess, NIP-AC WebRTC, and model caches
2026-05-30 15:55:59 -04:00
Claude 6b2265273a docs(commons): add target package hierarchy + naming rules for the migration
Adds the architecture end-state (ports exist, impls are forked 3 ways =>
unify), the concrete destination package tree for the migrated common
objects (cache engine, decomposed Account into model/account state +
actions + facade, per-NIP state holders, feeds, keystorage, service split),
and the package-naming decisions behind it (model/nipNN raw state vs
model/account composed view; state/ stays generic; actions=verbs; quartz
slug normalization; LocalCache class+delegating object). Also drops a stray
markup line at the end of the doc.

https://claude.ai/code/session_01KXLzsvx9Gyrm3Yz4Rims55
2026-05-30 19:51:38 +00:00
Claude d7aa307b9c docs(commons): add Amethyst→commons migration plan
Survey of amethyst/{model,service,ui} (1817 files) classifying what should
move to commons vs stay Android-native, with per-area matrices, the real
cross-cutting blockers (account state is the keystone; R.string is solved by
Compose Resources, not a new StringProvider; Coil is already KMP), a
dedup/reconciliation backlog, and a phased roadmap with a concrete first PR.

https://claude.ai/code/session_01KXLzsvx9Gyrm3Yz4Rims55
2026-05-30 19:36:22 +00:00
Claude 976c650b2f fix: detect gift-wrap REQs by kinds array, drop EOSE noise
The diagnostics logger tagged a subscription as gift-wrap when its raw REQ
string merely *contained* "1059"/"1060" — which matches incidentally inside a
pubkey hex or a since/limit number on unrelated feed REQs. Those feed subs then
leaked their EOSEs (and some connect/auth lines) into the DMPagination tag.

Match the filter's `kinds` array exactly against the real gift-wrap kinds
(1059 + 21059) instead. Also drop the per-relay EOSE line entirely — it's
redundant with the "cold boot: … initial load complete" summary that already
reports the first EOSE and gift-wrap count.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
2026-05-30 19:35:29 +00:00
Claude d8899eedf8 refactor(commons): move feature-specific UI out of ui/ into <feature>/ui
Make the feature-UI vs cross-cutting-UI rule consistent (feature-first):
- ui/nip53LiveActivities -> nip53LiveActivities/ui
- ui/article + ui/editor -> new nip23LongContent/ui (article reader + editor)

ui/ now holds only cross-cutting composables (theme, components, layouts,
elements, markdown, signing, thread, feeds, notifications, screens, state,
text). Tighten ARCHITECTURE.md with the deciding test ('could a second
unrelated feature reuse this as-is?') and reconcile the NIP-second-axis
section so a single-NIP feature owns its UI under <feature>/ui rather than
ui/nipNN.

https://claude.ai/code/session_01KXLzsvx9Gyrm3Yz4Rims55
2026-05-30 19:02:55 +00:00
Claude 79a9bf78f8 refactor(commons): name single-NIP feature packages after their quartz NIP
Rename the two single-NIP feature packages to mirror their quartz
counterparts for 1:1 traceability:
- chess -> nip64Chess
- call  -> nipACWebRtcCalls

marmot and nip53LiveActivities already match quartz and are unchanged.
Document the rule in commons/ARCHITECTURE.md: layer is the primary axis,
NIP is the secondary axis (nipNN<slug> matching quartz), and commons is
deliberately NOT reorganized NIP-first at the top level. Also remove
stray markup that leaked into the end of the doc.

https://claude.ai/code/session_01KXLzsvx9Gyrm3Yz4Rims55
2026-05-30 18:46:36 +00:00
David KasparandGitHub 1f1abbbc58 Merge pull request #3104 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-30 20:30:17 +02:00
Crowdin Bot 6b48b55920 New Crowdin translations by GitHub Action 2026-05-30 17:48:08 +00:00
davotoula 70f72d4604 fix(sonar-s1871): match ChannelMessage/Metadata explicitly, not via IsInPublicChatChannel
fix(sonar-s1871): merge PayInvoice/Nwc error arms via IErrorResponseLike
fix(sonar-s1871): drop redundant is CommentEvent branch in ThreadFeedView
fix(sonar-s1871): merge NPub/NProfile route arms via IPubKeyEntity
fix(sonar-s1871): merge Error/Notice debug-message arms via IRelayDebugMessageText
fix(sonar-s1871): merge LiveActivities/MeetingRoom arms via LiveStreamLike
fix(sonar-s1871): merge channel-message/metadata arms via IsInPublicChatChannel
fix(sonar-s1871): collapse channel-draft reply handling via BaseThreadedEvent
fix(sonar-s1871): drop redundant is CommentEvent arm in NoteCompose
fix(sonar-s1871): merge Failed/Error arms in NIP-05 badge
fix(sonar-s1871): merge Verifying/NotStarted arms in NIP-05 badge
fix(sonar-s1871): merge note-backed RenderOption arms via NoteBackedName
fix(sonar-s1871): drop redundant is PrivateDmEvent arm in RouteMaker
fix(sonar-s1871): merge GiftWrap/SealedRumor arms in RouteMaker via HasInnerEvent
fix(sonar-s1871): merge Connecting/Connected arms in CallSession state collector
fix(sonar-s1871): collapse playback-state when to if/else in CurrentPlayPositionCacher
fix(sonar-s1871): drop redundant is CommentEvent arm in sendPublicReply
fix(sonar-s1871): merge addressable-filter arms via AddressableTopFilter
fix(sonar-s1871): merge repost branches in LocalCache via BaseRepostEvent
fix(sonar-s1871): merge badge-set branches in LocalCache referenced-notes when
2026-05-30 19:43:03 +02:00
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 b0c6ffb821 refactor(commons): consolidate package taxonomy + add architecture doc
Document the commons module's purpose, source-set layout, and the CLI-safe vs
UI boundary in commons/ARCHITECTURE.md, then clean up the clearest package
overlaps that had accumulated:

- merge duplicate util/utils -> util (all source sets)
- unify service/services -> service (jvmAndroid)
- move data/UserMetadataCache -> model/cache
- fold compose/ into ui/ (ui/article, editor, elements, layouts, markdown,
  nip53LiveActivities, and Compose helpers in ui/state + ui/text)
- move ProfileBroadcastBanner composable into profile/ui

All changes are whole-file/whole-package moves with import rewrites; no logic
changed. The chess logic/UI split is documented as deferred debt (it needs
file-level surgery, not moves). Marks docs/shared-ui-analysis.md superseded.

https://claude.ai/code/session_01KXLzsvx9Gyrm3Yz4Rims55
2026-05-30 17:03:57 +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