diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt index 874936fe20..c082b39990 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Text.kt @@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.ui.note.ReplyNoteComposition import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags import com.vitorpamplona.amethyst.ui.note.nip22Comments.DisplayExternalId import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources.PreloadThreadForReply import com.vitorpamplona.amethyst.ui.theme.HalfVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.placeholderText @@ -82,6 +83,10 @@ fun RenderTextEvent( val noteEvent = note.event ?: return if (unPackReply != ReplyRenderType.NONE) { + // Eagerly pull the rest of this reply's thread while it's on screen, so opening + // the conversation finds it already loaded. No-op when the note is a root itself. + PreloadThreadForReply(note, accountViewModel) + val canShowReply by remember(note) { derivedStateOf { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt index f30c0221c8..75e206a528 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/datasources/ThreadFilterAssemblerSubscription.kt @@ -22,8 +22,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.datasources import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import com.vitorpamplona.amethyst.commons.model.ThreadAssembler import com.vitorpamplona.amethyst.commons.relayClient.subscriptions.LifecycleAwareKeyDataSourceSubscription import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.LocalCache +import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.quartz.nip01Core.core.HexKey @@ -52,3 +55,29 @@ fun ThreadFilterAssemblerSubscription( LifecycleAwareKeyDataSourceSubscription(state, filterAssembler) } + +/** + * Eagerly pre-loads the whole thread of a reply that is visible in a feed. + * + * When a reply shows up in `NoteCompose`, this resolves the thread root and opens + * the same root subscription the thread screen uses (a filter on the root's `e`/`a` + * tag, covering NIP-10 and NIP-22 event/addressable roots), so tapping into the + * conversation finds it already loaded. Keying on the resolved root id means every + * visible reply that shares a root collapses onto a single subscription, and a note + * that is itself a root (no parent) is skipped — nothing to pre-load. + */ +@Composable +fun PreloadThreadForReply( + note: Note, + accountViewModel: AccountViewModel, +) { + val rootId = + remember(note) { + val root = ThreadAssembler(LocalCache).findRoot(note.idHex) + if (root != null && root != note) root.idHex else null + } + + if (rootId != null) { + ThreadFilterAssemblerSubscription(rootId, accountViewModel) + } +}