diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt index 62abf89d7a..b1c16cf924 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/RelayGroupChannelListScreen.kt @@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupWarmupSubscription import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.relayGroup.datasource.RelayGroupsOnRelaySubscription import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter @@ -67,11 +68,15 @@ import com.vitorpamplona.quartz.nip01Core.relay.normalizer.RelayUrlNormalizer import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl import com.vitorpamplona.quartz.nip29RelayGroups.metadata.GroupMetadataEvent +/** A first screen's worth of recent messages to prefetch per visible group card, ahead of a tap. */ +private const val CHANNEL_LIST_WARMUP_LIMIT = 10 + /** * Lists every channel a relay hosts (its kind 39000-39003 directory), so the user * can browse and open channels on that relay. The relay's directory is streamed by * [RelayGroupsOnRelaySubscription] and consumed into per-group channels; this - * screen reads them back for the relay and renders them. + * screen reads them back for the relay and renders them. Each visible card also warms + * its group's recent messages so opening a chat lands on cached content. */ @Composable fun RelayGroupChannelListScreen( @@ -156,6 +161,18 @@ private fun RelayGroupChannelRow( val joined = channel.membershipOf(myPubkey).isMember() val memberCount = channel.memberCount() + // Anticipate a tap: while this row is on-screen, prefetch a first screen's worth of recent + // messages for its group (content only — the directory subscription already streams metadata), + // so opening the chat lands on cached content instead of a blank load. Bounded to visible rows + // by the LazyColumn, and released as they scroll off. + RelayGroupWarmupSubscription( + channel, + accountViewModel.dataSources().relayGroupWarmup, + accountViewModel, + contentOnly = true, + contentLimit = CHANNEL_LIST_WARMUP_LIMIT, + ) + Row( modifier = Modifier diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupFilterAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupFilterAssembler.kt index fb26d7b829..5aff994a54 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupFilterAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupFilterAssembler.kt @@ -40,6 +40,8 @@ class RelayGroupWarmupQueryState( val channel: RelayGroupChannel, /** When true, prefetch only recent content — the caller's screen already streams metadata. */ val contentOnly: Boolean = false, + /** How many recent content events to prefetch ahead of a tap. */ + val contentLimit: Int = RELAY_GROUP_WARMUP_LIMIT, ) /** Newest content kinds we prefetch so opening the card lands on populated screens. */ @@ -47,11 +49,12 @@ private val RELAY_GROUP_WARMUP_CONTENT_KINDS = listOf(ChatEvent.KIND, PollEvent.KIND, ThreadEvent.KIND, CommentEvent.KIND) /** - * How many recent events to pull ahead of a tap — enough to fill the first screen AND drive the - * discovery card's "50+ messages" activity signal (a chat that returns the full page reads as - * "50+"; fewer shows the exact loaded count). + * Default number of recent events to pull ahead of a tap — enough to fill the first screen AND drive + * the discovery card's "50+ messages" activity signal (a chat that returns the full page reads as + * "50+"; fewer shows the exact loaded count). Callers that only need a first-screen preview (e.g. a + * relay's channel list) pass a smaller [RelayGroupWarmupQueryState.contentLimit]. */ -private const val RELAY_GROUP_WARMUP_LIMIT = 50 +const val RELAY_GROUP_WARMUP_LIMIT = 50 /** * Warms a NIP-29 group referenced inline (a group-link card) without opening it: @@ -92,7 +95,7 @@ class RelayGroupWarmupSubAssembler( Filter( kinds = RELAY_GROUP_WARMUP_CONTENT_KINDS, tags = mapOf(GroupIdTag.TAG_NAME to listOf(groupId.id)), - limit = RELAY_GROUP_WARMUP_LIMIT, + limit = key.contentLimit, since = since?.get(groupId.relayUrl)?.time, ), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupSubscription.kt index 86440fdae9..fe8d9aafd8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/relayGroup/datasource/RelayGroupWarmupSubscription.kt @@ -37,10 +37,11 @@ fun RelayGroupWarmupSubscription( dataSource: RelayGroupWarmupFilterAssembler, accountViewModel: AccountViewModel, contentOnly: Boolean = false, + contentLimit: Int = RELAY_GROUP_WARMUP_LIMIT, ) { val state = - remember(channel.groupId, contentOnly) { - RelayGroupWarmupQueryState(channel, contentOnly) + remember(channel.groupId, contentOnly, contentLimit) { + RelayGroupWarmupQueryState(channel, contentOnly, contentLimit) } LifecycleAwareKeyDataSourceSubscription(state, dataSource)