diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt index deea89fdf..457d144f4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt @@ -21,11 +21,14 @@ package com.vitorpamplona.amethyst.ui.note import android.content.Context +import android.text.format.DateFormat import android.text.format.DateUtils import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.quartz.utils.TimeUtils import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date import java.util.Locale import kotlin.math.round @@ -42,6 +45,51 @@ var monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale) var yearNoDayFormatter = SimpleDateFormat(YEAR_NO_DAY_DATE_FORMAT, locale) var monthNoDayFormatter = SimpleDateFormat(MONTH_NO_DAY_DATE_FORMAT, locale) +/** + * Formats a Unix timestamp (seconds) as an absolute date/time string, picking the + * granularity from how far away the timestamp is: + * - same day → time only (e.g. "14:32" / "2:32 PM", locale-aware) + * - same year → "MMM dd, HH:mm" + * - older → "MMM dd, yyyy" + * + * Used by [com.vitorpamplona.amethyst.ui.note.elements.TimeAgo] when the user + * taps the relative timestamp to reveal the absolute one. + */ +fun timeAbsolute( + time: Long?, + context: Context, + prefix: String = " • ", +): String { + if (time == null) return " " + if (time == 0L) return prefix + stringRes(context, R.string.never) + + if (locale != Locale.getDefault()) { + locale = Locale.getDefault() + yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale) + monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale) + } + + val timeMs = time * 1000 + val now = Calendar.getInstance() + val then = Calendar.getInstance().apply { timeInMillis = timeMs } + + val sameYear = now.get(Calendar.YEAR) == then.get(Calendar.YEAR) + val sameDay = sameYear && now.get(Calendar.DAY_OF_YEAR) == then.get(Calendar.DAY_OF_YEAR) + + val timeOfDay = DateFormat.getTimeFormat(context).format(Date(timeMs)) + + return when { + sameDay -> prefix + timeOfDay + sameYear -> prefix + monthFormatter.format(timeMs) + ", " + timeOfDay + else -> prefix + yearFormatter.format(timeMs) + } +} + +fun timeAbsoluteNoDot( + time: Long?, + context: Context, +): String = timeAbsolute(time, context, prefix = "") + fun timeAgo( time: Long?, context: Context, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt index 3e6cb7f7e..59a296e44 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/TimeAgo.kt @@ -20,17 +20,24 @@ */ package com.vitorpamplona.amethyst.ui.note.elements +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.ui.note.timeAbsolute +import com.vitorpamplona.amethyst.ui.note.timeAbsoluteNoDot import com.vitorpamplona.amethyst.ui.note.timeAgo import com.vitorpamplona.amethyst.ui.note.timeAgoShort import com.vitorpamplona.amethyst.ui.stringRes @@ -48,11 +55,14 @@ fun TimeAgo(time: Long) { // Subscribe to the shared coarse ticker; `derivedStateOf` ensures the Text only // recomposes when the formatted string actually flips (e.g. 1m → 2m), not on every tick. val nowState = LocalNowSeconds.current + var showAbsolute by rememberSaveable(time) { mutableStateOf(false) } + val interactionSource = remember { MutableInteractionSource() } + val timeStr by remember(time, context, nowState) { derivedStateOf { nowState.value - timeAgo(time, context = context) + if (showAbsolute) timeAbsolute(time, context) else timeAgo(time, context = context) } } @@ -60,6 +70,11 @@ fun TimeAgo(time: Long) { text = timeStr, color = MaterialTheme.colorScheme.placeholderText, maxLines = 1, + modifier = + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + ) { showAbsolute = !showAbsolute }, ) } @@ -70,12 +85,16 @@ fun NormalTimeAgo( ) { val nowStr = stringRes(id = R.string.now) val nowState = LocalNowSeconds.current + val context = LocalContext.current + var showAbsolute by rememberSaveable(baseNote.idHex) { mutableStateOf(false) } + val interactionSource = remember { MutableInteractionSource() } val time by remember(baseNote, nowStr, nowState) { derivedStateOf { nowState.value - timeAgoShort(baseNote.createdAt() ?: 0L, nowStr) + val createdAt = baseNote.createdAt() ?: 0L + if (showAbsolute) timeAbsoluteNoDot(createdAt, context) else timeAgoShort(createdAt, nowStr) } } @@ -83,6 +102,10 @@ fun NormalTimeAgo( text = time, maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = modifier, + modifier = + modifier.clickable( + interactionSource = interactionSource, + indication = null, + ) { showAbsolute = !showAbsolute }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt index cce77ba4e..cef3ef345 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/ChatTimeAgo.kt @@ -20,6 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size @@ -28,7 +30,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -38,6 +43,7 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.note.elements.LocalNowSeconds +import com.vitorpamplona.amethyst.ui.note.timeAbsoluteNoDot import com.vitorpamplona.amethyst.ui.note.timeAgoShort import com.vitorpamplona.amethyst.ui.note.timeAheadNoDot import com.vitorpamplona.amethyst.ui.stringRes @@ -50,11 +56,16 @@ import com.vitorpamplona.quartz.nip40Expiration.expiration fun ChatTimeAgo(baseNote: Note) { val nowStr = stringRes(id = R.string.now) val nowState = LocalNowSeconds.current + val context = LocalContext.current + var showAbsolute by rememberSaveable(baseNote.idHex) { mutableStateOf(false) } + val interactionSource = remember { MutableInteractionSource() } + val time by remember(baseNote, nowStr, nowState) { derivedStateOf { nowState.value - timeAgoShort(baseNote.createdAt() ?: 0L, nowStr) + val createdAt = baseNote.createdAt() ?: 0L + if (showAbsolute) timeAbsoluteNoDot(createdAt, context) else timeAgoShort(createdAt, nowStr) } } @@ -63,6 +74,11 @@ fun ChatTimeAgo(baseNote: Note) { color = MaterialTheme.colorScheme.placeholderText, fontSize = Font12SP, maxLines = 1, + modifier = + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + ) { showAbsolute = !showAbsolute }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt index 8f25e959e..495af1173 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChatroomHeaderCompose.kt @@ -20,6 +20,8 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer import androidx.compose.material3.LocalTextStyle @@ -31,6 +33,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -62,6 +65,7 @@ import com.vitorpamplona.amethyst.ui.note.LoadPublicChatChannel import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures import com.vitorpamplona.amethyst.ui.note.ObserveDraftEvent import com.vitorpamplona.amethyst.ui.note.elements.LocalNowSeconds +import com.vitorpamplona.amethyst.ui.note.timeAbsolute import com.vitorpamplona.amethyst.ui.note.timeAgo import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.RoomNameDisplay @@ -486,17 +490,24 @@ private fun TimeAgo(channelLastTime: Long?) { val context = LocalContext.current val nowState = LocalNowSeconds.current + var showAbsolute by rememberSaveable(channelLastTime) { mutableStateOf(false) } + val interactionSource = remember { MutableInteractionSource() } val timeAgo by remember(channelLastTime, context, nowState) { derivedStateOf { nowState.value - timeAgo(channelLastTime, context) + if (showAbsolute) timeAbsolute(channelLastTime, context) else timeAgo(channelLastTime, context) } } Text( text = timeAgo, color = MaterialTheme.colorScheme.grayText, maxLines = 1, + modifier = + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + ) { showAbsolute = !showAbsolute }, ) } diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeAgoFormatter.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeAgoFormatter.kt index f03474017..3234edb75 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeAgoFormatter.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/util/TimeAgoFormatter.kt @@ -21,7 +21,10 @@ package com.vitorpamplona.amethyst.commons.util import com.vitorpamplona.quartz.utils.TimeUtils +import java.text.DateFormat import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Date import java.util.Locale private const val YEAR_DATE_FORMAT = "MMM dd, yyyy" @@ -30,12 +33,14 @@ private const val MONTH_DATE_FORMAT = "MMM dd" private var locale = Locale.getDefault() private var yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale) private var monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale) +private var timeOnlyFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, locale) private fun updateFormattersIfNeeded() { if (locale != Locale.getDefault()) { locale = Locale.getDefault() yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale) + timeOnlyFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, locale) } } @@ -145,3 +150,39 @@ fun dateFormatter( * Extension function for Long timestamps. */ fun Long.toTimeAgo(withDot: Boolean = true): String = timeAgo(this, withDot) + +/** + * Formats a Unix timestamp (seconds) as an absolute date/time string. Granularity + * depends on how far in the past the timestamp is: + * - same day → time only (e.g. "14:32"), locale-aware + * - same year → "MMM dd, HH:mm" + * - older → "MMM dd, yyyy" + */ +fun timeAbsolute( + time: Long?, + withDot: Boolean = true, + never: String = "never", +): String { + if (time == null) return " " + val prefix = if (withDot) " • " else "" + if (time == 0L) return prefix + never + + updateFormattersIfNeeded() + + val timeMs = time * 1000 + val now = Calendar.getInstance() + val then = Calendar.getInstance().apply { timeInMillis = timeMs } + + val sameYear = now.get(Calendar.YEAR) == then.get(Calendar.YEAR) + val sameDay = sameYear && now.get(Calendar.DAY_OF_YEAR) == then.get(Calendar.DAY_OF_YEAR) + + val timeOfDay = timeOnlyFormatter.format(Date(timeMs)) + + return when { + sameDay -> prefix + timeOfDay + sameYear -> prefix + monthFormatter.format(timeMs) + ", " + timeOfDay + else -> prefix + yearFormatter.format(timeMs) + } +} + +fun Long.toTimeAbsolute(withDot: Boolean = true): String = timeAbsolute(this, withDot) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt index 319d10111..48cf62403 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/NotificationsScreen.kt @@ -56,13 +56,13 @@ import com.vitorpamplona.amethyst.commons.icons.symbols.rememberMaterialSymbolPa import com.vitorpamplona.amethyst.commons.state.EventCollectionState import com.vitorpamplona.amethyst.commons.ui.components.EmptyState import com.vitorpamplona.amethyst.commons.ui.components.LoadingState -import com.vitorpamplona.amethyst.commons.util.toTimeAgo import com.vitorpamplona.amethyst.desktop.account.AccountState import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager import com.vitorpamplona.amethyst.desktop.subscriptions.DesktopRelaySubscriptionsCoordinator import com.vitorpamplona.amethyst.desktop.subscriptions.createNotificationsSubscription import com.vitorpamplona.amethyst.desktop.subscriptions.rememberSubscription +import com.vitorpamplona.amethyst.desktop.ui.components.ToggleableTimeAgoText import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.hexToByteArrayOrNull import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent @@ -342,8 +342,8 @@ fun NotificationCard(notification: NotificationItem) { ) } - Text( - text = notification.timestamp.toTimeAgo(withDot = false), + ToggleableTimeAgoText( + timestamp = notification.timestamp, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f), ) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt index 8082ea476..da094091c 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ChatPane.kt @@ -84,10 +84,10 @@ import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider import com.vitorpamplona.amethyst.commons.service.upload.UploadOrchestrator import com.vitorpamplona.amethyst.commons.ui.components.LoadingState import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState -import com.vitorpamplona.amethyst.commons.util.toTimeAgo import com.vitorpamplona.amethyst.commons.viewmodels.ChatNewMessageState import com.vitorpamplona.amethyst.commons.viewmodels.ChatroomFeedViewModel import com.vitorpamplona.amethyst.desktop.DesktopPreferences +import com.vitorpamplona.amethyst.desktop.ui.components.ToggleableTimeAgoText import com.vitorpamplona.amethyst.desktop.ui.media.DesktopFilePicker import com.vitorpamplona.amethyst.desktop.ui.media.MediaAttachmentRow import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle @@ -528,8 +528,8 @@ private fun MessageWithReactions( // Timestamp note.createdAt()?.let { timestamp -> - Text( - text = timestamp.toTimeAgo(withDot = false), + ToggleableTimeAgoText( + timestamp = timestamp, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), ) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ConversationListPane.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ConversationListPane.kt index ed7ee475a..79c470c15 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ConversationListPane.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/chats/ConversationListPane.kt @@ -66,7 +66,7 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.commons.ui.components.UserAvatar -import com.vitorpamplona.amethyst.commons.util.toTimeAgo +import com.vitorpamplona.amethyst.desktop.ui.components.ToggleableTimeAgoText import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey import kotlinx.coroutines.launch @@ -363,8 +363,8 @@ private fun ConversationCard( if (item.lastMessageTimestamp > 0) { Spacer(Modifier.width(8.dp)) - Text( - text = item.lastMessageTimestamp.toTimeAgo(withDot = false), + ToggleableTimeAgoText( + timestamp = item.lastMessageTimestamp, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f), ) diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/components/ToggleableTimeAgoText.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/components/ToggleableTimeAgoText.kt new file mode 100644 index 000000000..5852bb27e --- /dev/null +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/components/ToggleableTimeAgoText.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.desktop.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import com.vitorpamplona.amethyst.commons.util.timeAbsolute +import com.vitorpamplona.amethyst.commons.util.toTimeAgo + +/** + * Timestamp text that toggles between a relative ("5m") and an absolute, scale-adjusted + * ("14:32" / "Dec 12, 14:32" / "Dec 12, 2023") form when the user clicks it. + * + * The toggle is local to the call site and persists across recomposition via + * [rememberSaveable], so it survives scroll-induced disposal in a LazyColumn. + */ +@Composable +fun ToggleableTimeAgoText( + timestamp: Long, + style: TextStyle, + color: Color, + modifier: Modifier = Modifier, +) { + var showAbsolute by rememberSaveable(timestamp) { mutableStateOf(false) } + val interactionSource = remember { MutableInteractionSource() } + val text = + if (showAbsolute) timeAbsolute(timestamp, withDot = false) else timestamp.toTimeAgo(withDot = false) + + Text( + text = text, + style = style, + color = color, + modifier = + modifier.clickable( + interactionSource = interactionSource, + indication = null, + ) { showAbsolute = !showAbsolute }, + ) +} diff --git a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/NoteCard.kt b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/NoteCard.kt index 879ae5ca2..1b0208f63 100644 --- a/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/NoteCard.kt +++ b/desktopApp/src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/ui/note/NoteCard.kt @@ -59,8 +59,8 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.UrlParser import com.vitorpamplona.amethyst.commons.richtext.Urls import com.vitorpamplona.amethyst.commons.ui.components.UserAvatar -import com.vitorpamplona.amethyst.commons.util.toTimeAgo import com.vitorpamplona.amethyst.desktop.cache.DesktopLocalCache +import com.vitorpamplona.amethyst.desktop.ui.components.ToggleableTimeAgoText import com.vitorpamplona.amethyst.desktop.ui.media.AnimatedGifImage import com.vitorpamplona.amethyst.desktop.ui.media.AudioPlayer import com.vitorpamplona.amethyst.desktop.ui.media.DesktopVideoPlayer @@ -202,8 +202,8 @@ fun NoteCard( } // Timestamp - Text( - text = note.createdAt.toTimeAgo(withDot = false), + ToggleableTimeAgoText( + timestamp = note.createdAt, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant, )