From 955917547957faef34e2c0b4cfd24d8fcd91ebd5 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Tue, 1 Sep 2026 10:05:10 +0200 Subject: [PATCH] Migrate the message reaction lists to the generated ReactionResponse model --- .../client/api2/mapping/DomainMapping.kt | 19 +++------- .../client/api2/model/dto/MessageDtos.kt | 5 +-- .../android/client/EventChatJsonProvider.kt | 23 ++++++++++-- .../getstream/chat/android/client/Mother.kt | 4 +-- .../android/client/parser/EventArguments.kt | 21 ++++++++++- .../parser2/testdata/MessageDtoTestData.kt | 4 +-- .../parser2/testdata/MessageTestData.kt | 36 ++++++++++++++----- .../parser2/testdata/ReactionDtoTestData.kt | 27 ++++++++++++++ 8 files changed, 107 insertions(+), 32 deletions(-) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt index db124811efc..9d9f79368a9 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt @@ -304,17 +304,13 @@ internal class DomainMapping( html = html, i18n = i18n, id = id, - latestReactions = latest_reactions.toDomain( - messageId = id, - ), + latestReactions = latest_reactions.toReactions(messageId = id), mentionedUsers = mentioned_users.map { it.toDomain() }, mentionedHere = mentioned_here ?: false, mentionedChannel = mentioned_channel ?: false, mentionedGroups = mentioned_groups.map { it.toDomain() }, mentionedRoles = mentioned_roles, - ownReactions = own_reactions.toDomain( - messageId = id, - ), + ownReactions = own_reactions.toReactions(messageId = id), parentId = parent_id, pinExpires = pin_expires, pinned = pinned, @@ -386,18 +382,13 @@ internal class DomainMapping( ) /** - * Map a list of [DownstreamReactionDto] to a list of [Reaction]. - * They are filtered by [messageId] and mapped to domain model. - * - * @param messageId the message id + * Maps the reactions of one message, dropping any the response attributes to another message. */ @StreamHandsOff( reason = "Backend response is including wrong reactions for the message, so we need to filter them manually.", ) - private fun List.toDomain( - messageId: String, - ): List = - filter { it.message_id == messageId } + private fun List.toReactions(messageId: String): List = + filter { it.messageId == messageId } .map { it.toDomain() } private fun DownstreamMessageDto.lastUpdateTime(): Date = listOfNotNull( diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt index a367c70bc80..e3f1663dad2 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt @@ -20,6 +20,7 @@ import com.squareup.moshi.JsonClass import io.getstream.chat.android.core.internal.StreamHandsOff import io.getstream.chat.android.network.models.Attachment import io.getstream.chat.android.network.models.PollResponseData +import io.getstream.chat.android.network.models.ReactionResponse import java.util.Date /** @@ -41,13 +42,13 @@ internal data class DownstreamMessageDto( val html: String, val i18n: Map = emptyMap(), val id: String, - val latest_reactions: List, + val latest_reactions: List, val mentioned_users: List, val mentioned_here: Boolean? = null, val mentioned_channel: Boolean? = null, val mentioned_groups: List = emptyList(), val mentioned_roles: List = emptyList(), - val own_reactions: List, + val own_reactions: List, val parent_id: String?, val pin_expires: Date?, val pinned: Boolean = false, diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/EventChatJsonProvider.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/EventChatJsonProvider.kt index e3cc59c928f..cae4205183b 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/EventChatJsonProvider.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/EventChatJsonProvider.kt @@ -814,6 +814,7 @@ private fun createUserJsonString() = { "id": "bender", "role": "user", + "language": "en", "created_at": "2020-06-29T06:14:28.000Z", "updated_at": "2020-06-29T06:14:28.000Z", "last_active": "2020-06-29T06:14:28.000Z", @@ -878,6 +879,23 @@ private fun createMessageAttachmentJsonString() = } """.trimIndent() +@Language("JSON") +private fun createReactionUserJsonString() = + """ + { + "id": "bender", + "role": "user", + "language": "en", + "created_at": "2020-06-29T06:14:28.000Z", + "updated_at": "2020-06-29T06:14:28.000Z", + "last_active": "2020-06-29T06:14:28.000Z", + "banned": false, + "online": true, + "image": "https://api.adorable.io/avatars/285/bender.png", + "name": "Bender" + } + """.trimIndent() + @Language("JSON") private fun createMessageReactionJsonString() = """ @@ -885,9 +903,10 @@ private fun createMessageReactionJsonString() = "message_id": "09afcd85-9dbb-4da8-8d85-5a6b4268d755", "type": "like", "score": 1, - "user": ${createUserJsonString()}, + "user": ${createReactionUserJsonString()}, "user_id": "bender", - "created_at": "2020-06-29T06:14:28.000Z" + "created_at": "2020-06-29T06:14:28.000Z", + "updated_at": "2020-06-29T06:14:28.000Z" } """.trimIndent() diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt index df123c37ca8..71767cc69fe 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt @@ -303,13 +303,13 @@ internal object Mother { html: String = randomString(), i18n: Map = emptyMap(), id: String = randomString(), - latest_reactions: List = emptyList(), + latest_reactions: List = emptyList(), mentioned_users: List = emptyList(), mentioned_here: Boolean? = null, mentioned_channel: Boolean? = null, mentioned_groups: List = emptyList(), mentioned_roles: List = emptyList(), - own_reactions: List = emptyList(), + own_reactions: List = emptyList(), parent_id: String? = randomString(), pin_expires: Date? = randomDateOrNull(), pinned: Boolean = randomBoolean(), diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser/EventArguments.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser/EventArguments.kt index 7a5d518537b..efe358e2479 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser/EventArguments.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser/EventArguments.kt @@ -187,6 +187,7 @@ internal object EventArguments { private val user = User( id = "bender", role = "user", + language = "en", invisible = false, banned = false, online = true, @@ -267,13 +268,31 @@ internal object EventArguments { mimeType = "image/png", ) + /** + * A reaction embedded in a message is parsed by the generated model, so its user is the shape the + * wire sends for anyone but the connected user: no unread counts, devices, mutes or `invisible`. + */ + private val reactionUser = User( + id = "bender", + role = "user", + language = "en", + banned = false, + online = true, + createdAt = date, + updatedAt = date, + lastActive = date, + name = "Bender", + image = "https://api.adorable.io/avatars/285/bender.png", + ) + private val messageReaction = Reaction( messageId = "09afcd85-9dbb-4da8-8d85-5a6b4268d755", type = "like", score = 1, - user = user, + user = reactionUser, userId = "bender", createdAt = date, + updatedAt = date, ) private val message = Message( diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt index 3e8ab311505..13185ad94e7 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt @@ -37,7 +37,7 @@ internal object MessageDtoTestData { "html": "html", "i18n": {}, "id": "8584452-6d711169-0224-41c2-b9aa-1adbe624521b", - "latest_reactions": [${ReactionDtoTestData.downstreamJsonWithoutExtraData}], + "latest_reactions": [${ReactionDtoTestData.reactionResponseJson}], "mentioned_users": [${UserDtoTestData.downstreamJsonWithoutExtraData}], "mentioned_here": true, "mentioned_channel": true, @@ -158,7 +158,7 @@ internal object MessageDtoTestData { last_reaction_at = Date(1591787071588), ), ), - latest_reactions = listOf(ReactionDtoTestData.downstreamReactionWithoutExtraData), + latest_reactions = listOf(ReactionDtoTestData.reactionResponse), own_reactions = emptyList(), show_in_channel = false, mentioned_users = listOf(UserDtoTestData.downstreamUserWithoutExtraData), diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageTestData.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageTestData.kt index 1c2a1af5924..5f01b77c880 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageTestData.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageTestData.kt @@ -61,7 +61,7 @@ internal object MessageTestData { "type": "like", "score": 1, "user_id": "user-2", - "user": {"id": "user-2", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-2", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T01:00:00.000Z", "updated_at": "2020-01-01T01:00:00.000Z" } @@ -72,7 +72,7 @@ internal object MessageTestData { "type": "love", "score": 1, "user_id": "user-1", - "user": {"id": "user-1", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-1", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T00:30:00.000Z", "updated_at": "2020-01-01T00:30:00.000Z" } @@ -867,7 +867,7 @@ internal object MessageTestData { "type": "like", "score": 1, "user_id": "user-2", - "user": {"id": "user-2", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-2", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T01:00:00.000Z", "updated_at": "2020-01-01T01:00:00.000Z" }, @@ -876,7 +876,7 @@ internal object MessageTestData { "type": "love", "score": 1, "user_id": "user-3", - "user": {"id": "user-3", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-3", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T01:01:00.000Z", "updated_at": "2020-01-01T01:01:00.000Z" }, @@ -885,7 +885,7 @@ internal object MessageTestData { "type": "haha", "score": 1, "user_id": "user-4", - "user": {"id": "user-4", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-4", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T01:02:00.000Z", "updated_at": "2020-01-01T01:02:00.000Z" } @@ -896,7 +896,7 @@ internal object MessageTestData { "type": "love", "score": 1, "user_id": "user-1", - "user": {"id": "user-1", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-1", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T00:30:00.000Z", "updated_at": "2020-01-01T00:30:00.000Z" }, @@ -905,7 +905,7 @@ internal object MessageTestData { "type": "wow", "score": 1, "user_id": "user-1", - "user": {"id": "user-1", "role": "user", "banned": false, "online": true}, + "user": {"id": "user-1", "role": "user", "banned": false, "online": true, "created_at": "2020-01-01T00:00:00.000Z", "updated_at": "2020-01-01T00:00:00.000Z", "language": "en"}, "created_at": "2020-01-01T00:31:00.000Z", "updated_at": "2020-01-01T00:31:00.000Z" } @@ -936,7 +936,16 @@ internal object MessageTestData { type = "like", score = 1, userId = "user-2", - user = User(id = "user-2", role = "user", invisible = null, banned = false, online = true), + user = User( + id = "user-2", + role = "user", + invisible = null, + banned = false, + online = true, + language = "en", + createdAt = Date(1577836800000L), + updatedAt = Date(1577836800000L), + ), createdAt = Date(1577840400000L), updatedAt = Date(1577840400000L), ), @@ -947,7 +956,16 @@ internal object MessageTestData { type = "love", score = 1, userId = "user-1", - user = User(id = "user-1", role = "user", invisible = null, banned = false, online = true), + user = User( + id = "user-1", + role = "user", + invisible = null, + banned = false, + online = true, + language = "en", + createdAt = Date(1577836800000L), + updatedAt = Date(1577836800000L), + ), createdAt = Date(1577838600000L), updatedAt = Date(1577838600000L), ), diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/ReactionDtoTestData.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/ReactionDtoTestData.kt index 2ee22328ffc..0128a354e65 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/ReactionDtoTestData.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/ReactionDtoTestData.kt @@ -18,6 +18,7 @@ package io.getstream.chat.android.client.parser2.testdata import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionDto import io.getstream.chat.android.network.models.ReactionRequest +import io.getstream.chat.android.network.models.ReactionResponse import org.intellij.lang.annotations.Language import java.util.Date @@ -78,6 +79,32 @@ internal object ReactionDtoTestData { extraData = emptyMap(), ) + /** + * The wire always sends both timestamps and a full user for a reaction, so the generated model + * declares them non-null. + */ + @Language("JSON") + val reactionResponseJson = + """{ + "message_id": "8584452-6d711169-0224-41c2-b9aa-1adbe624521b", + "score": 0, + "type": "like", + "user": ${UserDtoTestData.userResponseJson}, + "user_id": "", + "created_at": "2020-06-10T11:04:31.000Z", + "updated_at": "2020-06-10T11:04:31.588Z" + }""".withoutWhitespace() + + val reactionResponse = ReactionResponse( + messageId = "8584452-6d711169-0224-41c2-b9aa-1adbe624521b", + type = "like", + score = 0, + user = UserDtoTestData.userResponse, + userId = "", + createdAt = Date(1591787071000), + updatedAt = Date(1591787071588), + ) + @Language("JSON") val upstreamJsonWithoutExtraData = """{