From 0c2a68b86e71af0c65b5cbcd5473773965265eea Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Apr 2026 19:36:32 +0000 Subject: [PATCH] feat: preload bookmark events via EventFinderFilterAssembler Subscribe all bookmark and pinned note IDs to the EventFinderFilterAssembler when the bookmark screen opens, so events are fetched in bulk via a single REQ rather than loading one-by-one as the user scrolls. Applied to both BookmarkListScreen (new bookmarks + pinned notes) and OldBookmarkListScreen (legacy bookmarks). https://claude.ai/code/session_01C8gevBfB8vLDFoBW3hnPeU --- .../default/BookmarkListScreen.kt | 31 +++++++++++++++++++ .../old/OldBookmarkListScreen.kt | 30 ++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/default/BookmarkListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/default/BookmarkListScreen.kt index 11d584796..7c0e3be39 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/default/BookmarkListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/default/BookmarkListScreen.kt @@ -31,8 +31,10 @@ import androidx.compose.material3.SecondaryScrollableTabRow import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -40,6 +42,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderQueryState import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton @@ -90,6 +93,9 @@ fun BookmarkListScreen( pinnedNotesFeedViewModel.invalidateData() } + // Preload all bookmarked and pinned events so they don't load one-by-one when scrolling + PreloadBookmarkEvents(bookmarkState, pinState, accountViewModel) + RenderBookmarkScreen(publicFeedViewModel, privateFeedViewModel, pinnedNotesFeedViewModel, accountViewModel, nav) } @@ -171,3 +177,28 @@ private fun RenderBookmarkScreen( } } } + +@Composable +private fun PreloadBookmarkEvents( + bookmarkState: com.vitorpamplona.amethyst.commons.model.nip51Lists.BookmarkListState.BookmarkList?, + pinState: List?, + accountViewModel: AccountViewModel, +) { + val eventFinder = accountViewModel.dataSources().eventFinder + val account = accountViewModel.account + + val queries = + remember(bookmarkState, pinState) { + val allNotes = (bookmarkState?.public.orEmpty() + bookmarkState?.private.orEmpty() + pinState.orEmpty()) + allNotes + .filter { it.event == null } + .map { EventFinderQueryState(it, account) } + } + + DisposableEffect(queries) { + eventFinder.subscribe(queries) + onDispose { + eventFinder.unsubscribe(queries) + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt index 107b3e2bd..866852bf3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt @@ -36,8 +36,10 @@ import androidx.compose.material3.SecondaryTabRow import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -45,6 +47,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFinderQueryState import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton @@ -81,6 +84,9 @@ fun OldBookmarkListScreen( privateFeedViewModel.invalidateData() } + // Preload all bookmarked events so they don't load one-by-one when scrolling + PreloadOldBookmarkEvents(bookmarkState, accountViewModel) + RenderOldBookmarkScreen(publicFeedViewModel, privateFeedViewModel, accountViewModel, nav) } @@ -172,3 +178,27 @@ private fun RenderOldBookmarkScreen( } } } + +@Composable +private fun PreloadOldBookmarkEvents( + bookmarkState: com.vitorpamplona.amethyst.commons.model.nip51Lists.OldBookmarkListState.BookmarkList?, + accountViewModel: AccountViewModel, +) { + val eventFinder = accountViewModel.dataSources().eventFinder + val account = accountViewModel.account + + val queries = + remember(bookmarkState) { + val allNotes = bookmarkState?.public.orEmpty() + bookmarkState?.private.orEmpty() + allNotes + .filter { it.event == null } + .map { EventFinderQueryState(it, account) } + } + + DisposableEffect(queries) { + eventFinder.subscribe(queries) + onDispose { + eventFinder.unsubscribe(queries) + } + } +}