mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 16:33:27 +00:00
feat: drop the rooms-list stall-gate — page to exhaustion while in view
The stall-gate stopped widening once a load surfaced older messages but no new conversation row, leaving the boundary card in a confusing paused state (not loading, not caught-up). It existed to brake the OLD pagination model, where every widen re-downloaded the whole window; the per-relay until+limit paging doesn't re-download, so the brake is obsolete — and a visible boundary card means the user is waiting for more, so pausing there made no sense. Now while the boundary is in view each protocol pages round after round until genuinely exhausted (empty page), then shows 'all caught up'. The card is only ever loading or caught-up, never paused. Removes the autoFillRoomMark mark plumbing and the getMark/setMark gate. Tradeoff: a user with few conversations but deep message history pages that history to the end on reaching the bottom — bounded and efficient now (no re-download), terminated by exhaustion + the no-progress guard. https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
This commit is contained in:
-9
@@ -110,14 +110,6 @@ class AccountGiftWrapsHistoryEoseManager(
|
||||
private val _reachedBack = MutableStateFlow<Long?>(null)
|
||||
val reachedBack: StateFlow<Long?> = _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
|
||||
}
|
||||
|
||||
-5
@@ -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
|
||||
}
|
||||
|
||||
+10
-24
@@ -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<Boolean>,
|
||||
exhausted: StateFlow<Boolean>,
|
||||
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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user