Add preview for date rendering

This commit is contained in:
Benoit Marty
2024-12-12 11:23:26 +01:00
parent 01a5766438
commit d21917278c
9 changed files with 315 additions and 2 deletions

View File

@@ -8,7 +8,7 @@ import extension.setupAnvil
*/
plugins {
id("io.element.android-library")
id("io.element.android-compose-library")
}
setupAnvil()
@@ -25,6 +25,7 @@ android {
dependencies {
implementation(libs.dagger)
implementation(projects.libraries.core)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.di)
implementation(projects.libraries.uiStrings)
implementation(projects.services.toolbox.api)

View File

@@ -26,12 +26,13 @@ class DateFormatters @Inject constructor(
localeChangeObserver: LocaleChangeObserver,
private val clock: Clock,
private val timeZoneProvider: TimezoneProvider,
locale: Locale,
) : LocaleChangeListener {
init {
localeChangeObserver.addListener(this)
}
private var dateTimeFormatters: DateTimeFormatters = DateTimeFormatters(Locale.getDefault())
private var dateTimeFormatters: DateTimeFormatters = DateTimeFormatters(locale)
override fun onLocaleChange() {
Timber.w("Locale changed, updating formatters")

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
data class DateForPreview(
val semantic: String,
val date: String,
)
val dateForPreviewToday = DateForPreview(
semantic = "Today",
date = "1980-04-06T18:35:24.00Z",
)
val dateForPreviews = listOf(
DateForPreview(
semantic = "Now",
date = dateForPreviewToday.date,
),
DateForPreview(
semantic = "One second ago",
date = "1980-04-06T18:35:23.00Z",
),
DateForPreview(
semantic = "One minute ago",
date = "1980-04-06T18:34:24.00Z",
),
DateForPreview(
semantic = "One hour ago",
date = "1980-04-06T17:35:24.00Z",
),
DateForPreview(
semantic = "One day ago",
date = "1980-04-05T18:35:24.00Z",
),
DateForPreview(
semantic = "Two days ago",
date = "1980-04-04T18:35:24.00Z",
),
DateForPreview(
semantic = "One month ago",
date = "1980-03-06T18:35:24.00Z",
),
DateForPreview(
semantic = "One year ago",
date = "1979-04-06T18:35:24.00Z",
),
)

View File

@@ -0,0 +1,16 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.dateformatter.api.DateFormatterMode
class DateFormatterModeProvider : PreviewParameterProvider<DateFormatterMode> {
override val values: Sequence<DateFormatterMode>
get() = DateFormatterMode.entries.asSequence()
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.dateformatter.api.DateFormatterMode
import io.element.android.libraries.dateformatter.impl.DefaultDateFormatter
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.allBooleans
import kotlinx.datetime.Instant
@Preview
@Composable
internal fun DateFormatterModeViewPreview(
@PreviewParameter(DateFormatterModeProvider::class) dateFormatterMode: DateFormatterMode,
) = ElementPreview {
DateFormatterModeView(dateFormatterMode)
}
@Composable
private fun DateFormatterModeView(
mode: DateFormatterMode,
) {
val context = LocalContext.current
val composeLocale = Locale.current
val dateFormatter = remember {
createFormatter(
context = context,
currentDate = dateForPreviewToday.date,
locale = java.util.Locale.Builder()
.setLanguageTag(composeLocale.toLanguageTag())
.build(),
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Mode $mode / $composeLocale",
style = ElementTheme.typography.fontHeadingSmMedium
)
val today = Instant.parse(dateForPreviewToday.date).toEpochMilliseconds()
Text(
text = "Today is: ${dateFormatter.format(today, DateFormatterMode.Full, useRelative = false)}",
style = ElementTheme.typography.fontHeadingSmMedium,
)
dateForPreviews.forEach { dateForPreview ->
DateForPreviewItem(
dateForPreview = dateForPreview,
dateFormatter = dateFormatter,
mode = mode,
)
}
}
}
@Composable
private fun DateForPreviewItem(
dateForPreview: DateForPreview,
dateFormatter: DefaultDateFormatter,
mode: DateFormatterMode,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(2.dp),
) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 8.dp),
text = dateForPreview.semantic,
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textSecondary,
)
val ts = Instant.parse(dateForPreview.date).toEpochMilliseconds()
Row {
Column {
listOf("Absolute:", "Relative:").forEach { label ->
Text(
text = label,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textPrimary,
)
}
}
Spacer(modifier = Modifier.width(8.dp))
Column {
allBooleans.forEach { useRelative ->
Text(
modifier = Modifier.fillMaxWidth(),
text = dateFormatter.format(ts, mode, useRelative),
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textPrimary,
)
}
}
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
import android.content.Context
import io.element.android.libraries.dateformatter.impl.DateFormatterFull
import io.element.android.libraries.dateformatter.impl.DateFormatterMonth
import io.element.android.libraries.dateformatter.impl.DateFormatterTime
import io.element.android.libraries.dateformatter.impl.DateFormatterTimeOnly
import io.element.android.libraries.dateformatter.impl.DateFormatters
import io.element.android.libraries.dateformatter.impl.DefaultDateFormatter
import io.element.android.libraries.dateformatter.impl.DefaultDateFormatterDay
import io.element.android.libraries.dateformatter.impl.LocalDateTimeProvider
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import java.util.Locale
/**
* Create DefaultDateFormatter and set current time to the provided date.
*/
fun createFormatter(
context: Context,
currentDate: String,
locale: Locale,
): DefaultDateFormatter {
val clock = PreviewClock().apply { givenInstant(Instant.parse(currentDate)) }
val localDateTimeProvider = LocalDateTimeProvider(clock) { TimeZone.UTC }
val dateFormatters = DateFormatters(
localeChangeObserver = {},
clock = clock,
timeZoneProvider = { TimeZone.UTC },
locale = locale,
)
val stringProvider = PreviewStringProvider(context.resources)
val dateFormatterDay = DefaultDateFormatterDay(
localDateTimeProvider = localDateTimeProvider,
dateFormatters = dateFormatters,
)
return DefaultDateFormatter(
dateFormatterFull = DateFormatterFull(
stringProvider = stringProvider,
localDateTimeProvider = localDateTimeProvider,
dateFormatters = dateFormatters,
dateFormatterDay = dateFormatterDay,
),
dateFormatterMonth = DateFormatterMonth(
stringProvider = stringProvider,
localDateTimeProvider = localDateTimeProvider,
dateFormatters = dateFormatters,
),
dateFormatterDay = dateFormatterDay,
dateFormatterTime = DateFormatterTime(
localDateTimeProvider = localDateTimeProvider,
dateFormatters = dateFormatters,
),
dateFormatterTimeOnly = DateFormatterTimeOnly(
localDateTimeProvider = localDateTimeProvider,
dateFormatters = dateFormatters,
),
)
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
class PreviewClock : Clock {
private var instant: Instant = Instant.fromEpochMilliseconds(0)
fun givenInstant(instant: Instant) {
this.instant = instant
}
override fun now(): Instant = instant
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only
* Please see LICENSE in the repository root for full details.
*/
package io.element.android.libraries.dateformatter.impl.previews
import android.content.res.Resources
import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
import io.element.android.services.toolbox.api.strings.StringProvider
class PreviewStringProvider(
private val resources: Resources
) : StringProvider {
override fun getString(@StringRes resId: Int): String {
return resources.getString(resId)
}
override fun getString(@StringRes resId: Int, vararg formatArgs: Any?): String {
return resources.getString(resId, *formatArgs)
}
override fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any?): String {
return resources.getQuantityString(resId, quantity, *formatArgs)
}
}

View File

@@ -10,6 +10,7 @@ package io.element.android.libraries.dateformatter.impl
import io.element.android.tests.testutils.InstrumentationStringProvider
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import java.util.Locale
/**
* Create DefaultDateFormatter and set current time to the provided date.
@@ -21,6 +22,7 @@ fun createFormatter(currentDate: String): DefaultDateFormatter {
localeChangeObserver = {},
clock = clock,
timeZoneProvider = { TimeZone.UTC },
locale = Locale.getDefault(),
)
val stringProvider = InstrumentationStringProvider()
val dateFormatterDay = DefaultDateFormatterDay(