fix: compact relay markers + no '0 relays' flash on the DM loading card

Two UI papercuts in the per-relay DM history surface:

- RelayReachMarker listed every relay name comma-joined per state, so a
  gap shared by many relays (e.g. all nine clustered at the live-tail
  floor on first open) overflowed into an unreadable line. Now each state
  shows the relay's host name only when it is the sole one of its state at
  that depth (the usual converged case); otherwise just a count, with
  maxLines/ellipsis as a backstop.
- The history status card briefly read 'loading from 0 relays': the count
  populated a beat after loadingMore flipped true (and again as the last
  relay settled). Set the relay count before raising the spinner in
  loadMore, and defensively drop the relay clause from the card subtitle
  when the count is 0.

https://claude.ai/code/session_01B1fmmmX8JjQWH3amMLdvcW
This commit is contained in:
Claude
2026-06-03 18:50:13 +00:00
parent ab7aa44d21
commit 40b60e7bcc
3 changed files with 16 additions and 3 deletions
@@ -162,6 +162,9 @@ private fun historySubtitle(
relayCount: Int,
reachedBack: Long?,
): String {
// A transient frame can carry loading=true with relayCount=0 (the count updates a beat after the
// spinner flips, and again as the last relay settles); don't render a nonsensical "0 relays".
if (relayCount <= 0) return protocolTag
val backLabel =
remember(reachedBack) {
reachedBack?.let { SimpleDateFormat("MMM yyyy", Locale.getDefault()).format(Date(it * 1000)) }
@@ -30,6 +30,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -58,6 +59,11 @@ data class RelayReach(
* As a relay loads older history its reached cursor drops, so the caller places this marker further
* down (older) in the stream — relays that race ahead leave their marker deep while slower relays'
* markers trail higher up, converging as they catch up.
*
* Each state in the gap renders one compact label: a relay's host name when it is the only one of its
* state there (the usual converged case, where each relay sits at its own depth), or just a count when
* several pile up at the same depth (e.g. all nine clustered at the live-tail floor on first open) so
* the line can't grow into an unreadable comma list.
*/
@Composable
fun RelayReachMarker(entries: List<RelayReach>) {
@@ -65,20 +71,21 @@ fun RelayReachMarker(entries: List<RelayReach>) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
modifier = HalfPadding,
) {
HorizontalDivider(modifier = Modifier.weight(1f), thickness = DividerThickness)
// Group by state so a gap shared by several relays reads as e.g. "✓ vitor, nos.lol ↓ wine".
entries
.groupBy { it.state }
.toSortedMap(compareBy { it.ordinal })
.forEach { (state, list) ->
Text(
text = glyph(state) + " " + list.joinToString(", ") { it.name },
text = glyph(state) + " " + if (list.size == 1) list.first().name else list.size.toString(),
color = color(state),
fontSize = 11.sp,
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
HorizontalDivider(modifier = Modifier.weight(1f), thickness = DividerThickness)
@@ -200,6 +200,9 @@ class ChatroomNip04HistorySubAssembler(
if (!windowActive) {
windowActive = true
windowFloor = startUntil()
// Populate the relay count BEFORE raising the spinner, so the status card never renders
// a "loading from 0 relays" frame between loadingMore flipping true and the first progress.
publishProgress()
_loadingMore.value = true
windowLoad.startLoading(it)
}