diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/nip59GiftWraps/AccountGiftWrapsHistoryEoseManager.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/nip59GiftWraps/AccountGiftWrapsHistoryEoseManager.kt index ecc58ff8f5..ff3f2180b9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/nip59GiftWraps/AccountGiftWrapsHistoryEoseManager.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/relayClient/reqCommand/account/nip59GiftWraps/AccountGiftWrapsHistoryEoseManager.kt @@ -110,14 +110,6 @@ class AccountGiftWrapsHistoryEoseManager( private val _reachedBack = MutableStateFlow(null) val reachedBack: StateFlow = _reachedBack.asStateFlow() - // Rooms-list auto-fill stall mark: the number of THIS protocol's distinct rooms shown the last - // time the list auto-widened it. The list stops widening once a step adds no new room of this - // protocol (widening only pulls older MESSAGES, which for a few busy correspondents can be - // thousands of events without a single new room). Kept here so the stall survives leaving and - // reopening the Messages screen. - @Volatile - var autoFillRoomMark: Int = Int.MIN_VALUE - // Account scope for the watchdog / round collector. Volatile: written on IO (newSub), read on UI. @Volatile private var scope: CoroutineScope? = null @@ -258,7 +250,6 @@ class AccountGiftWrapsHistoryEoseManager( _exhausted.value = exhaustedByUser[user.pubkeyHex] ?: false _relayCount.value = 0 _reachedBack.value = null - autoFillRoomMark = Int.MIN_VALUE lastAskedActive = emptySet() lastRoundEventCount = -1 } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt index 5ad5fc6634..b94820782e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/datasource/ChatroomListNip04HistorySubAssembler.kt @@ -100,10 +100,6 @@ class ChatroomListNip04HistorySubAssembler( @Volatile private var autoLoadAll = false - // Rooms-list auto-fill stall mark for NIP-04 rooms (see the gift-wrap history manager's twin). - @Volatile - var autoFillRoomMark: Int = Int.MIN_VALUE - private fun startUntil() = TimeUtils.now() - AccountGiftWrapsEoseManager.LIVE_TAIL_SECONDS override fun user(key: ChatroomListState) = key.account.userProfile() @@ -208,7 +204,6 @@ class ChatroomListNip04HistorySubAssembler( _exhausted.value = exhaustedByUser[user.pubkeyHex] ?: false _relayCount.value = 0 _reachedBack.value = null - autoFillRoomMark = Int.MIN_VALUE lastAskedActive = emptySet() lastRoundEventCount = -1 } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt index 065b48fcc7..092bd87f5c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/feed/ChatroomListFeedView.kt @@ -112,8 +112,6 @@ private fun CrossFadeState( giftWrapsHistory.loadingMore, giftWrapsHistory.exhausted, roomCount = null, - getMark = { 0 }, - setMark = {}, loadMore = { giftWrapsHistory.loadMore(user) }, ) { feedState is FeedState.Empty } WidenHistoryWhen( @@ -121,8 +119,6 @@ private fun CrossFadeState( nip04History.loadingMore, nip04History.exhausted, roomCount = null, - getMark = { 0 }, - setMark = {}, loadMore = { nip04History.loadMore(user) }, ) { feedState is FeedState.Empty } @@ -178,15 +174,14 @@ private fun FeedLoaded( // while NIP-17 is shallow), so each protocol gets its OWN trigger keyed to its OWN oldest loaded // room — otherwise the deeper protocol's tail pins the boundary to the bottom and the shallower // one never loads until the user scrolls all the way past it. Each is gated only on its own loader - // and its own room count (stall-gate), so they advance independently as the user scrolls. Public / + // and "is my oldest room near the bottom of the viewport", so while the boundary is in view it keeps + // paging to exhaustion (no stall-gate — a visible card means the user is waiting for more). Public / // group / ephemeral rooms are membership-based and excluded. WidenHistoryWhen( "scroll.nip17", giftWrapsHistory.loadingMore, giftWrapsHistory.exhausted, roomCount = { items.list.count { it.event is ChatroomKeyable && it.event !is PrivateDmEvent } }, - getMark = { giftWrapsHistory.autoFillRoomMark }, - setMark = { giftWrapsHistory.autoFillRoomMark = it }, loadMore = { giftWrapsHistory.loadMore(user) }, ) { val info = listState.layoutInfo @@ -199,8 +194,6 @@ private fun FeedLoaded( nip04History.loadingMore, nip04History.exhausted, roomCount = { items.list.count { it.event is PrivateDmEvent } }, - getMark = { nip04History.autoFillRoomMark }, - setMark = { nip04History.autoFillRoomMark = it }, loadMore = { nip04History.loadMore(user) }, ) { val info = listState.layoutInfo @@ -271,15 +264,16 @@ private fun FeedLoaded( private const val PREFETCH_PRIVATE_CHATS = 5 /** - * Drives ONE protocol's history paging from a scroll/empty trigger. When [wantMore] becomes true and - * that protocol isn't already loading or [exhausted], it calls [loadMore]. + * Drives ONE protocol's history paging from a scroll/empty trigger. While [wantMore] is true and that + * protocol isn't already loading or [exhausted], it keeps calling [loadMore] round after round until + * the history is genuinely exhausted (an empty `until`+`limit` page) — there is no stall-gate: if the + * boundary card is in view the user is waiting for more, so we don't stop just because a band of older + * messages surfaced no new conversation row. Paging naturally stops when [wantMore] goes false (the + * boundary scrolls out of view) or the protocol exhausts. * * [wantMore] and [roomCount] are read inside a snapshotFlow, so they may observe live Compose state - * (scroll position, the feed list). [roomCount] (when non-null) feeds the stall-gate: widening only - * pulls older MESSAGES, so a few busy correspondents can flood events without adding a single room — - * paging therefore stops once a step brings in no new room of this protocol (tracked via [getMark] / - * [setMark], which live on the history manager so the stall survives leaving/reopening the screen). - * Pass `roomCount = null` to widen regardless of progress (the empty feed, hunting for the first room). + * (scroll position, the feed list). [roomCount] is only carried for the log line; pass `null` when the + * caller has no room measure (the empty feed, hunting for the first room). * * Each protocol gets its own instance, gated only on its own loader, so NIP-04 and NIP-17 — which have * very different histories — page independently as the user scrolls. @@ -290,8 +284,6 @@ private fun WidenHistoryWhen( loadingMore: StateFlow, exhausted: StateFlow, roomCount: (() -> Int)?, - getMark: () -> Int, - setMark: (Int) -> Unit, loadMore: () -> Unit, wantMore: () -> Boolean, ) { @@ -306,12 +298,6 @@ private fun WidenHistoryWhen( }.distinctUntilChanged() .collect { count -> if (count == NOT_WANTED) return@collect - // Stop once a widen adds no new room of this protocol (but keep hunting while none loaded). - if (roomCount != null && count > 0 && count <= getMark()) { - Log.d("DMPagination") { "rooms.list: widen ($trigger) stop — no new rooms (count=$count)" } - return@collect - } - if (roomCount != null) setMark(count) Log.d("DMPagination") { "rooms.list: widen ($trigger) → loadMore (rooms=$count)" } loadMore() }