feat: Add "now" string and update formatAgo function (#4183)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich
2026-01-10 20:25:21 -06:00
committed by GitHub
parent c5c7c2b454
commit 0591e9186b
5 changed files with 37 additions and 36 deletions

View File

@@ -1088,4 +1088,5 @@
<string name="compass_uncertainty">Estimated area: \u00b1%1$s (\u00b1%2$s)</string>
<string name="compass_uncertainty_unknown">Estimated area: unknown accuracy</string>
<string name="mark_as_read">Mark as read</string>
<string name="now">Now</string>
</resources>

View File

@@ -17,16 +17,38 @@
package org.meshtastic.core.ui.util
import android.text.format.DateUtils
import com.meshtastic.core.strings.getString
import org.meshtastic.core.strings.Res
import org.meshtastic.core.strings.now
import java.lang.System.currentTimeMillis
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
@Suppress("MagicNumber")
fun formatAgo(lastSeenUnix: Int, currentTimeMillis: Long = System.currentTimeMillis()): String {
val timeInMillis = lastSeenUnix * 1000L
/**
* Formats a given Unix timestamp (in seconds) into a relative "time ago" string.
*
* For durations less than a minute, it returns "now". For longer durations, it uses Android's
* `DateUtils.getRelativeTimeSpanString` to generate a concise, localized, and abbreviated representation (e.g., "5m
* ago", "2h ago").
*
* @param lastSeenUnixSeconds The Unix timestamp in seconds to be formatted.
* @return A [String] representing the relative time that has passed.
*/
fun formatAgo(lastSeenUnixSeconds: Int): String {
val lastSeenDuration = lastSeenUnixSeconds.seconds
val currentDuration = currentTimeMillis().milliseconds
val diff = (currentDuration - lastSeenDuration).absoluteValue
return DateUtils.getRelativeTimeSpanString(
timeInMillis,
currentTimeMillis,
DateUtils.SECOND_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE,
)
.toString()
return if (diff < 1.minutes) {
getString(Res.string.now)
} else {
DateUtils.getRelativeTimeSpanString(
lastSeenDuration.inWholeMilliseconds,
currentDuration.inWholeMilliseconds,
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE,
)
.toString()
}
}

View File

@@ -34,14 +34,13 @@ import org.meshtastic.core.ui.util.formatAgo
fun LastHeardInfo(
modifier: Modifier = Modifier,
lastHeard: Int,
currentTimeMillis: Long,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
) {
IconInfo(
modifier = modifier,
icon = ImageVector.vectorResource(id = R.drawable.ic_antenna_24),
contentDescription = stringResource(Res.string.node_sort_last_heard),
text = formatAgo(lastHeard, currentTimeMillis),
text = formatAgo(lastHeard),
contentColor = contentColor,
)
}
@@ -49,10 +48,5 @@ fun LastHeardInfo(
@PreviewLightDark
@Composable
private fun LastHeardInfoPreview() {
AppTheme {
LastHeardInfo(
lastHeard = (System.currentTimeMillis() / 1000).toInt() - 8600,
currentTimeMillis = System.currentTimeMillis(),
)
}
AppTheme { LastHeardInfo(lastHeard = (System.currentTimeMillis() / 1000).toInt() - 8600) }
}

View File

@@ -74,7 +74,6 @@ fun NodeItem(
modifier: Modifier = Modifier,
onClick: () -> Unit = {},
onLongClick: (() -> Unit)? = null,
currentTimeMillis: Long,
connectionState: ConnectionState,
isActive: Boolean = false,
) {
@@ -137,11 +136,7 @@ fun NodeItem(
textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
softWrap = true,
)
LastHeardInfo(
lastHeard = thatNode.lastHeard,
currentTimeMillis = currentTimeMillis,
contentColor = contentColor,
)
LastHeardInfo(lastHeard = thatNode.lastHeard, contentColor = contentColor)
NodeStatusIcons(
isThisNode = isThisNode,
isFavorite = isFavorite,
@@ -228,14 +223,7 @@ fun NodeInfoSimplePreview() {
AppTheme {
val thisNode = NodePreviewParameterProvider().values.first()
val thatNode = NodePreviewParameterProvider().values.last()
NodeItem(
thisNode = thisNode,
thatNode = thatNode,
0,
true,
currentTimeMillis = System.currentTimeMillis(),
connectionState = ConnectionState.Connected,
)
NodeItem(thisNode = thisNode, thatNode = thatNode, 0, true, connectionState = ConnectionState.Connected)
}
}
@@ -249,7 +237,6 @@ fun NodeInfoPreview(@PreviewParameter(NodePreviewParameterProvider::class) thatN
thatNode = thatNode,
distanceUnits = 1,
tempInFahrenheit = true,
currentTimeMillis = System.currentTimeMillis(),
connectionState = ConnectionState.Connected,
)
}

View File

@@ -78,7 +78,6 @@ import org.meshtastic.core.strings.unmute
import org.meshtastic.core.ui.component.AddContactFAB
import org.meshtastic.core.ui.component.MainAppBar
import org.meshtastic.core.ui.component.ScrollToTopEvent
import org.meshtastic.core.ui.component.rememberTimeTickWithLifecycle
import org.meshtastic.core.ui.component.smartScrollToTop
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
import org.meshtastic.feature.node.component.NodeActionDialogs
@@ -115,7 +114,6 @@ fun NodeListScreen(
}
}
val currentTimeMillis = rememberTimeTickWithLifecycle()
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
val isScrollInProgress by remember {
@@ -224,7 +222,6 @@ fun NodeListScreen(
tempInFahrenheit = state.tempInFahrenheit,
onClick = { navigateToNodeDetails(node.num) },
onLongClick = longClick,
currentTimeMillis = currentTimeMillis,
connectionState = connectionState,
isActive = isActive,
)