- Split build.yml into lint, test (matrix: 3 OS), build-android, and
build-desktop (matrix: 3 OS with native packaging) jobs
- Tests now run on ubuntu, macos, and windows to validate cross-platform
compatibility
- Desktop distributions (DEB, DMG, MSI) built on their native OS and
uploaded as artifacts
- Split create-release.yml into create-release, deploy-android, and
deploy-desktop jobs so desktop distributions are included in releases
- Android APK/AAB builds and Quartz publishing remain on Linux
https://claude.ai/code/session_016dxFUErY4P6WRir4xxhCEr
* 'main' of https://github.com/vitorpamplona/amethyst:
Rename build step for Benchmark APK in workflow
compilation error fix: Changed map to mapNotNull
spotlessApply
Replace all TODO stubs in the iOS actual implementation with delegation
to KotlinSerializationMapper. Wraps SerializationException into
IllegalArgumentException to match the jvmAndroid error handling pattern.
https://claude.ai/code/session_01QBc4Pb7a6m4TfLZSJKVdjw
Add 15 KSerializer implementations in commonMain that replicate the exact
behavior of all 21 Jackson serializer/deserializer registrations:
- EventKSerializer, TagArrayKSerializer, FilterKSerializer
- EventTemplateKSerializer, RumorKSerializer, CountResultKSerializer
- MessageKSerializer (8 message types), CommandKSerializer (5 command types)
- BunkerRequest/Response/MessageKSerializer (NIP-46)
- Nip47Request/Response/NotificationKSerializer (NIP-47)
- KotlinSerializationMapper as the main entry point
Uses JsonElement-based approach for performance. All serializers produce
identical JSON output to Jackson. Includes comprehensive test suite (35+
tests) verifying exact output parity and cross-deserialization between
Jackson and kotlinx.serialization.
https://claude.ai/code/session_01QBc4Pb7a6m4TfLZSJKVdjw
Add INostrClient.queryCountSuspend() extension functions in Quartz that
wrap NIP-45 COUNT queries as suspend functions, managing subscription
lifecycle internally. Two overloads: single-relay and multi-relay.
Simplify BasicRelaySetupInfoModel and Nip65RelayListViewModel to use the
new utility — removes IRelayClientListener implementation, subId tracking
maps, onIncomingMessage handlers, and cleanup logic from both ViewModels.
https://claude.ai/code/session_016158D5mq5BygS1uBbLNbsA
Replace the plain icon+text count display with rounded pill chips that
have a subtle green border and tinted background. Each count entry gets
its own pill with an icon and bold text, making the event counts more
visually distinct and scannable.
https://claude.ai/code/session_016158D5mq5BygS1uBbLNbsA
Move count query logic from separate RelayEventCountViewModel into each
relay list's own ViewModel. BasicRelaySetupInfoModel now manages counts
via countFilters() override. Nip65RelayListViewModel gets its own count
infrastructure for home (outbox) and notification (inbox) relay lists.
Simplify AllRelayListScreen by removing 7 extra count ViewModels and
their LaunchedEffects.
https://claude.ai/code/session_016158D5mq5BygS1uBbLNbsA
Use NostrClient's NIP-45 COUNT queries to show how many events each
relay stores, with filters specific to each relay role:
- Outbox: events authored by the user
- Inbox: events where the user is p-tagged
- DM Inbox: DM events (kind 4, 1059) tagging the user
- Private Home: events authored by the user
- Proxy: total event count
- Search: total event count
- Indexer: kind 0 and kind 10002 counts separately
- Broadcast: no count (as specified)
https://claude.ai/code/session_016158D5mq5BygS1uBbLNbsA
Add an "Encrypt files" toggle (default: on) to the file upload dialog in
NIP-17 encrypted DMs. When encrypted upload fails, show an informative
dialog explaining that many servers don't accept encrypted files on free
accounts, with an option to retry without encryption. The retry dialog
warns that without encryption anyone with the link can see the content.
https://claude.ai/code/session_012xvKzHrZPq3ZTAzBN2LvrR
ViewModel:
- MAX_ACTIVITY_LOG raised to 5000
- Remove activeRelays from LiveSyncActivity (active relay chips removed)
- Add eventsAccepted: Int to CompletedRelayInfo
- Add totalEventsAccepted: Int to SyncState.Done
- Register IRelayClientListener on account.client for the duration of
the sync to intercept OkMessage(success=true) from destination relays
- sourceRelayOfEvent map attributes each forwarded event to the relay
it was first seen on; ConcurrentHashMap.remove() ensures the first
OK true is counted exactly once per event (dedup across dest relays)
- acceptedCountPerRelay accumulates per-source-relay accepted counts,
read at onRelayComplete time
- downloadFromPool.onEvent changed to (Event, NormalizedRelayUrl) to
carry the source relay into runSync routing logic
Screen:
- Remove ActiveRelaysCard and ActiveRelayChip composables + preview
- Remove animation imports (no longer needed)
- ActivityLogRow now shows two right-hand columns per relay:
recv N — events received from that source relay
new N — events accepted as new by destination relays (highlighted
in primary color when > 0)
- DoneCard now shows three lines:
Forwarded N events to destination relays.
M events accepted as new by destination relays. ← key headline
Completed in N seconds.
Strings: event_sync_done_body replaced by done_sent / done_accepted /
done_duration; event_sync_reading_from and events_found_log removed;
log_recv and log_new added.
https://claude.ai/code/session_01U8qF9mK4UBvXsXP1zNMXfX
extract MediaUploadTracker with common methods
bugfixes:
ChatFileUploadState.canPost() was missing isUploadingFile check
ChatNewMessageViewModel.cancel() was never resetting upload flags
Double-reset in ChatFileUploader and ChannelNewMessageViewModel
- All ViewModels track isUploadingFile alongside isUploadingImage, set via hasNonMedia()
- All screen call sites pass source-specific flags so only the initiating button spins
- canPost() methods block posting during file uploads too
- ChatNewMessageViewModel delegates to ChatFileUploadState (fixes pre-existing dead state)
- cancel() methods reset both flags in all ViewModels