Commit Graph

234 Commits

Author SHA1 Message Date
nrobi144 84c4b461a9 fix(release): address code review findings (P1-P3)
P1 fixes (must-fix):
- AppRun: VLC path corrected to usr/lib/app/linux/vlc (jpackage actual
  path), add VLC_PLUGIN_PATH env var for codec discovery
- linuxdeploy: use APPIMAGE_EXTRACT_AND_RUN=1 env var to bypass FUSE
  on CI runners instead of fragile pre-extract + symlink approach
- Prerelease classifier: inverted to positive-match stable format
  (^v[0-9]+\.[0-9]+\.[0-9]+$) across desktop, Android, and composite
  action — eliminates regex drift between allowlists
- assert-stable-release: now accepts inputs (tag, is_prerelease,
  is_draft) so workflow_dispatch on bump workflows also validates

P2 fixes (should-fix):
- .gitignore: add linuxdeploy-*.AppImage and extracted dirs
- RPM version: use replace("-", "~") instead of substringBefore("-")
  for correct RPM prerelease ordering (1.08.0~rc1 < 1.08.0)
- linux-portable → linux alias moved into collect_assets() in
  scripts/asset-name.sh (single source of truth)
- Android cache key: add gradle/libs.versions.toml to hashFiles
- r0adkll/sign-android-release: SHA-pinned to 349ebdef (v1)

P3 fixes (nice-to-have):
- LINUXDEPLOY_OUTPUT_VERSION env var replaced with
  APPIMAGE_EXTRACT_AND_RUN (misleading comment + redundant var)
- nick-fields/retry timeout: 40m → 15m (surface real hangs)
- generate_release_notes: true only on Android job (avoid
  last-writer-wins race across 6 concurrent uploaders)
