Add preview for date rendering
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user