Commit Graph
4 Commits
Author SHA1 Message Date
Claude 94121c71c9 feat(cashu): mint URL directory + autocomplete
Adds a cache-backed Cashu mint directory sibling to LocalCache.relayHints
that aggregates mint URLs from every relevant event the cache sees, and
wires it into the AddCashuWallet mint-URL text field as inline
autocomplete so users don't have to remember mint URLs.

What feeds the directory:
- NutzapInfoEvent (kind:10019) — every nostr user with a Cashu wallet
  publishes their accepted mints there. A typical inbox of cached
  profiles seeds a useful starter directory automatically.
- MintRecommendationEvent (kind:38000) — explicit public vouches.
- CashuMintEvent (kind:38172) — formal mint announcements from the
  NIP-87 directory subscription.

How it's populated:
- LocalCache.updateMintIndex(event) is called from
  justConsumeAndUpdateIndexes alongside updateHintIndexes, so every new
  event with a mint URL adds to the index. wasNew gating prevents
  re-emissions from inflating popularity counters.
- LocalCache.ensureMintDirectoryBackfilled() does a one-shot scan of the
  existing notes + addressables maps. The autocomplete UI kicks this in
  a LaunchedEffect on screen open so suggestions are useful before the
  next relay round-trip.

Where it surfaces today:
- AddCashuWalletScreen — under the mint-URL OutlinedTextField, a
  MintSuggestionList card shows up to 6 cache-derived suggestions ranked
  by popularity desc + URL asc. Tapping a row fills the field (does not
  auto-add — users typically want to Verify first). Filters out URLs the
  user already added and exact matches of what they typed.

The MintPicker dropdown inside the Receive / Send dialogs is unchanged
— those only need to choose between mints the user already has in their
wallet, so no directory autocomplete applies there.

Tests: 8 unit tests cover normalisation (case-insensitive, trailing-slash
stripping, http(s) gating), popularity ranking, substring filtering,
limit enforcement, and malformed-URL handling.

URL normalisation: trimmed, lower-cased, trailing `/` stripped, scheme
must be http(s). Same URL with different casing or trailing slash
collapses to one entry so popularity counts correctly.

Implementation notes:
- MintDirectoryIndex lives in commons/jvmAndroid (uses ConcurrentHashMap;
  iOS doesn't ship Cashu wallet yet).
- Thread-safe; safe to read from any dispatcher.
- No persistence — purely in-memory, accumulates over the session.
- Entries are never removed: stale entries don't hurt (user always
  verifies before adding), and tracking which event added which URL
  would add bookkeeping without UX benefit.

https://claude.ai/code/session_01MdWddiar819f8XYt5N8BjP
2026-05-27 15:17:42 +00:00
Claude d1749c314f fix: audit findings on iOS-readiness migration
Address bugs and gaps surfaced by an audit of the prior 14 commits.
JVM tests passed because of typealias / platform-type lenience that
won't hold on Native; these are real iOS compile / behavior issues.

BUG fixes (iOS compile failures):

- commons/.../Note.kt:899 — Iterable.sumOf { -> BigDecimal } is a
  JVM-stdlib-only overload. Common stdlib ships sumOf only for
  Int/Long/Double/Float/UInt/ULong. Replaced with fold(BigDecimal(0)).
- commons/.../Note.kt:889 — BigDecimal(it.event?.content): the quartz
  expect-class constructor takes String non-null; JVM accepted nullable
  via platform-type lenience and threw NPE caught downstream. Switched
  to ?.let { content -> BigDecimal(content) }.
- commons/.../Note.kt:838 — `catch (e: java.lang.Exception)` -> `Exception`.
- commons/.../feeds/custom/FeedDefinitionBuilder.kt + FeedBuilderState.kt:
  inline FQN `java.util.UUID.randomUUID().toString()` -> kotlin.uuid.Uuid.
  random().toString() (Kotlin 2.0+, @OptIn ExperimentalUuidApi).
  inline `System.currentTimeMillis() / 1000` -> TimeUtils.now() (already
  used elsewhere in the codebase).
- commons/.../viewmodels/NestViewModelTest.kt: moved from commonTest to
  jvmTest. The test imports NestViewModel + nestsclient, both of which
  the prior PR moved to jvmAndroid. commonTest depends on commonMain
  only, so the test would fail to compile for iosSimulatorArm64Test.

SUBTLE fixes:

- commons/.../UserRelaysCache.kt: the flow field used double-checked
  locking on a non-volatile var. JMM hazard on Native (ARM weak memory
  model) — outer fast-path could observe a partially-published
  WeakReference. Added @kotlin.concurrent.Volatile.
- commons/.../util/UrlValidation.ios.kt: NSURL.URLWithString("http:")
  returns non-null with scheme="http" and no host; JVM's URI.toURL()
  rejects with MalformedURLException. Reject scheme-only network URLs
  (http/https/ws/wss/ftp without a host) to match JVM behavior.
- commons/.../util/KmpLock.kt commonMain doc: corrected "NSLock" ->
  "NSRecursiveLock" to match the actual iOS implementation.

verifyKmpPurity gate extended (commons + quartz):

- Adds patterns: System.currentTimeMillis, Thread.sleep, java.util.UUID,
  kotlin.jvm.Synchronized, kotlin.jvm.Volatile.
- Each pattern paired with a hint pointing at the canonical KMP
  replacement; the error message surfaces both.
- Skips lines that start with //, *, or /* to avoid false positives on
  KDoc / migration notes.
2026-05-25 02:17:32 +00:00
Claude 5116bc21ae refactor(calendars): extract DAL to commons + a11y + inline-FQN cleanup
DAL extraction (commons/src/jvmAndroid/.../model/nip52Calendar/):
- CalendarSortKeys, CalendarAppointmentView, MonthGridBars, IcsExport now
  live in commons so desktop and the future CLI can consume them. Package
  changed to com.vitorpamplona.amethyst.commons.model.nip52Calendar.
- IcsExportTest moved to commons/jvmTest so it can see the `internal`
  escapeText helper without exposing it as public API.
- Feed-filter classes (CalendarAppointmentsFeedFilter,
  CalendarCollectionsFeedFilter) stay in amethyst — they depend on Account
  and LocalCache. The ViewModels stay for the same reason; lifting them
  requires moving Account/LocalCache too, out of scope here.
- A few smart-cast call sites needed local bindings because cross-module
  properties don't support implicit smart-cast.

Accessibility:
- Each month-grid cell announces a full content description ("Wednesday,
  January 15, 2025, 2 events, today, selected") via mergeDescendants so
  TalkBack reads the cell as one item with role=Button.
- Week-strip cells get the same treatment with role=Tab.
- Header title now announces both the title and "jump to today" so the
  affordance is discoverable.
- The expanding FAB describes itself as a toggle, and each sub-FAB as
  the concrete create action.

Inline-FQN cleanup pass:
- CalendarEventDetailScreen (already in a prior pass), CalendarCollectionsView,
  NewCalendarEventScreen, NewCalendarCollectionScreen: hoisted
  fully-qualified androidx.compose / com.vitorpamplona references into
  proper imports per the codebase style.

All 56 calendar tests still pass (48 in amethyst + 8 IcsExport in commons).
2026-05-19 21:58:34 +00:00
Claude ca9f7233f2 test: verify real-world 5.5KB nowhr.xyz/s store link round-trips through the parser 2026-05-16 21:16:45 +00:00