- apt-get install rpm: tightened guard to matrix.family == 'linux'
  (linux-portable leg doesn't need rpm tooling)
2026-04-17 11:03:01 +03:00
nrobi144 da1037423c feat(desktop): version source-of-truth + RPM + AppImage packaging
Phase 1+2 of multi-platform distribution plan:
- gradle/libs.versions.toml: add `app = "1.08.0"` as single source of truth
- Root build.gradle: allprojects { version = libs.versions.app.get() }
- amethyst/build.gradle: versionName from catalog (versionCode stays local)
- desktopApp/build.gradle.kts: drop hardcoded "1.0.0"; inherit project.version;
  add TargetFormat.Rpm; add linux DSL (menuGroup, appCategory, debMaintainer,
  rpmLicenseType, rpmPackageVersion with dashes stripped)
- desktopApp/build.gradle.kts: new createReleaseAppImage task wrapping
  createReleaseDistributable with linuxdeploy (TargetFormat.AppImage is
  broken in Compose 1.10.x — CMP-7101)
- packaging/appimage/: AppRun launcher (sets LD_LIBRARY_PATH for bundled VLC),
  amethyst.desktop XDG entry, 512x512 icon extracted from icon.icns
- scripts/asset-name.sh: single source for release asset naming contract
2026-04-16 14:35:01 +03:00
Vitor Pamplona e4e7d4816a Fixes desktop compilation 2026-04-15 21:31:40 -04:00
Vitor Pamplona cd8957ad7d Merge pull request #2381 from nrobi144/feat/tor-support
feat: Desktop Tor support — embedded kmp-tor, full privacy routing, settings UI
2026-04-13 11:52:42 -04:00
nrobi144 c9c0cda2c1 feat(tor): full app rebuild on Tor toggle via key() + bootstrap gate + retry
Complete Tor toggle UX:
- key(appRestartKey) wraps App — full Compose tree rebuild on toggle
- TorManager at Window level — survives rebuild, no stop/start collision
- Tor bootstrap gate at top of App — blocks ALL creation (clients, relays,
  Coil, subscriptions) until Tor proxy is ready. Zero clearnet during bootstrap.
- Coil ImageLoader reinitialized after setInstance on each rebuild
- Connection pool evicted on rebuild
- DesktopTorManager retries start up to 3x (handles previous daemon stopping)
- Settings reload from prefs on rebuild (fixes stale toggle display)
- Confirmation dialog on mode change in both TorSettingsSection and Dialog

Known: 2-4 stale Coil CDN connections may persist briefly after toggle
(image loads, not relay traffic — close after OkHttp 5min keep-alive).
Relay WebSocket connections are fully through Tor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 2df621056e feat(tor): restart confirmation dialog + key() app rebuild on Tor toggle
When user changes Tor settings, shows "Restart Required" confirmation
dialog. On confirm: saves prefs, triggers appRestartKey++ which forces
Compose to unmount/remount entire App tree.

Key behaviors:
- Window stays open, user stays logged in (AccountManager outside App)
- Column layout preserved (DeckState outside App)
- DisposableEffect stops old Tor daemon before new one starts
- TorSettingsSection mode change → confirmation dialog
- TorSettingsDialog Save → confirmation dialog
- Relays, subscriptions, caches all recreated fresh

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 01fc5f3ac6 fix(tor): remove broken runtime reconnect, add evictConnections
Remove the LaunchedEffect/scope.launch reconnect code that didn't
work (relay subscriptions lost after disconnect+connect cycle).
Tor toggle will use app rebuild via key() instead (next commit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 70356d541e fix(tor): reconnect relays on Tor OFF too, not just ON
Relay reconnect was only triggered when Tor transitioned to Active.
When toggling OFF, relays stayed on the dead SOCKS proxy and never
reconnected over clearnet — feed went blank.

Now triggers reconnect on both Active and Off transitions, so relays
switch between proxy and direct connections when toggling Tor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 566a4bf452 fix(tor): wire Coil image loading through Tor via custom Fetcher.Factory
Port Android's OkHttpFactory pattern to desktop — creates a custom
Fetcher.Factory<Uri> that calls DesktopHttpClient.currentClient() per
image request, routing all image loads through the SOCKS proxy when
Tor is active.

Avoids the OkHttpNetworkFetcher.factory() import issue by directly
constructing NetworkFetcher with the Tor-aware Call.Factory, matching
the proven Android implementation.

This closes the last network leak — ALL egress paths now route through
Tor when enabled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 11d0a657ac fix(tor): close 8 network traffic leaks — fail-closed when Tor expected
SECURITY: With Tor ON, all HTTP traffic now routes through SOCKS proxy.
Previously only relay WebSocket connections used Tor; 8 other egress
paths created bare OkHttpClient() instances bypassing Tor entirely.

Fail-closed behavior:
- When Tor expected but bootstrapping → dead SOCKS proxy on port 1
  (requests fail instead of leaking IP)
- lateinit var crashes on misconfiguration (loud failure vs silent leak)

Leak sites fixed (all use DesktopHttpClient.currentClient()):
- AnimatedGifImage: GIF fetch
- SaveMediaAction: media downloads
- EncryptedMediaService: NIP-17 DM media
- ServerHealthCheck: Blossom server probes
- NoteActions: zap/lightning LNURL resolution
- DesktopBlossomClient: media uploads
- AccountManager: NIP-46 bunker relay connections
- Coil image loader: TODO (OkHttpNetworkFetcher import issue)

Also:
- Relay reconnect on Tor Active (prevents stale clearnet connections)
- isTorExpected() for fail-closed logic
- Tests updated for new torTypeProvider parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:45 +03:00
nrobi144 8a23a1cd65 fix(tor): move shield to bottom of SinglePane sidebar to prevent cutoff
Shield was getting pushed off screen when RelayHealthIndicator ("x s ago")
appeared above it. Moved to very last position (after BunkerHeartbeat)
so it's always visible at the bottom edge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 df401c823b fix(tor): add TorStatusIndicator to SinglePaneLayout sidebar
Shield icon was only in DeckSidebar (deck mode) but missing from
SinglePaneLayout's NavigationRail. Added between RelayHealthIndicator
and BunkerHeartbeatIndicator. Clicking navigates to Settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 4aa4d39a91 fix(tor): default to Internal + Full Privacy, clickable shield, sticky dialog buttons
UX improvements based on manual testing feedback:

- Default TorType changed from OFF to INTERNAL (Tor on by default)
- Default preset changed to Full Privacy (all routing via Tor)
- Shield icon always visible in sidebar (not hidden when Off)
- Shield icon clickable — opens Settings
- TorSettingsDialog Cancel/Save buttons sticky at bottom (don't scroll away)
- HorizontalDivider above buttons for visual separation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 95ec190b0d feat(tor): wire per-relay routing, .onion badge, shutdown hook, CompositionLocal
Phase 6: Polish and wiring.

Per-relay routing:
- TorRelayEvaluation wired into DesktopHttpClient with actual settings
- shouldUseTorForRelay uses real evaluation instead of { false }
- Settings changes propagate to torTypeFlow/externalPortFlow reactively

CompositionLocal for Tor state:
- LocalTorState provides TorState (status, settings, onSettingsChanged)
- Avoids threading Tor params through every composable layer
- DeckColumnContainer reads from LocalTorState for RelaySettingsScreen

.onion relay badge:
- RelayStatusCard shows "Requires Tor" (red) when Tor is off
- Shows "via Tor" (green) when Tor is active
- Only on .onion relay URLs

Shutdown hook:
- activeTorManager.stopSync() in existing shutdown hook
- Ensures no orphaned Tor processes on app exit
- Set via volatile var from App composable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 e6068c0916 feat(tor): desktop Tor settings UI — indicator, toggle, dialog
Phase 5: Desktop settings UI for Tor.

TorStatusIndicator:
- Shield icon in sidebar footer (gray/yellow/green/red)
- Tooltip shows status (no port number for security)
- Only visible when Tor is not Off

TorSettingsSection:
- Inline in settings screen between Media Server and Dev Settings
- Mode selector (Off/Internal/External) with segmented buttons
- External SOCKS port input with validation (1-65535)
- "Advanced..." button opens full dialog

TorSettingsDialog:
- Full DialogWindow with preset selector (radio buttons)
- 4 relay routing toggles + 7 content routing toggles
- Preset auto-detection (whichPreset)
- Save/Cancel with DesktopTorPreferences persistence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 8a149af34b feat(tor): proxy-aware DesktopHttpClient + relay connection wiring
Phase 3: Network plumbing for Tor-routed relay connections.

DesktopHttpClient refactored from static singleton to Tor-aware class:
- Dual client: direct (30s timeouts) + SOCKS proxy (60s, 2x multiplier)
- Per-relay routing: localhost=direct, .onion=proxy, others via evaluator
- Shared ConnectionPool across client rebuilds
- Companion getSimpleHttpClient() for backward compat (NIP-46 bunker)

DesktopRelayConnectionManager now takes DesktopHttpClient instance.
Main.kt wires TorManager → DesktopHttpClient → RelayManager.

Tests: 10 new DesktopHttpClientTest cases covering Tor on/off,
.onion routing, localhost bypass, timeout multipliers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:44 +03:00
nrobi144 7cad30b899 feat(tor): add kmp-tor desktop daemon + preferences
Phase 2: Desktop Tor runtime implementation.

DesktopTorManager:
- Implements ITorManager using kmp-tor (Apache 2.0)
- Reactive: auto-starts/stops Tor based on TorType flow
- Supports INTERNAL (embedded kmp-tor) and EXTERNAL (user SOCKS port)
- NEWNYM/Dormant/Active signal support via TorCmd
- OS-specific data dirs (~/Library/Application Support, ~/.local/share, %APPDATA%)
- Directory permissions set to 700
- stopSync() for shutdown hook integration

DesktopTorPreferences:
- Implements ITorSettingsPersistence using java.util.prefs.Preferences
- Underscore-separated keys matching project convention
- Default TorType.OFF (user must opt-in)
- No explicit flush() (matches existing DesktopPreferences pattern)

Gradle:
- kmp-tor runtime 2.6.0 + resource-exec-tor 409.5.0 in version catalog
- java.management module added to nativeDistributions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 07:25:43 +03:00
M 4341137e4a fix: VLC discovery for desktop Gradle run task 2026-04-13 06:17:07 +10:00
Claude 7ed036bbaa feat: Phase 7 — Marmot group chat UI & ViewModel layer
Add complete UI layer for Marmot Protocol MLS group messaging:

State models (commons/commonMain/):
- MarmotGroupChatroom: tracks decrypted inner messages per group
- MarmotGroupList: manages all group chatrooms with change notifications
- MarmotGroupFeedFilter: feed filter for group message feeds
- MarmotGroupFeedViewModel: ViewModel exposing group messages

Account integration:
- Add marmotGroupList to IAccount interface and implementations
- GroupEventHandler indexes inner events into MarmotGroupList
- AccountViewModel gains sendMarmotGroupMessage, createMarmotGroup,
  publishMarmotKeyPackage helper methods

Android screens (amethyst/):
- MarmotGroupListScreen: lists all groups with last message preview
- MarmotGroupChatScreen: group conversation with message composer
- MarmotGroupChatView: message feed + simple text composer
- CreateGroupDialog: create new MLS group with display name
- AddMemberDialog: add member by npub/hex (KeyPackage fetch TBD)

Navigation & integration:
- Route.MarmotGroupList and Route.MarmotGroupChat routes
- Wired into AppNavigation nav graph
- "MLS Groups" button added to ChannelFabColumn on Messages screen
- Marmot group newest messages appear in ChatroomListKnownFeedFilter

https://claude.ai/code/session_0194SxKfAU61PY92eqP4cCXM
2026-04-05 20:38:23 +00:00
davotoula c0bdde0ce4 ?: 0L forces Long regardless of nullability, so all comparisons are Long vs Long — no cast issue. 2026-04-03 21:08:05 +02:00
Claude 6116f45888 feat: add EphemeralGiftWrapEvent (kind 21059)
Adds an ephemeral variant of GiftWrap for transient encrypted messages
that relays don't need to persist. EphemeralGiftWrapEvent extends
GiftWrapEvent so all existing processing paths (decryption, routing,
notifications, call signaling) automatically handle it via type
hierarchy. Relay subscription filters are updated to request both kinds.

https://claude.ai/code/session_0157X96G6HLTzYxkdX9pyTSJ
2026-04-02 15:55:44 +00:00
Claude b960a4692a feat: migrate BookmarkListEvent from kind 30001 to 10003
Rename the existing BookmarkListEvent (kind 30001) to OldBookmarkListEvent
and create a new BookmarkListEvent with kind 10003 as per the updated spec.

- OldBookmarkListEvent (kind 30001): Kept as-is for backward compatibility,
  displayed as "Old Bookmarks" in the Bookmark Lists screen
- BookmarkListEvent (kind 10003): New replaceable event, all new bookmark
  operations (save/check/remove) use this kind
- Both kinds are displayed as separate items in the Bookmark Lists screen
- Old Bookmarks screen includes a "Move All to New Bookmarks" button that
  migrates public bookmarks to public and private to private
- Created PrivateReplaceableTagArrayEvent base class for replaceable events
  with encrypted private tags (kind 10003 is in the 10000-19999 range)
- Updated all relay filters, search queries, and profile views to fetch
  both kinds
- Updated Android and Desktop modules

https://claude.ai/code/session_01U9sjQHQMVVHxYiesoXjqop
2026-03-31 13:24:27 +00:00
Vitor Pamplona f291bb126a Merge branch 'main' into claude/migrate-quartz-nip58-XQ8bw 2026-03-31 09:17:11 -04:00
Vitor Pamplona 3dbd79d879 Merge pull request #2052 from vitorpamplona/claude/fix-sorting-order-p23ZY
Improve sorting stability by adding secondary sort keys
2026-03-31 08:42:46 -04:00
Claude 96c4092138 fix: ensure secondary sort by id ascending when sorting by createdAt descending
When events share the same createdAt timestamp, the tiebreaker sort by id
must be ascending to produce a stable, deterministic order. Fixed several
locations that either had no secondary sort, used descending id sort, or
used .reversed() which incorrectly flipped both sort directions.

https://claude.ai/code/session_01RvuyPf1x9wLf2DCgsSXLGz
2026-03-31 03:45:24 +00:00
Claude a4bf093cb3 refactor: migrate NIP-58 badges to nip88Polls-style sub-package structure
Restructures the flat nip58Badges package into sub-packages following
the established nip88Polls pattern with proper tag classes, TagArrayExt,
and TagArrayBuilderExt for each event type:

- definition/ - BadgeDefinitionEvent (kind 30009) with NameTag, ImageTag,
  DescriptionTag, ThumbTag supporting dimensions per spec
- award/ - BadgeAwardEvent (kind 8) with build() method
- profiles/ - BadgeProfilesEvent (kind 30008) with AcceptedBadge tag
  for proper paired a+e tag parsing per NIP-58 spec

Adds build() companion methods to all three event types and full
spec compliance including image/thumb dimension support.

https://claude.ai/code/session_019Uvxfa7jJbjeprLxECoJ8s
2026-03-31 03:08:25 +00:00
davotoula a7259b21ee Merge branch 'main-upstream' into chess-enhancements-and-bug-fixes 2026-03-30 17:40:40 +02:00
davotoula 06fae3ba31 remove unused imports 2026-03-30 17:26:21 +02:00
davotoula f76fc23840 update logging to new style 2026-03-30 16:42:38 +02:00
davotoula 17f071b0f4 Merge branch 'main' into chess-enhancements-and-bug-fixes
# Conflicts:
#	commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/chess/ChessEventBroadcaster.kt
2026-03-30 13:14:33 +02:00
nrobi144 0a41806d7e fix(desktop): feed loading issues — missing events, broken profile navigation
- Profile feed now includes reposts (kind 6/16), not just text notes
- Metadata consume returns false to avoid wasteful null note lookups
- Feed subscription limits raised from 50 to 200 for global/following
- Thread replies subscription fetches NIP-22 comments (kind 1111)
- Cache now handles CommentEvent for thread display
- Wire onNavigateToThread through UserProfileScreen so tapping notes opens threads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 13:33:18 +03:00
nrobi144 882c39fd0e fix(cache): use getOrCreateNote for reply linking to fix flaky thread test
Fixes #2001 — ThreadFilter test failed intermittently because
consumeTextNote used getNoteIfExists for reply-to linking. If the
reply event arrived before the root, the link was lost. Now uses
getOrCreateNote to create placeholders, same pattern as reposts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:44:24 +03:00
nrobi144 2b3005797e feat(desktop): render reposts and quoted notes in feed
- Extract GenericRepostLayout + BoostedMark to commons for cross-platform use
- Feed filters accept kind 6/16 reposts with deduplication by original note
- Relay subscriptions request kinds 1, 6, 16
- Cache consumes GenericRepostEvent (kind 16) and uses getOrCreateNote for
  repost originals to handle out-of-order arrival
- FeedNoteCard renders reposts with overlapping avatars + "Boosted" label
- QuotedNoteEmbed renders nostr:nevent/note references as embedded NoteCards
  with reactive metadata observation
- Direct relay subscriptions fetch missing referenced notes and author metadata
- FeedMetadataCoordinator routes all events (not just metadata) back to cache
- consumeMetadata invalidates note flows when author metadata arrives

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 11:44:24 +03:00
nrobi144 0e8c6fa434 fix(cache): route ReadsScreen following-mode events through cache
Following-mode long-form feed was not calling consumeEvent(),
so LongTextNoteEvents never reached the cache. Back-navigation
showed empty reads because cache had nothing to seed from.
2026-03-30 11:44:23 +03:00
Vitor Pamplona 03c692a8cf Merge pull request #2005 from davotoula/Introduce-log-level-filtering
Introduce log level filtering
2026-03-29 09:56:21 -04:00
davotoula 380026c7be optimise imports 2026-03-29 15:38:51 +02:00
davotoula 250d89ca0a feat: set Log.minLevel in app entry points and migrate android.util.Log usages
- Set Log.minLevel based on BuildConfig.DEBUG in Amethyst.kt init
- Set Log.minLevel = DEBUG in Desktop Main.kt
- Migrate VideoCompressionHelper to quartz Log + LogLevel enum
- Migrate VoiceAnonymizer, VoiceMessagePreview, LiveStatusIndicator to quartz Log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 10:56:18 +02:00
Vitor Pamplona 589bb041b2 Fixes refactoring issues by Claude. 2026-03-28 19:58:04 -04:00
Vitor Pamplona d991af72ff Merge pull request #1986 from vitorpamplona/claude/simplify-nostrclient-api-ZpOTB
Refactor Nostr client API with clearer naming conventions
2026-03-28 18:09:12 -04:00
Vitor Pamplona 20eb95ae57 Removing more warnings 2026-03-28 16:30:00 -04:00
Vitor Pamplona f3cefe0e13 resolving some more warnings 2026-03-28 15:35:45 -04:00
Vitor Pamplona 4db617aeef fixes the need for coroutine scopes 2026-03-28 15:26:29 -04:00
Claude 0eb32ae4fb refactor: migrate LocalClipboardManager to LocalClipboard
Replace deprecated LocalClipboardManager with LocalClipboard across
all 21 files in both amethyst and desktopApp modules.

https://claude.ai/code/session_01FE2NjKxhUYUFRvhyqbYpUH
2026-03-28 18:46:02 +00:00
Vitor Pamplona cfa59dab01 removing more warnings 2026-03-28 14:24:13 -04:00
Claude 4be617e64a revert: keep INostrClient/NostrClient naming and restore onEose/isLive
Reverts three naming changes from the previous refactor:
- Interface stays INostrClient (not NostrClient)
- Class stays NostrClient (not DefaultNostrClient)
- onEose stays onEose (not onCaughtUp)
- isLive stays isLive (not isRealTime)

All other renames from the API simplification are preserved:
subscribe, unsubscribe, publish, count, fetchAll, fetchFirst, etc.

https://claude.ai/code/session_01JPcYCcRx5eZN4GvgGxGwbf
2026-03-28 16:43:30 +00:00
Claude 4e2717c5c5 refactor: simplify NostrClient API for beginner-friendliness
Comprehensive rename of the NostrClient relay infrastructure to use
intuitive, self-documenting names instead of protocol-level jargon.

Interface/class renames:
- INostrClient → NostrClient (interface)
- NostrClient → DefaultNostrClient (implementation)
- IRequestListener → SubscriptionListener
- IRelayClientListener → RelayConnectionListener
- IOpenNostrRequest → SubscriptionHandle
- NostrClientStaticReq → StaticSubscription
- NostrClientDynamicReq → DynamicSubscription

Method renames:
- openReqSubscription() → subscribe()
- close(subId) → unsubscribe(subId)
- send() → publish()
- queryCount() → count()
- subscribe/unsubscribe(listener) → addConnectionListener/removeConnectionListener
- renewFilters() → syncFilters()

Callback renames:
- onEose → onCaughtUp
- isLive → isRealTime
- onStartReq → onSubscriptionStarted
- onCloseReq → onSubscriptionClosed

Extension function renames:
- query() → fetchAll()
- downloadFirstEvent() → fetchFirst()
- req() → subscribe()
- reqAsFlow() → subscribeAsFlow()
- reqUntilEoseAsFlow() → fetchAsFlow()
- sendAndWaitForResponse() → publishAndConfirm()
- queryCountSuspend() → count()
- queryCountMergedHll() → countMerged()
- reqBypassingRelayLimits() → fetchAllPages()
- updateFilter() → refresh() on SubscriptionHandle

https://claude.ai/code/session_01JPcYCcRx5eZN4GvgGxGwbf
2026-03-28 16:43:30 +00:00
Vitor Pamplona 943d46e01a Moves public messages out of experimental 2026-03-28 09:18:34 -04:00
nrobi144 9c9c7d3f24 feat(cache): seed NotificationsScreen from cache on compose
- Add localCache param to NotificationsScreen
- Seed reactions and zaps from cache on initial compose
- Notifications now persist across tab navigation
- All screens now have cache seeding (Feed, Thread, Profile,
  Bookmarks, Reads, Notifications)
2026-03-26 07:20:22 +02:00
nrobi144 072e437277 feat(cache): wire State classes on DesktopIAccount for GC retention
- Add BookmarkListState, Kind3FollowListState, Nip65RelayListState
  to DesktopIAccount — pins important AddressableNotes via strong refs
- Create stub Kind3FollowListRepository + Nip65RelayListRepository
  (no persistence yet, null backups)
- Update followingKeySet() to read from Kind3FollowListState.flow
- Revert accidental long-form branch changes in DeckColumnContainer
2026-03-26 07:17:39 +02:00
nrobi144 cf6a74b162 feat(cache): add periodic memory cleanup for Desktop
- Add cleanMemory() to DesktopLocalCache (sweeps stale WeakRef entries)
- Add startCleanupLoop() to Coordinator with heap monitoring
  (MemoryMXBean, checks every 30s, cleans at >75% heap or every 5min)
- cleanObservers() clears unused NoteFlowSets on notes + addressables
- Try-catch per operation so one failure doesn't skip the rest
- 2-minute startup grace period before first cleanup
- Cleanup job cancelled on clear() (account switch / logout)
2026-03-26 07:14:15 +02:00