From f14c1e296b78691df12ecba3a2b10df279980f81 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 05:48:35 -0300 Subject: [PATCH 1/9] feat: remove unused strings Signed-off-by: alexandreferris --- app/src/main/res/values-af/strings.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 459ca976c77..c59c8433969 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -472,13 +472,6 @@ Learn more about read receipts Just now - - 1 minute ago - %1$d minutes ago - - Today, %s - Yesterday, %s - %s File Gallery From f2eb556590cd796b1961e9046a3023917aa33570 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 05:49:37 -0300 Subject: [PATCH 2/9] feat: remove unused date formatters and adjust sealed interface for MessageDateTimeGroup Signed-off-by: alexandreferris --- .../com/wire/android/util/DateTimeUtil.kt | 80 +++++++++++-------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/util/DateTimeUtil.kt b/app/src/main/kotlin/com/wire/android/util/DateTimeUtil.kt index 22a5b203b10..a5086799790 100644 --- a/app/src/main/kotlin/com/wire/android/util/DateTimeUtil.kt +++ b/app/src/main/kotlin/com/wire/android/util/DateTimeUtil.kt @@ -20,11 +20,14 @@ package com.wire.android.util +import android.text.format.DateUtils import com.wire.android.appLogger import kotlinx.datetime.Instant import java.text.DateFormat import java.text.ParseException import java.text.SimpleDateFormat +import java.time.LocalDate +import java.time.ZoneId import java.util.Calendar import java.util.Date import java.util.Locale @@ -40,21 +43,12 @@ private val longDateShortTimeFormat = DateFormat .getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT) private val mediumOnlyDateTimeFormat = DateFormat .getDateInstance(DateFormat.MEDIUM) -private val messageTimeFormatter = DateFormat +val messageTimeFormatter = DateFormat .getTimeInstance(DateFormat.SHORT) .apply { timeZone = TimeZone.getDefault() } -private val messageWeekDayFormatter = SimpleDateFormat( - "EEEE MMM dd, hh:mm a", - Locale.getDefault() -) -private val messageLongerThanWeekAndSameYearFormatter = SimpleDateFormat( - "MMM dd, hh:mm a", - Locale.getDefault() -) -private val messageMonthDayAndYear = SimpleDateFormat( - "MMM dd yyyy, hh:mm a", - Locale.getDefault() -) +private val messageDateTimeFormatter = DateFormat + .getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) + .apply { timeZone = TimeZone.getDefault() } private const val ONE_MINUTE_FROM_MILLIS = 60 * 1000 private const val THIRTY_MINUTES = 30 private const val ONE_WEEK_IN_DAYS = 7 @@ -171,18 +165,31 @@ private fun isDatesWithinWeek(date: Long, now: Long): Boolean = private fun isDatesSameYear(date: Long, now: Long): Boolean = date.getCalendar().get(Calendar.YEAR) == now.getCalendar().get(Calendar.YEAR) -sealed interface MessageDateTime { - data object Now : MessageDateTime - data class Within30Minutes(val minutes: Int) : MessageDateTime - data class Today(val time: String) : MessageDateTime - data class Yesterday(val time: String) : MessageDateTime - data class WithinWeek(val date: String) : MessageDateTime - data class NotWithinWeekButSameYear(val date: String) : MessageDateTime - data class Other(val date: String) : MessageDateTime +sealed interface MessageDateTimeGroup { + data object Now : MessageDateTimeGroup + data object Within30Minutes : MessageDateTimeGroup + data class Daily(val type: Type, val date: LocalDate) : MessageDateTimeGroup { + enum class Type { + Today, + Yesterday, + WithinWeek, + NotWithinWeekButSameYear, + Other + } + } } -fun String.uiMessageDateTime(now: Long): MessageDateTime? = this +fun String.uiMessageDateTime(): String? = this + .serverDate()?.let { serverDate -> + when (DateUtils.isToday(serverDate.time)) { + true -> messageTimeFormatter.format(serverDate) + false -> messageDateTimeFormatter.format(serverDate) + } + } + +fun String.groupedUIMessageDateTime(now: Long): MessageDateTimeGroup? = this .serverDate()?.let { serverDate -> + val localDate = serverDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate() val serverDateInMillis = serverDate.time val differenceBetweenServerDateAndNow = now - serverDateInMillis val differenceInMinutes: Long = differenceBetweenServerDateAndNow / ONE_MINUTE_FROM_MILLIS @@ -191,23 +198,28 @@ fun String.uiMessageDateTime(now: Long): MessageDateTime? = this val isSameYear = isDatesSameYear(date = serverDateInMillis, now = now) when { - differenceBetweenServerDateAndNow < ONE_MINUTE_FROM_MILLIS -> MessageDateTime.Now - differenceInMinutes <= THIRTY_MINUTES -> MessageDateTime.Within30Minutes( - minutes = differenceInMinutes.toInt() + differenceBetweenServerDateAndNow < ONE_MINUTE_FROM_MILLIS -> MessageDateTimeGroup.Now + differenceInMinutes <= THIRTY_MINUTES -> MessageDateTimeGroup.Within30Minutes + differenceInMinutes > THIRTY_MINUTES && isSameDay -> MessageDateTimeGroup.Daily( + type = MessageDateTimeGroup.Daily.Type.Today, + date = localDate ) - differenceInMinutes > THIRTY_MINUTES && isSameDay -> MessageDateTime.Today( - time = messageTimeFormatter.format(serverDateInMillis) + isYesterday(serverDateInMillis, now) -> MessageDateTimeGroup.Daily( + type = MessageDateTimeGroup.Daily.Type.Yesterday, + date = localDate ) - isYesterday(serverDateInMillis, now) -> MessageDateTime.Yesterday( - time = messageTimeFormatter.format(serverDateInMillis) + withinWeek -> MessageDateTimeGroup.Daily( + type = MessageDateTimeGroup.Daily.Type.WithinWeek, + date = localDate ) - withinWeek -> MessageDateTime.WithinWeek( - date = messageWeekDayFormatter.format(serverDate) + !withinWeek && isSameYear -> MessageDateTimeGroup.Daily( + type = MessageDateTimeGroup.Daily.Type.NotWithinWeekButSameYear, + date = localDate ) - !withinWeek && isSameYear -> MessageDateTime.NotWithinWeekButSameYear( - date = messageLongerThanWeekAndSameYearFormatter.format(serverDate) + else -> MessageDateTimeGroup.Daily( + type = MessageDateTimeGroup.Daily.Type.Other, + date = localDate ) - else -> MessageDateTime.Other(date = messageMonthDayAndYear.format(serverDate)) } } From 655c42126446e2f870b768b409a7f17c1824e877 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 05:50:39 -0300 Subject: [PATCH 3/9] feat: create new method to get formatted date time of grouped messages to be shown Signed-off-by: alexandreferris --- .../wire/android/ui/home/conversations/model/UIMessage.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt index ea03e70aaa1..c8d19e69fef 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/model/UIMessage.kt @@ -31,7 +31,8 @@ import com.wire.android.ui.home.messagecomposer.SelfDeletionDuration import com.wire.android.ui.markdown.MarkdownConstants import com.wire.android.ui.theme.Accent import com.wire.android.util.Copyable -import com.wire.android.util.MessageDateTime +import com.wire.android.util.MessageDateTimeGroup +import com.wire.android.util.groupedUIMessageDateTime import com.wire.android.util.ui.LocalizedStringResource import com.wire.android.util.ui.UIText import com.wire.android.util.uiMessageDateTime @@ -598,7 +599,8 @@ enum class MessageSource { } data class MessageTime(val utcISO: String) { - fun formattedDate(now: Long): MessageDateTime? = utcISO.uiMessageDateTime(now = now) + val formattedDate: String = utcISO.uiMessageDateTime() ?: "" + fun getFormattedDateGroup(now: Long): MessageDateTimeGroup? = utcISO.groupedUIMessageDateTime(now = now) } @Stable From 0ae4f79589f1161752a1e781349262ce3306c1cb Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 05:51:33 -0300 Subject: [PATCH 4/9] feat: remove unused currentTimeInMillisFlow from MessageItem Signed-off-by: alexandreferris --- .../messages/item/MessageContainerItem.kt | 8 +--- .../messages/item/RegularMessageItem.kt | 42 +++---------------- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContainerItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContainerItem.kt index b23a89dbd5e..abba45a5933 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContainerItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/MessageContainerItem.kt @@ -45,8 +45,6 @@ import com.wire.kalium.logic.data.asset.AssetTransferStatus import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.UserId import kotlinx.collections.immutable.PersistentMap -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow @OptIn(ExperimentalFoundationApi::class) @Suppress("ComplexMethod") @@ -78,8 +76,7 @@ fun MessageContainerItem( shouldDisplayFooter: Boolean = true, onReplyClickable: Clickable? = null, isSelectedMessage: Boolean = false, - isInteractionAvailable: Boolean = true, - currentTimeInMillisFlow: Flow = flow { }, + isInteractionAvailable: Boolean = true ) { val selfDeletionTimerState = rememberSelfDeletionTimer(message.header.messageStatus.expirationStatus) if ( @@ -156,8 +153,7 @@ fun MessageContainerItem( shouldDisplayMessageStatus = shouldDisplayMessageStatus, shouldDisplayFooter = shouldDisplayFooter, selfDeletionTimerState = selfDeletionTimerState, - useSmallBottomPadding = useSmallBottomPadding, - currentTimeInMillisFlow = currentTimeInMillisFlow + useSmallBottomPadding = useSmallBottomPadding ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/RegularMessageItem.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/RegularMessageItem.kt index e6d2fed4e62..efd97195b3b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/RegularMessageItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/messages/item/RegularMessageItem.kt @@ -30,8 +30,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -63,7 +61,6 @@ import com.wire.android.ui.home.conversations.model.MessageHeader import com.wire.android.ui.home.conversations.model.MessageImage import com.wire.android.ui.home.conversations.model.MessageSource import com.wire.android.ui.home.conversations.model.MessageStatus -import com.wire.android.ui.home.conversations.model.MessageTime import com.wire.android.ui.home.conversations.model.UIMessage import com.wire.android.ui.home.conversations.model.UIMessageContent import com.wire.android.ui.home.conversations.model.UIQuotedMessage @@ -75,15 +72,12 @@ import com.wire.android.ui.home.conversations.model.messagetypes.location.Locati import com.wire.android.ui.theme.Accent import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireTypography -import com.wire.android.util.MessageDateTime import com.wire.android.util.launchGeoIntent import com.wire.kalium.logic.data.asset.AssetTransferStatus import com.wire.kalium.logic.data.asset.isSaved import com.wire.kalium.logic.data.id.ConversationId import com.wire.kalium.logic.data.user.UserId import kotlinx.collections.immutable.PersistentMap -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow // TODO: a definite candidate for a refactor and cleanup @Suppress("ComplexMethod") @@ -112,7 +106,6 @@ fun RegularMessageItem( onReplyClickable: Clickable? = null, isInteractionAvailable: Boolean = true, useSmallBottomPadding: Boolean = false, - currentTimeInMillisFlow: Flow = flow { }, selfDeletionTimerState: SelfDeletionTimerHelper.SelfDeletionTimerState = SelfDeletionTimerHelper.SelfDeletionTimerState.NotExpirable ) { with(message) { @@ -133,7 +126,7 @@ fun RegularMessageItem( Column { if (showAuthor) { Spacer(modifier = Modifier.height(dimensions().avatarClickablePadding)) - MessageAuthorRow(messageHeader = message.header, currentTimeInMillisFlow) + MessageAuthorRow(messageHeader = message.header) Spacer(modifier = Modifier.height(dimensions().spacing4x)) } if (selfDeletionTimerState is SelfDeletionTimerHelper.SelfDeletionTimerState.Expirable) { @@ -329,7 +322,7 @@ fun MessageExpireLabel(messageContent: UIMessageContent?, assetTransferStatus: A } @Composable -private fun MessageAuthorRow(messageHeader: MessageHeader, currentTimeInMillisFlow: Flow) { +private fun MessageAuthorRow(messageHeader: MessageHeader) { with(messageHeader) { Row(verticalAlignment = Alignment.CenterVertically) { Row( @@ -352,8 +345,7 @@ private fun MessageAuthorRow(messageHeader: MessageHeader, currentTimeInMillisFl } } MessageTimeLabel( - messageTime = messageHeader.messageTime, - currentTimeInMillisFlow = currentTimeInMillisFlow, + messageTime = messageHeader.messageTime.formattedDate, modifier = Modifier.padding(start = dimensions().spacing6x) ) } @@ -392,35 +384,11 @@ private fun MessageFooter( @Composable private fun MessageTimeLabel( - messageTime: MessageTime, - currentTimeInMillisFlow: Flow, + messageTime: String, modifier: Modifier = Modifier ) { - - val currentTime by currentTimeInMillisFlow.collectAsState(initial = System.currentTimeMillis()) - - val messageDateTime = messageTime.formattedDate(now = currentTime) - - val context = LocalContext.current - - val timeString = when (messageDateTime) { - is MessageDateTime.Now -> context.resources.getString(R.string.message_datetime_now) - is MessageDateTime.Within30Minutes -> context.resources.getQuantityString( - R.plurals.message_datetime_minutes_ago, - messageDateTime.minutes, - messageDateTime.minutes - ) - - is MessageDateTime.Today -> context.resources.getString(R.string.message_datetime_today, messageDateTime.time) - is MessageDateTime.Yesterday -> context.resources.getString(R.string.message_datetime_yesterday, messageDateTime.time) - is MessageDateTime.WithinWeek -> context.resources.getString(R.string.message_datetime_other, messageDateTime.date) - is MessageDateTime.NotWithinWeekButSameYear -> context.resources.getString(R.string.message_datetime_other, messageDateTime.date) - is MessageDateTime.Other -> context.resources.getString(R.string.message_datetime_other, messageDateTime.date) - null -> "" - } - Text( - text = timeString, + text = messageTime, style = MaterialTheme.typography.labelSmall.copy(color = MaterialTheme.wireColorScheme.secondaryText), maxLines = 1, modifier = modifier From 4c630c7729f464b4af0e5b9b2b5e75e9b1a3a1ed Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 05:52:07 -0300 Subject: [PATCH 5/9] feat: add formatting of message group date and time on UI Signed-off-by: alexandreferris --- .../home/conversations/ConversationScreen.kt | 100 +++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt index 06e54bde0e1..449450bbade 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/conversations/ConversationScreen.kt @@ -20,6 +20,7 @@ package com.wire.android.ui.home.conversations import android.annotation.SuppressLint import android.net.Uri +import android.text.format.DateUtils import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandIn @@ -28,6 +29,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -46,9 +48,11 @@ import androidx.compose.material3.SmallFloatingActionButton import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarResult +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -142,8 +146,11 @@ import com.wire.android.ui.home.messagecomposer.state.MessageComposerStateHolder import com.wire.android.ui.home.messagecomposer.state.rememberMessageComposerStateHolder import com.wire.android.ui.legalhold.dialog.subject.LegalHoldSubjectMessageDialog import com.wire.android.ui.theme.wireColorScheme +import com.wire.android.ui.theme.wireTypography +import com.wire.android.util.MessageDateTimeGroup import com.wire.android.util.normalizeLink import com.wire.android.util.permission.PermissionDenialType +import com.wire.android.util.serverDate import com.wire.android.util.ui.UIText import com.wire.android.util.ui.openDownloadFolder import com.wire.kalium.logic.NetworkFailure @@ -164,6 +171,8 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.datetime.Instant +import java.util.Date +import java.util.Locale import kotlin.time.Duration.Companion.milliseconds /** @@ -973,6 +982,7 @@ fun MessageList( ) { val prevItemCount = remember { mutableStateOf(lazyPagingMessages.itemCount) } val readLastMessageAtStartTriggered = remember { mutableStateOf(false) } + val currentTime by currentTimeInMillisFlow.collectAsState(initial = System.currentTimeMillis()) LaunchedEffect(lazyPagingMessages.itemCount) { if (lazyPagingMessages.itemCount > prevItemCount.value && selectedMessageId == null) { @@ -1044,6 +1054,23 @@ fun MessageList( val showAuthor = rememberShouldShowHeader(index, message, lazyPagingMessages) val useSmallBottomPadding = rememberShouldHaveSmallBottomPadding(index, message, lazyPagingMessages) + if (index > 0) { + val previousMessage = lazyPagingMessages[index - 1] ?: message + + val currentGroup = message.header.messageTime.getFormattedDateGroup(now = currentTime) + val previousGroup = previousMessage.header.messageTime.getFormattedDateGroup(now = currentTime) + + if (currentGroup != previousGroup) { + previousMessage.header.messageTime.utcISO.serverDate()?.let { serverDate -> + MessageGroupDateTime( + messageDateTime = serverDate, + messageDateTimeGroup = previousGroup, + now = currentTime + ) + } + } + } + MessageContainerItem( message = message, conversationDetailsData = conversationDetailsData, @@ -1070,8 +1097,7 @@ fun MessageList( } ), isSelectedMessage = (message.header.messageId == selectedMessageId), - isInteractionAvailable = interactionAvailability == InteractionAvailability.ENABLED, - currentTimeInMillisFlow = currentTimeInMillisFlow + isInteractionAvailable = interactionAvailability == InteractionAvailability.ENABLED ) } } @@ -1080,6 +1106,76 @@ fun MessageList( ) } +@Composable +private fun MessageGroupDateTime( + now: Long, + messageDateTime: Date, + messageDateTimeGroup: MessageDateTimeGroup? +) { + val context = LocalContext.current + + val timeString = when (messageDateTimeGroup) { + is MessageDateTimeGroup.Now -> context.resources.getString(R.string.message_datetime_now) + is MessageDateTimeGroup.Within30Minutes -> DateUtils.getRelativeTimeSpanString( + messageDateTime.time, + now, + DateUtils.MINUTE_IN_MILLIS + ).toString() + is MessageDateTimeGroup.Daily -> { + when (messageDateTimeGroup.type) { + MessageDateTimeGroup.Daily.Type.Today -> DateUtils.getRelativeDateTimeString( + context, + messageDateTime.time, + DateUtils.DAY_IN_MILLIS, + DateUtils.DAY_IN_MILLIS, + 0 + ).toString() + MessageDateTimeGroup.Daily.Type.Yesterday -> + DateUtils.getRelativeDateTimeString( + context, + messageDateTime.time, + DateUtils.DAY_IN_MILLIS, + DateUtils.DAY_IN_MILLIS * 2, + 0 + ).toString() + MessageDateTimeGroup.Daily.Type.WithinWeek -> DateUtils.formatDateTime( + context, + messageDateTime.time, + DateUtils.FORMAT_SHOW_WEEKDAY or DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME + ) + MessageDateTimeGroup.Daily.Type.NotWithinWeekButSameYear -> DateUtils.formatDateTime( + context, + messageDateTime.time, + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_TIME + ) + MessageDateTimeGroup.Daily.Type.Other -> DateUtils.formatDateTime( + context, + messageDateTime.time, + DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_TIME + ) + } + } + null -> "" + } + + Row( + Modifier + .fillMaxWidth() + .background(color = colorsScheme().divider) + .padding( + top = dimensions().spacing6x, + bottom = dimensions().spacing6x, + start = dimensions().spacing56x + ) + ) { + Text( + text = timeString.uppercase(Locale.getDefault()), + color = colorsScheme().secondaryText, + style = MaterialTheme.wireTypography.title03, + ) + } +} + private fun updateLastReadMessage( lastVisibleMessage: UIMessage, lastUnreadMessageInstant: Instant?, From d3eff4023e3c06f67365ca65bfe7278ccda07abc Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 06:13:34 -0300 Subject: [PATCH 6/9] test: remove old tests Signed-off-by: alexandreferris --- .../wire/android/mapper/MessageMapperTest.kt | 168 ++++-------------- .../wire/android/util/DateTimeUtilKtTest.kt | 77 -------- 2 files changed, 39 insertions(+), 206 deletions(-) diff --git a/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt b/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt index 7b141bd4433..89e9df57f9d 100644 --- a/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/mapper/MessageMapperTest.kt @@ -30,7 +30,6 @@ import com.wire.android.ui.home.conversations.model.MessageStatus import com.wire.android.ui.home.conversations.model.UIMessage import com.wire.android.ui.home.conversations.model.UIMessageContent.TextMessage import com.wire.android.ui.home.conversationslist.model.Membership -import com.wire.android.util.MessageDateTime import com.wire.android.util.time.ISOFormatter import com.wire.android.util.ui.UIText import com.wire.android.util.ui.WireSessionImageLoader @@ -80,12 +79,15 @@ class MessageMapperTest { @Suppress("LongMethod") fun givenMessageList_whenMappingToUIMessages_thenCorrectValuesShouldBeReturned() = runTest { // Given - val (arrangement, mapper) = Arrangement().arrange() + val serverDateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) + .apply { timeZone = TimeZone.getTimeZone("UTC") } - val now = arrangement.serverDateFormatter.format(arrangement.dateNow) + val now = serverDateFormatter.format(Date()) val calendar = Calendar.getInstance() calendar.add(Calendar.DATE, -1) - val yesterday = arrangement.serverDateFormatter.format(calendar.time) + val yesterday = serverDateFormatter.format(calendar.time) + + val (arrangement, mapper) = Arrangement().arrange() val userId1 = UserId("user-id1", "user-domain") val userId2 = UserId("user-id2", "user-domain") @@ -110,16 +112,16 @@ class MessageMapperTest { // Then assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage1, - time = message1.date.uiMessageDateTime(arrangement.dateNow.time) + time = message1.date.uiMessageDateTime() ) ) assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage2, - time = message2.date.uiMessageDateTime(arrangement.dateNow.time), + time = message2.date.uiMessageDateTime(), source = MessageSource.OtherUser, membership = Membership.Guest, status = MessageStatus( @@ -130,21 +132,21 @@ class MessageMapperTest { ) assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage3, - time = message3.date.uiMessageDateTime(arrangement.dateNow.time), + time = message3.date.uiMessageDateTime(), status = MessageStatus( flowStatus = MessageFlowStatus.Sent, - editStatus = MessageEditStatus.Edited(now), + editStatus = MessageEditStatus.Edited(now.uiMessageDateTime() ?: ""), expirationStatus = ExpirationStatus.NotExpirable ) ) ) assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage4, - time = message4.date.uiMessageDateTime(arrangement.dateNow.time), + time = message4.date.uiMessageDateTime(), status = MessageStatus( flowStatus = MessageFlowStatus.Sent, isDeleted = true, @@ -155,9 +157,9 @@ class MessageMapperTest { assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage5, - time = message5.date.uiMessageDateTime(arrangement.dateNow.time), + time = message5.date.uiMessageDateTime(), status = MessageStatus( flowStatus = MessageFlowStatus.Failure.Decryption(false), isDeleted = false, @@ -168,9 +170,9 @@ class MessageMapperTest { assertEquals( true, - arrangement.checkMessageData( + checkMessageData( uiMessage = uiMessage6, - time = message6.date.uiMessageDateTime(arrangement.dateNow.time), + time = message6.date.uiMessageDateTime(), status = MessageStatus( flowStatus = MessageFlowStatus.Failure.Decryption(true), isDeleted = false, @@ -199,96 +201,28 @@ class MessageMapperTest { val result = mapper.toUIMessage(members, message)?.header?.messageStatus?.flowStatus // then - assertEquals( - true, - result != null - ) - assertEquals( - true, - result!! is MessageFlowStatus.Read - ) - assertEquals( - true, - (result as MessageFlowStatus.Read).count == 10L - ) + assert(result != null) + assert(result!! is MessageFlowStatus.Read) + assert((result as MessageFlowStatus.Read).count == 10L) } - @Suppress("LongMethod") - @Test - fun givenMessageWithDate_whenCheckingFormattedUIDates_thenReturnCorrectMessageDateTime() = runTest { - // given - val (arrangement, _) = Arrangement().arrange() - val calendar = Calendar.getInstance().apply { - set(Calendar.SECOND, 0) - set(Calendar.MINUTE, 0) - set(Calendar.HOUR, 7) - set(Calendar.AM_PM, Calendar.AM) - set(Calendar.MONTH, Calendar.JANUARY) - set(Calendar.DAY_OF_MONTH, 20) - set(Calendar.YEAR, 2024) - } - val tempCalendar: Calendar = calendar.clone() as Calendar - val now = arrangement.serverDateFormatter.format(calendar.time) - - val userId1 = UserId("user-id1", "user-domain") - val message = arrangement.testMessage(senderUserId = userId1, date = now) - - // when - val resultNow = message.date.uiMessageDateTime(tempCalendar.timeInMillis) - - val resultWithin30Minutes = message.date.uiMessageDateTime( - tempCalendar.apply { add(Calendar.MINUTE, 10) }.timeInMillis - ) - - val resultToday = message.date.uiMessageDateTime( - tempCalendar.apply { add(Calendar.MINUTE, 31) }.timeInMillis - ) - - val resultYesterday = message.date.uiMessageDateTime( - tempCalendar.apply { add(Calendar.DATE, 1) }.timeInMillis - ) - - val resultWithinWeek = message.date.uiMessageDateTime( - tempCalendar.apply { add(Calendar.DATE, 3) }.timeInMillis - ) - - val resultNotWithinWeekButSameYear = message.date.uiMessageDateTime( - tempCalendar.apply { add(Calendar.DATE, 10) }.timeInMillis - ) - - val resultOther = message.date.uiMessageDateTime( - tempCalendar.apply { set(Calendar.YEAR, 2025) }.timeInMillis - ) - - // then - assertEquals( - MessageDateTime.Now, - resultNow - ) - assertEquals( - MessageDateTime.Within30Minutes(10), - resultWithin30Minutes - ) - assertEquals( - MessageDateTime.Today("7:00 AM"), - resultToday - ) - assertEquals( - MessageDateTime.Yesterday("7:00 AM"), - resultYesterday - ) - assertEquals( - MessageDateTime.WithinWeek("Saturday Jan 20, 07:00 AM"), - resultWithinWeek - ) - assertEquals( - MessageDateTime.NotWithinWeekButSameYear("Jan 20, 07:00 AM"), - resultNotWithinWeekButSameYear - ) - assertEquals( - MessageDateTime.Other("Jan 20 2024, 07:00 AM"), - resultOther + private fun checkMessageData( + uiMessage: UIMessage?, + time: String?, + source: MessageSource = MessageSource.Self, + membership: Membership = Membership.None, + status: MessageStatus = MessageStatus( + flowStatus = MessageFlowStatus.Sent, + expirationStatus = ExpirationStatus.NotExpirable ) + ): Boolean { + return (uiMessage?.source == source && uiMessage.header.membership == membership + && uiMessage.header.messageTime.formattedDate == time + && uiMessage.header.messageStatus.flowStatus == status.flowStatus + && uiMessage.header.messageStatus.isDeleted == status.isDeleted + && uiMessage.header.messageStatus.editStatus == status.editStatus + && uiMessage.header.messageStatus.expirationStatus == status.expirationStatus + ) } private class Arrangement { @@ -308,19 +242,13 @@ class MessageMapperTest { MessageMapper(userTypeMapper, messageContentMapper, isoFormatter, wireSessionImageLoader) } - val serverDateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) - .apply { timeZone = TimeZone.getTimeZone("UTC") } - val dateNow = Date() - init { MockKAnnotations.init(this, relaxUnitFun = true) coEvery { userTypeMapper.toMembership(any()) } returns Membership.Guest coEvery { messageContentMapper.fromMessage(any(), any()) } returns TextMessage( MessageBody(UIText.DynamicString("some message text")) ) - every { isoFormatter.fromISO8601ToTimeFormat(any()) } answers { - serverDateFormatter.format(dateNow) - } + every { isoFormatter.fromISO8601ToTimeFormat(any()) } answers { firstArg().uiMessageDateTime() ?: "" } } fun arrange() = this to messageMapper @@ -338,24 +266,6 @@ class MessageMapperTest { visibility = visibility, editStatus = editStatus ) - - fun checkMessageData( - uiMessage: UIMessage?, - time: MessageDateTime?, - source: MessageSource = MessageSource.Self, - membership: Membership = Membership.None, - status: MessageStatus = MessageStatus( - flowStatus = MessageFlowStatus.Sent, - expirationStatus = ExpirationStatus.NotExpirable - ) - ): Boolean { - return uiMessage?.source == source && uiMessage.header.membership == membership - && uiMessage.header.messageTime.formattedDate(dateNow.time) == time - && uiMessage.header.messageStatus.flowStatus == status.flowStatus - && uiMessage.header.messageStatus.isDeleted == status.isDeleted - && uiMessage.header.messageStatus.editStatus == status.editStatus - && uiMessage.header.messageStatus.expirationStatus == status.expirationStatus - } } } diff --git a/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt b/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt index 9a9cb46ecfb..17c9bfd0108 100644 --- a/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt +++ b/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt @@ -20,7 +20,6 @@ package com.wire.android.util import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import java.util.Calendar class DateTimeUtilKtTest { @@ -41,80 +40,4 @@ class DateTimeUtilKtTest { val result = "2022-03-24T18:02:30.360Z".formatMediumDateTime() assertEquals("Mar 24, 2022, 6:02:30 PM", result) } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Now`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime(getDummyCalendar().timeInMillis) - assertEquals(MessageDateTime.Now, result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Within30Minutes`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - add(Calendar.MINUTE, 10) - }.timeInMillis - ) - assertEquals(MessageDateTime.Within30Minutes(10), result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Today`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - add(Calendar.MINUTE, 31) - }.timeInMillis - ) - assertEquals(MessageDateTime.Today("7:00 AM"), result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Yesterday`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - add(Calendar.DATE, 1) - }.timeInMillis - ) - assertEquals(MessageDateTime.Yesterday("7:00 AM"), result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_WithinWeek`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - add(Calendar.DATE, 3) - }.timeInMillis - ) - assertEquals(MessageDateTime.WithinWeek("Saturday Jan 20, 07:00 AM"), result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_NotWithinWeekButSameYear`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - add(Calendar.DATE, 10) - }.timeInMillis - ) - assertEquals(MessageDateTime.NotWithinWeekButSameYear("Jan 20, 07:00 AM"), result) - } - - @Test - fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Other`() { - val result = "2024-01-20T07:00:00.000Z".uiMessageDateTime( - getDummyCalendar().apply { - set(Calendar.YEAR, 2025) - }.timeInMillis - ) - assertEquals(MessageDateTime.Other("Jan 20 2024, 07:00 AM"), result) - } - - private fun getDummyCalendar(): Calendar = Calendar.getInstance().apply { - set(Calendar.SECOND, 0) - set(Calendar.MINUTE, 0) - set(Calendar.HOUR, 7) - set(Calendar.AM_PM, Calendar.AM) - set(Calendar.MONTH, Calendar.JANUARY) - set(Calendar.DAY_OF_MONTH, 20) - set(Calendar.YEAR, 2024) - } } From b4db3b0449a7865faace969027190e4919dab3eb Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 06:25:30 -0300 Subject: [PATCH 7/9] test: adjust existing tests to match new sealed class and type Signed-off-by: alexandreferris --- .../wire/android/util/DateTimeUtilKtTest.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt b/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt index 17c9bfd0108..f110b2d65d0 100644 --- a/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt +++ b/app/src/test/kotlin/com/wire/android/util/DateTimeUtilKtTest.kt @@ -20,6 +20,7 @@ package com.wire.android.util import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test +import java.util.Calendar class DateTimeUtilKtTest { @@ -40,4 +41,101 @@ class DateTimeUtilKtTest { val result = "2022-03-24T18:02:30.360Z".formatMediumDateTime() assertEquals("Mar 24, 2022, 6:02:30 PM", result) } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Now`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime(getDummyCalendar().timeInMillis) + assertEquals(MessageDateTimeGroup.Now, result) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Within30Minutes`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + add(Calendar.MINUTE, 10) + }.timeInMillis + ) + assertEquals(MessageDateTimeGroup.Within30Minutes, result) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Today`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + add(Calendar.MINUTE, 31) + }.timeInMillis + ) + assertEquals( + MessageDateTimeGroup.Daily.Type.Today, + (result as MessageDateTimeGroup.Daily).type + ) + assertEquals( + "2024-01-20", + result.date.toString() + ) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Yesterday`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + add(Calendar.DATE, 1) + }.timeInMillis + ) + + assertEquals( + MessageDateTimeGroup.Daily.Type.Yesterday, + (result as MessageDateTimeGroup.Daily).type + ) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_WithinWeek`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + add(Calendar.DATE, 3) + }.timeInMillis + ) + + assertEquals( + MessageDateTimeGroup.Daily.Type.WithinWeek, + (result as MessageDateTimeGroup.Daily).type + ) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_NotWithinWeekButSameYear`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + add(Calendar.DATE, 10) + }.timeInMillis + ) + assertEquals( + MessageDateTimeGroup.Daily.Type.NotWithinWeekButSameYear, + (result as MessageDateTimeGroup.Daily).type + ) + } + + @Test + fun `given valid date, when transforming to ui message date time, then return MessageDateTime_Other`() { + val result = "2024-01-20T07:00:00.000Z".groupedUIMessageDateTime( + getDummyCalendar().apply { + set(Calendar.YEAR, 2025) + }.timeInMillis + ) + assertEquals( + MessageDateTimeGroup.Daily.Type.Other, + (result as MessageDateTimeGroup.Daily).type + ) + } + + private fun getDummyCalendar(): Calendar = Calendar.getInstance().apply { + set(Calendar.SECOND, 0) + set(Calendar.MINUTE, 0) + set(Calendar.HOUR, 7) + set(Calendar.AM_PM, Calendar.AM) + set(Calendar.MONTH, Calendar.JANUARY) + set(Calendar.DAY_OF_MONTH, 20) + set(Calendar.YEAR, 2024) + } } From 2bbde9cad5e478c23d55c3e15b833edcc66d4dd9 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 06:46:39 -0300 Subject: [PATCH 8/9] chore: update kalium reference Signed-off-by: alexandreferris --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index bbf74b111bd..b06a4b606bc 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit bbf74b111bde930b8acb3639563ef0c2c32f56a4 +Subproject commit b06a4b606bc8f71305cab1d2b940355d943703e2 From f7a1ce47ee066ed973470af84e640e2544dabcf9 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Fri, 3 May 2024 07:32:56 -0300 Subject: [PATCH 9/9] empty trigger commit