Sticky date
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user