From 3b489fb3cb8832c89e8cfb52bac2a4408bd161bb Mon Sep 17 00:00:00 2001 From: nrobi144 Date: Mon, 23 Mar 2026 14:36:09 +0200 Subject: [PATCH] feat(cache): seed Reads and Bookmarks screens from cache on compose Both screens now show cached data instantly on navigation instead of showing loading states while re-fetching from relays. - BookmarksScreen: reads cached BookmarkListEvent from addressableNotes, seeds public bookmark events from notes cache - ReadsScreen: seeds long-form notes (kind 30023) from notes cache Relay subscriptions still run to fetch fresh data in parallel. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../amethyst/desktop/ui/BookmarksScreen.kt | 21 +++++++++++++++++++ .../amethyst/desktop/ui/ReadsScreen.kt | 13 ++++++++++++ 2 files changed, 34 insertions(+) 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 9ecbc6b9c..e692b6470 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 @@ -121,6 +121,27 @@ fun BookmarksScreen( } } + // Seed from cache — bookmark list event may already be in addressableNotes + LaunchedEffect(account.pubKeyHex) { + val address = BookmarkListEvent.createBookmarkAddress(account.pubKeyHex) + val cachedNote = localCache.getOrCreateAddressableNote(address) + val cachedEvent = cachedNote.event as? BookmarkListEvent + if (cachedEvent != null) { + bookmarkList = cachedEvent + publicBookmarkIds = + cachedEvent + .publicBookmarks() + .filterIsInstance() + .map { it.eventId } + // Seed public events from cache + publicBookmarkIds.forEach { id -> + val note = localCache.getNoteIfExists(id) + val event = note?.event + if (event != null) publicEventState.addItem(event) + } + } + } + // Subscribe to user's bookmark list (kind 30001) rememberSubscription(connectedRelays, account.pubKeyHex, relayManager = relayManager) { if (connectedRelays.isNotEmpty()) { 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 78274fbb2..80438b7b0 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 @@ -46,6 +46,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -207,6 +208,18 @@ fun ReadsScreen( var eoseReceivedCount by remember { mutableStateOf(0) } val initialLoadComplete = eoseReceivedCount > 0 + // Seed from cache — long-form notes already consumed are in cache + LaunchedEffect(Unit) { + val cached = + localCache.notes.filterIntoSet { _, note -> + note.event is LongTextNoteEvent + } + cached.forEach { note -> + (note.event as? LongTextNoteEvent)?.let { eventState.addItem(it) } + } + if (cached.isNotEmpty()) eoseReceivedCount++ + } + // Load followed users for Following feed mode rememberSubscription(connectedRelays, account, feedMode, relayManager = relayManager) { val connectedRelays = connectedRelays