From 3e41bab5d53e6d191358a26a61e6ced79713f776 Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Mon, 23 Mar 2026 13:50:14 +0200 Subject: [PATCH] fix(cache): use relayStatuses (available) instead of connectedRelays for subscriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of "0 relays, 0 notes, 0 follows": all screens used connectedRelays as the relay set for subscriptions, but NostrClient uses relay-on-demand — relays only connect when openReqSubscription is called with their URL. This created a deadlock: screens waited for connected relays, but relays only connect when screens subscribe. Fix: use relayStatuses.keys (registered/available relays) which are populated by addDefaultRelays() at startup. openReqSubscription triggers connection on-demand. Affected screens: FeedScreen, UserProfileScreen, ThreadScreen, NotificationsScreen, BookmarksScreen, ReadsScreen, NewDmDialog. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../amethyst/desktop/ui/BookmarksScreen.kt | 3 ++- .../amethyst/desktop/ui/FeedScreen.kt | 18 +++++++++++------- .../amethyst/desktop/ui/NotificationsScreen.kt | 3 ++- .../amethyst/desktop/ui/ReadsScreen.kt | 3 ++- .../amethyst/desktop/ui/ThreadScreen.kt | 3 ++- .../amethyst/desktop/ui/UserProfileScreen.kt | 3 ++- .../amethyst/desktop/ui/chats/NewDmDialog.kt | 3 ++- 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/BookmarksScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/BookmarksScreen.kt index ac56815dc..9ecbc6b9c 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/BookmarksScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/BookmarksScreen.kt @@ -76,7 +76,8 @@ fun BookmarksScreen( onNavigateToThread: (String) -> Unit = {}, onZapFeedback: (ZapFeedback) -> Unit = {}, ) { - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } val scope = rememberCoroutineScope() // Tab state diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/FeedScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/FeedScreen.kt index 961d61fae..9f12a8163 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/FeedScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/FeedScreen.kt @@ -166,18 +166,22 @@ fun FeedScreen( onNavigateToThread: (String) -> Unit = {}, onZapFeedback: (ZapFeedback) -> Unit = {}, ) { + val relayStatuses by relayManager.relayStatuses.collectAsState() val connectedRelays by relayManager.connectedRelays.collectAsState() val followedUsers by localCache.followedUsers.collectAsState() + // Available relay URLs — openReqSubscription triggers connection on-demand + val allRelayUrls = remember(relayStatuses) { relayStatuses.keys } + var replyToEvent by remember { mutableStateOf(null) } var lightboxState by remember { mutableStateOf(null) } var feedMode by remember { mutableStateOf(initialFeedMode ?: DesktopPreferences.feedMode) } // Subscribe to contact list (kind 3) — populates localCache.followedUsers - rememberSubscription(connectedRelays, account, relayManager = relayManager) { - if (connectedRelays.isNotEmpty() && account != null) { + rememberSubscription(allRelayUrls, account, relayManager = relayManager) { + if (allRelayUrls.isNotEmpty() && account != null) { createContactListSubscription( - relays = connectedRelays, + relays = allRelayUrls, pubKeyHex = account.pubKeyHex, onEvent = { event, _, relay, _ -> subscriptionsCoordinator?.consumeEvent(event, relay) @@ -189,13 +193,13 @@ fun FeedScreen( } // Subscribe to feed events (kind 1) — populates cache via coordinator - rememberSubscription(connectedRelays, feedMode, followedUsers, relayManager = relayManager) { - if (connectedRelays.isEmpty()) return@rememberSubscription null + rememberSubscription(allRelayUrls, feedMode, followedUsers, relayManager = relayManager) { + if (allRelayUrls.isEmpty()) return@rememberSubscription null when (feedMode) { FeedMode.GLOBAL -> { createGlobalFeedSubscription( - relays = connectedRelays, + relays = allRelayUrls, onEvent = { event, _, relay, _ -> subscriptionsCoordinator?.consumeEvent(event, relay) }, @@ -206,7 +210,7 @@ fun FeedScreen( val follows = followedUsers.toList() if (follows.isNotEmpty()) { createFollowingFeedSubscription( - relays = connectedRelays, + relays = allRelayUrls, followedUsers = follows, onEvent = { event, _, relay, _ -> subscriptionsCoordinator?.consumeEvent(event, relay) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt index 1fe3d31fd..6fa7f9225 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt @@ -112,7 +112,8 @@ fun NotificationsScreen( account: AccountState.LoggedIn, subscriptionsCoordinator: DesktopRelaySubscriptionsCoordinator? = null, ) { - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } val scope = rememberCoroutineScope() val notificationState = remember { diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ReadsScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ReadsScreen.kt index c1e6d94bb..78274fbb2 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ReadsScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ReadsScreen.kt @@ -187,7 +187,8 @@ fun ReadsScreen( onNavigateToThread: (String) -> Unit = {}, onZapFeedback: (ZapFeedback) -> Unit = {}, ) { - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } val scope = rememberCoroutineScope() val eventState = diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ThreadScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ThreadScreen.kt index 4b6962c24..ff54ca15b 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ThreadScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/ThreadScreen.kt @@ -89,7 +89,8 @@ fun ThreadScreen( onZapFeedback: (ZapFeedback) -> Unit = {}, onReply: (Event) -> Unit = {}, ) { - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } // Lightbox state var lightboxState by remember { mutableStateOf(null) } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt index e772eda71..80a9919cf 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/UserProfileScreen.kt @@ -124,7 +124,8 @@ fun UserProfileScreen( onNavigateToArticle: (String) -> Unit = {}, onZapFeedback: (ZapFeedback) -> Unit = {}, ) { - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } // User metadata var displayName by remember { mutableStateOf(null) } diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/NewDmDialog.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/NewDmDialog.kt index dd220a06b..d50cbbb33 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/NewDmDialog.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/NewDmDialog.kt @@ -80,7 +80,8 @@ fun NewDmDialog( val cachedUsers by searchState.cachedUserResults.collectAsState() val relaySearchResults by searchState.relaySearchResults.collectAsState() val isSearchingRelays by searchState.isSearchingRelays.collectAsState() - val connectedRelays by relayManager.connectedRelays.collectAsState() + val relayStatuses by relayManager.relayStatuses.collectAsState() + val connectedRelays = remember(relayStatuses) { relayStatuses.keys } val focusRequester = remember { FocusRequester() } // NIP-50 relay search when local cache has few/no results