Make the channel preview deterministic when the last message is delet… · GetStream/stream-chat-android@0dadef8 · GitHub
Skip to content

Commit 0dadef8

Browse files
authored
Make the channel preview deterministic when the last message is deleted (#6577)
* compose: Pick the later message on creation-time ties for the channel preview The preview selection returned the first of the messages tied on createdAt, so backends with second-precision timestamps made the deleted-message preview depend on send timing. Prefer the later list element, which is the newer message. Also make the preview fixture dates strictly increasing so the Paparazzi goldens stop depending on initialization timing. * e2e: Expect the deleted-message placeholder in the channel preview The test expected the previous message, which predates the channel list redesign and current iOS behavior. It only passed when the mock server's second-precision timestamps made the two messages tie. * ui-components: Update the channel list goldens for the ordered preview fixtures The preview fixture dates are now strictly increasing, so the channel list preview renders message2 as the newest message instead of depending on initialization timing.
1 parent f4e7c55 commit 0dadef8

23 files changed

Lines changed: 164 additions & 14 deletions

File tree

‎stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/tests/ChannelListTests.kt‎

Lines changed: 3 additions & 3 deletions

‎stream-chat-android-compose/api/stream-chat-android-compose.api‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,9 @@ public final class io/getstream/chat/android/compose/ui/channels/list/Composable
934934
public final fun getLambda$1340453964$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
935935
public final fun getLambda$1561934893$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
936936
public final fun getLambda$1593816240$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
937+
public final fun getLambda$169276223$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
937938
public final fun getLambda$1946891593$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
939+
public final fun getLambda$2027448065$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
938940
public final fun getLambda$2130050484$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
939941
public final fun getLambda$2144537814$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
940942
public final fun getLambda$230028639$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;

‎stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channels/list/ChannelItem.kt‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import io.getstream.chat.android.models.SyncStatus
8787
import io.getstream.chat.android.models.User
8888
import io.getstream.chat.android.previewdata.PreviewChannelData
8989
import io.getstream.chat.android.previewdata.PreviewChannelUserRead
90+
import io.getstream.chat.android.previewdata.PreviewMessageData
9091
import io.getstream.chat.android.previewdata.PreviewUserData
9192
import java.util.Date
9293

@@ -770,6 +771,54 @@ internal fun ChannelItemDraftMessage() {
770771
)
771772
}
772773

774+
@Preview(showBackground = true)
775+
@Composable
776+
private fun ChannelItemDeletedLastMessagePreview() {
777+
ChatTheme {
778+
ChannelItemDeletedLastMessage()
779+
}
780+
}
781+
782+
/**
783+
* The newest message is deleted while an older regular message exists,
784+
* so the preview shows the deleted placeholder.
785+
*/
786+
@Composable
787+
internal fun ChannelItemDeletedLastMessage() {
788+
ChannelItem(
789+
currentUser = PreviewUserData.user1,
790+
channel = PreviewChannelData.channelWithMessages.copy(
791+
messages = listOf(
792+
PreviewMessageData.message1,
793+
PreviewMessageData.message2.copy(deletedAt = Date()),
794+
),
795+
),
796+
)
797+
}
798+
799+
@Preview(showBackground = true)
800+
@Composable
801+
private fun ChannelItemOnlyDeletedMessagePreview() {
802+
ChatTheme {
803+
ChannelItemOnlyDeletedMessage()
804+
}
805+
}
806+
807+
/**
808+
* The only message in the channel is deleted, so the preview shows the deleted placeholder.
809+
*/
810+
@Composable
811+
internal fun ChannelItemOnlyDeletedMessage() {
812+
ChannelItem(
813+
currentUser = PreviewUserData.user1,
814+
channel = PreviewChannelData.channelWithMessages.copy(
815+
messages = listOf(
816+
PreviewMessageData.message1.copy(deletedAt = Date()),
817+
),
818+
),
819+
)
820+
}
821+
773822
@Composable
774823
private fun ChannelItem(
775824
currentUser: User?,

‎stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ChannelUtils.kt‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public fun Channel.getLastMessage(currentUser: User?): Message? = getPreviewMess
4242
/**
4343
* Returns channel's last regular or system message, **including deleted messages**.
4444
* Used by the channel list to show "Message deleted" when the last message was deleted.
45+
* When several messages share the same creation time, the one closest to the end of
46+
* [Channel.messages] wins, since the list keeps insertion order for equal timestamps.
4547
*/
4648
internal fun Channel.getLastMessageIncludingDeleted(currentUser: User?): Message? {
4749
// Consider last pending message (if available)
@@ -57,7 +59,15 @@ internal fun Channel.getLastMessageIncludingDeleted(currentUser: User?): Message
5759
.filter { it.createdAt != null || it.createdLocallyAt != null }
5860
.filter { it.user.id == currentUser?.id || !it.shadowed }
5961
.filter { it.isRegular() || it.isSystem() || it.isDeleted() }
60-
.maxByOrNull { it.getCreatedAtOrDefault(NEVER) }
62+
// Backends with second-precision timestamps can produce equal creation times,
63+
// so on a tie the later element wins as the newer message
64+
.reduceOrNull { latest, message ->
65+
if (message.getCreatedAtOrDefault(NEVER) >= latest.getCreatedAtOrDefault(NEVER)) {
66+
message
67+
} else {
68+
latest
69+
}
70+
}
6171
}
6272

6373
/**

‎stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/channels/ChannelItemTest.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import app.cash.paparazzi.Paparazzi
2020
import com.android.ide.common.rendering.api.SessionParams
2121
import io.getstream.chat.android.compose.ui.PIXEL_2_HDPI
2222
import io.getstream.chat.android.compose.ui.PaparazziComposeTest
23+
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemDeletedLastMessage
2324
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemDraftMessage
2425
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemLastMessageDeliveredStatus
2526
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemLastMessagePendingStatus
@@ -31,6 +32,7 @@ import io.getstream.chat.android.compose.ui.channels.list.ChannelItemMutedPinned
3132
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemMutedPinnedTrailingBottom
3233
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemMutedTrailingBottom
3334
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemNoMessages
35+
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemOnlyDeletedMessage
3436
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemPinned
3537
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemPinnedTrailingBottom
3638
import io.getstream.chat.android.compose.ui.channels.list.ChannelItemUnreadMessages
@@ -142,4 +144,18 @@ internal class ChannelItemTest : PaparazziComposeTest {
142144
ChannelItemDraftMessage()
143145
}
144146
}
147+
148+
@Test
149+
fun `deleted last message`() {
150+
snapshotWithDarkMode {
151+
ChannelItemDeletedLastMessage()
152+
}
153+
}
154+
155+
@Test
156+
fun `only deleted message`() {
157+
snapshotWithDarkMode {
158+
ChannelItemOnlyDeletedMessage()
159+
}
160+
}
145161
}

‎stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/util/ChannelUtilsTest.kt‎

Lines changed: 61 additions & 0 deletions
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)