From b65cf87508ab6512a9e95dbc55fa975dab77f985 Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Thu, 14 Sep 2023 21:56:20 +0200 Subject: [PATCH 1/5] Fix datetime parse failures --- .../jerboa/ui/components/common/TimeAgo.kt | 60 +++++++++++++++---- app/src/main/res/values/strings.xml | 1 + 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt index c115332d7..399f4e97f 100644 --- a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt +++ b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt @@ -1,5 +1,6 @@ package com.jerboa.ui.components.common +import android.util.Log import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -29,7 +30,9 @@ import com.jerboa.formatDuration import com.jerboa.ui.theme.SMALL_PADDING import com.jerboa.ui.theme.muted import java.time.Instant +import java.time.format.DateTimeParseException import java.util.Date +import kotlin.jvm.Throws @Composable fun TimeAgo( @@ -40,6 +43,15 @@ fun TimeAgo( ) { val publishedPretty = dateStringToPretty(published, longTimeFormat) + if (publishedPretty == null) { + Text( + text = stringResource(R.string.time_ago_failed_to_parse), + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelSmall, + ) + return + } + val afterPreceding = precedingString?.let { stringResource(R.string.time_ago_ago, it, publishedPretty) } ?: run { publishedPretty } @@ -52,28 +64,52 @@ fun TimeAgo( ) updated?.also { - val updatedPretty = dateStringToPretty(it, longTimeFormat) - DotSpacer( padding = SMALL_PADDING, style = MaterialTheme.typography.bodyMedium, ) - Text( - text = "($updatedPretty)", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onBackground.muted, - fontStyle = FontStyle.Italic, - ) + val updatedPretty = dateStringToPretty(it, longTimeFormat) + + if (updatedPretty == null) { + Text( + text = stringResource(R.string.time_ago_failed_to_parse), + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelSmall, + ) + } else { + Text( + text = "($updatedPretty)", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onBackground.muted, + fontStyle = FontStyle.Italic, + ) + } } } } -fun dateStringToPretty(dateStr: String, longTimeFormat: Boolean = false): String { - val publishedDate = Date.from(Instant.parse(dateStr + "Z")) - return formatDuration(publishedDate, longTimeFormat) +/** + * Converts a date string to a pretty string like "2 hours" or "2h" + * + * @param dateStr The date string to convert + * @param longTimeFormat If true, use a long time format like "2 hours, 3 minutes ago" + * @return The pretty string, or null if the date string could not be parsed + */ +@Throws(DateTimeParseException::class) +fun dateStringToPretty(dateStr: String, longTimeFormat: Boolean = false): String? { + return try { + // TODO: Remove this hack once backward API compatibility is implemented + // pre 0.19 Datetimes didn't have a timezone, so we add one here + val withTimezone = if (dateStr.last() == 'Z') dateStr else dateStr + "Z" + val publishedDate = Date.from(Instant.parse(withTimezone)) + formatDuration(publishedDate, longTimeFormat) + } catch (e: DateTimeParseException) { + Log.d("TimeAgo", "Failed to parse date string: $dateStr", e) + null + } } -@Preview +@Preview(backgroundColor = 0x00000000) @Composable fun TimeAgoPreview() { TimeAgo(samplePerson.published, samplePerson.updated) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index affeb71e2..8fd86c896 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -418,4 +418,5 @@ Connect on Matrix Posts failed loading, retry Failed to share media! + Failed to parse datetime From f3435a9b0e0cc49a6dac4ff1dcaa780ba682eacb Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Thu, 14 Sep 2023 21:57:28 +0200 Subject: [PATCH 2/5] Remove throws --- app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt index 399f4e97f..cc09277db 100644 --- a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt +++ b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt @@ -95,7 +95,6 @@ fun TimeAgo( * @param longTimeFormat If true, use a long time format like "2 hours, 3 minutes ago" * @return The pretty string, or null if the date string could not be parsed */ -@Throws(DateTimeParseException::class) fun dateStringToPretty(dateStr: String, longTimeFormat: Boolean = false): String? { return try { // TODO: Remove this hack once backward API compatibility is implemented From 1ca1705314a9b03645986af7585881c0f79c407c Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Thu, 14 Sep 2023 21:57:51 +0200 Subject: [PATCH 3/5] Remove remnants --- app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt index cc09277db..833dd0ff0 100644 --- a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt +++ b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt @@ -108,7 +108,7 @@ fun dateStringToPretty(dateStr: String, longTimeFormat: Boolean = false): String } } -@Preview(backgroundColor = 0x00000000) +@Preview @Composable fun TimeAgoPreview() { TimeAgo(samplePerson.published, samplePerson.updated) From d5256049638f5621b309ade82efa47faab25d300 Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Thu, 14 Sep 2023 22:08:40 +0200 Subject: [PATCH 4/5] Fix formatting --- app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt index 833dd0ff0..c48429d60 100644 --- a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt +++ b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt @@ -32,7 +32,6 @@ import com.jerboa.ui.theme.muted import java.time.Instant import java.time.format.DateTimeParseException import java.util.Date -import kotlin.jvm.Throws @Composable fun TimeAgo( From 2503b70bc735beb07aecf9a5ff00f05ab3f9ce25 Mon Sep 17 00:00:00 2001 From: "maarten.vercruysse" Date: Fri, 15 Sep 2023 15:12:48 +0200 Subject: [PATCH 5/5] Add changes --- .../jerboa/ui/components/common/TimeAgo.kt | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt index c48429d60..728595ec6 100644 --- a/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt +++ b/app/src/main/java/com/jerboa/ui/components/common/TimeAgo.kt @@ -43,11 +43,7 @@ fun TimeAgo( val publishedPretty = dateStringToPretty(published, longTimeFormat) if (publishedPretty == null) { - Text( - text = stringResource(R.string.time_ago_failed_to_parse), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.labelSmall, - ) + SmallErrorLabel(text = stringResource(R.string.time_ago_failed_to_parse)) return } @@ -70,11 +66,7 @@ fun TimeAgo( val updatedPretty = dateStringToPretty(it, longTimeFormat) if (updatedPretty == null) { - Text( - text = stringResource(R.string.time_ago_failed_to_parse), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.labelSmall, - ) + SmallErrorLabel(text = stringResource(R.string.time_ago_failed_to_parse)) } else { Text( text = "($updatedPretty)", @@ -96,7 +88,7 @@ fun TimeAgo( */ fun dateStringToPretty(dateStr: String, longTimeFormat: Boolean = false): String? { return try { - // TODO: Remove this hack once backward API compatibility is implemented + // TODO: v0.18.4_deprecated Remove this hack once backward API compatibility is implemented // pre 0.19 Datetimes didn't have a timezone, so we add one here val withTimezone = if (dateStr.last() == 'Z') dateStr else dateStr + "Z" val publishedDate = Date.from(Instant.parse(withTimezone)) @@ -214,3 +206,18 @@ fun NsfwBadge(visible: Boolean) { fun NsfwBadgePreview() { NsfwBadge(visible = true) } + +@Composable +fun SmallErrorLabel(text: String) { + Text( + text = text, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelSmall, + ) +} + +@Preview +@Composable +fun SmallErrorLabelPreview() { + SmallErrorLabel(stringResource(id = R.string.time_ago_failed_to_parse)) +}