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.
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).