Sticky date

This commit is contained in:
2026-04-18 22:53:43 +03:00
parent aa18e5b0a6
commit f90a8e3472
@@ -24,6 +24,7 @@ import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.TextView import android.widget.TextView
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -38,7 +39,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -169,7 +169,7 @@ private val headingMetrics = mapOf(
"h6" to TextBlockMetrics(22f, 8f, 10f), "h6" to TextBlockMetrics(22f, 8f, 10f),
) )
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
@Composable @Composable
fun Room(modifier: Modifier = Modifier, rid: String) { fun Room(modifier: Modifier = Modifier, rid: String) {
val roomId = remember(rid) { RoomId(rid) } val roomId = remember(rid) { RoomId(rid) }
@@ -195,9 +195,23 @@ fun Room(modifier: Modifier = Modifier, rid: String) {
Column(modifier) { Column(modifier) {
var message by remember { mutableStateOf("") } var message by remember { mutableStateOf("") }
LazyColumn(Modifier.weight(1f), state = listState, reverseLayout = true) { LaunchedEffect(timelineItems.size) {
items(timelineItems, key = { it.key }) { item -> if (timelineItems.isNotEmpty()) {
TimelineItem(item) listState.scrollToItem(timelineItems.lastIndex)
}
}
LazyColumn(Modifier.weight(1f), state = listState) {
for (timelineItem in timelineItems) {
when (timelineItem) {
is DateDividerItem -> stickyHeader(key = timelineItem.key) {
DateDivider(timelineItem)
}
else -> item(key = timelineItem.key) {
TimelineItem(timelineItem)
}
}
} }
if (timelineItems.isEmpty()) { if (timelineItems.isEmpty()) {
@@ -306,12 +320,12 @@ private fun buildTimelineItems(
val previousMessage = lastMessageIndex val previousMessage = lastMessageIndex
?.let { items.getOrNull(it) as? MessageTimelineItem } ?.let { items.getOrNull(it) as? MessageTimelineItem }
val groupedWithPrevious = previousMessage != null && val groupedWithPrevious = previousMessage != null &&
previousMessage.senderId != null && previousMessage.senderId != null &&
previousMessage.senderId == senderId && previousMessage.senderId == senderId &&
previousMessage.isOwn == isOwn && previousMessage.isOwn == isOwn &&
previousMessage.timestampMs != null && previousMessage.timestampMs != null &&
timestampMs != null && timestampMs != null &&
timestampMs - previousMessage.timestampMs <= MESSAGE_GROUP_WINDOW_MS timestampMs - previousMessage.timestampMs <= MESSAGE_GROUP_WINDOW_MS
if (groupedWithPrevious && previousMessage != null && lastMessageIndex != null) { if (groupedWithPrevious && previousMessage != null && lastMessageIndex != null) {
items[lastMessageIndex] = previousMessage.copy(showTimestamp = false) items[lastMessageIndex] = previousMessage.copy(showTimestamp = false)
@@ -350,7 +364,7 @@ private fun buildTimelineItems(
} }
} }
return items.asReversed() return items
} }
@Composable @Composable
@@ -364,15 +378,25 @@ private fun TimelineItem(item: TimelineUiItem) {
@Composable @Composable
private fun DateDivider(item: DateDividerItem) { private fun DateDivider(item: DateDividerItem) {
Text( Box(
text = item.label,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 8.dp), .padding(vertical = 8.dp),
textAlign = TextAlign.Center, contentAlignment = Alignment.Center,
style = MaterialTheme.typography.labelSmall, ) {
color = MaterialTheme.colorScheme.onSurfaceVariant, Text(
) text = item.label,
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.92f),
shape = RoundedCornerShape(50),
)
.padding(horizontal = 12.dp, vertical = 4.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
} }
@Composable @Composable
@@ -942,8 +966,8 @@ private fun ComplexHtmlRenderer(
val resolvedHeightPx = rawHeightPx.coerceAtLeast(1) val resolvedHeightPx = rawHeightPx.coerceAtLeast(1)
val currentHeightPx = measuredHeightPx ?: estimatedHeightPx val currentHeightPx = measuredHeightPx ?: estimatedHeightPx
val shouldUpdate = measuredHeightPx == null || val shouldUpdate = measuredHeightPx == null ||
resolvedHeightPx > currentHeightPx || resolvedHeightPx > currentHeightPx ||
abs(resolvedHeightPx - currentHeightPx) > updateThresholdPx abs(resolvedHeightPx - currentHeightPx) > updateThresholdPx
if (shouldUpdate) { if (shouldUpdate) {
complexHtmlHeightCache[cacheKey] = resolvedHeightPx complexHtmlHeightCache[cacheKey] = resolvedHeightPx
@@ -999,7 +1023,7 @@ private fun ComplexHtmlRenderer(
val currentView = view ?: return val currentView = view ?: return
currentView.post { currentView.post {
val fallbackHeightPx = (currentView.contentHeight * val fallbackHeightPx = (currentView.contentHeight *
currentView.resources.displayMetrics.density).roundToInt() currentView.resources.displayMetrics.density).roundToInt()
if (fallbackHeightPx > 0) { if (fallbackHeightPx > 0) {
onHeightMeasured(fallbackHeightPx) onHeightMeasured(fallbackHeightPx)
} }