show message timestamp

This commit is contained in:
2026-07-04 08:49:36 +07:00
parent a5f544d49f
commit 77d422f20e
2 changed files with 41 additions and 9 deletions

View File

@@ -7,12 +7,16 @@ import android.speech.RecognizerIntent
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut import androidx.compose.animation.scaleOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -97,6 +101,7 @@ import su.reya.coop.Room
import su.reya.coop.Screen import su.reya.coop.Screen
import su.reya.coop.extractUrls import su.reya.coop.extractUrls
import su.reya.coop.formatAsGroupHeader import su.reya.coop.formatAsGroupHeader
import su.reya.coop.formatAsTime
import su.reya.coop.humanReadable import su.reya.coop.humanReadable
import su.reya.coop.isImageUrl import su.reya.coop.isImageUrl
import su.reya.coop.rememberUiState import su.reya.coop.rememberUiState
@@ -554,6 +559,9 @@ fun ChatMessage(
val content = rumor.content() val content = rumor.content()
val images = remember(content) { content.extractUrls().filter { it.isImageUrl() } } val images = remember(content) { content.extractUrls().filter { it.isImageUrl() } }
val cleanedContent = remember(content) { content.removeImageUrls() } val cleanedContent = remember(content) { content.removeImageUrls() }
val timestamp = remember(rumor) { rumor.createdAt().formatAsTime() }
var isMessageClicked by remember { mutableStateOf(false) }
val bubbleShape = if (isMine) { val bubbleShape = if (isMine) {
RoundedCornerShape(topStart = 20.dp, topEnd = 4.dp, bottomStart = 20.dp, bottomEnd = 20.dp) RoundedCornerShape(topStart = 20.dp, topEnd = 4.dp, bottomStart = 20.dp, bottomEnd = 20.dp)
@@ -574,6 +582,12 @@ fun ChatMessage(
contentAlignment = if (isMine) Alignment.CenterEnd else Alignment.CenterStart contentAlignment = if (isMine) Alignment.CenterEnd else Alignment.CenterStart
) { ) {
Column( Column(
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
isMessageClicked = !isMessageClicked
},
horizontalAlignment = if (isMine) Alignment.End else Alignment.Start, horizontalAlignment = if (isMine) Alignment.End else Alignment.Start,
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
@@ -607,6 +621,20 @@ fun ChatMessage(
) )
} }
} }
AnimatedVisibility(
visible = isMessageClicked,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
Text(
text = timestamp,
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.outline,
modifier = Modifier.align(
if (isMine) Alignment.End else Alignment.Start
)
)
}
} }
} }
} }

View File

@@ -153,21 +153,25 @@ fun UnsignedEvent.roomId(): Long {
return sortedUniqueKeys.hashCode().toLong() return sortedUniqueKeys.hashCode().toLong()
} }
fun Timestamp.ago(): String { fun Timestamp.formatAsTime(): String {
val SECONDS_IN_MINUTE = 60L val timeZone = TimeZone.currentSystemDefault()
val MINUTES_IN_HOUR = 60L val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
val HOURS_IN_DAY = 24L val inputDateTime = inputInstant.toLocalDateTime(timeZone)
val DAYS_IN_MONTH = 30L val hour = inputDateTime.hour.toString().padStart(2, '0')
val minute = inputDateTime.minute.toString().padStart(2, '0')
return "$hour:$minute"
}
fun Timestamp.ago(): String {
val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong()) val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
val now = Clock.System.now() val now = Clock.System.now()
val duration = now - inputInstant val duration = now - inputInstant
return when { return when {
duration.inWholeSeconds < SECONDS_IN_MINUTE -> "Now" duration.inWholeSeconds < 60L -> "Now"
duration.inWholeMinutes < MINUTES_IN_HOUR -> "${duration.inWholeMinutes}m" duration.inWholeMinutes < 60L -> "${duration.inWholeMinutes}m"
duration.inWholeHours < HOURS_IN_DAY -> "${duration.inWholeHours}h" duration.inWholeHours < 24L -> "${duration.inWholeHours}h"
duration.inWholeDays < DAYS_IN_MONTH -> "${duration.inWholeDays}d" duration.inWholeDays < 30L -> "${duration.inWholeDays}d"
else -> { else -> {
val localDateTime = inputInstant.toLocalDateTime(TimeZone.currentSystemDefault()) val localDateTime = inputInstant.toLocalDateTime(TimeZone.currentSystemDefault())
val month = val month =