diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/EventAdapter.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/EventAdapter.kt index 2dbfd7f01257..267beade3b9f 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/EventAdapter.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/EventAdapter.kt @@ -100,26 +100,23 @@ internal class EventAdapterFactory : JsonAdapter.Factory { } } -internal class EventDtoAdapter( - private val moshi: Moshi, -) : JsonAdapter() { - - private val mapAdapter: JsonAdapter> = - moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) - - private val connectedEventAdapter = generatedEventAdapter { emptyMap() } - private val connectionErrorEventAdapter = moshi.adapter(ConnectionErrorEventDto::class.java) - private val healthEventAdapter = generatedEventAdapter { emptyMap() } - private val draftMessageUpdatedEventAdapter = generatedEventAdapter { mapOf("draft" to draft) } - private val draftMessageDeletedEventAdapter = generatedEventAdapter { mapOf("draft" to draft) } - private val newMessageEventAdapter = generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val messageDeletedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val messageUpdatedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val messageReadEventAdapter = generatedEventAdapter { +/** + * The generated adapter of every event type that always maps to one model, shared by the v1 and v2 socket parsers. + * Types whose model depends on the payload, connection errors and unknown types are left to each parser. + */ +@Suppress("LongMethod") +internal fun generatedEventAdapters(moshi: Moshi): Map> = mapOf( + EventType.DRAFT_MESSAGE_UPDATED to generatedEventAdapter(moshi) { mapOf("draft" to draft) }, + EventType.DRAFT_MESSAGE_DELETED to generatedEventAdapter(moshi) { mapOf("draft" to draft) }, + EventType.MESSAGE_NEW to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) }, + EventType.MESSAGE_DELETED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.MESSAGE_UPDATED to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.MESSAGE_READ to generatedEventAdapter(moshi) { mapOf("cid" to cid, "channel_type" to channelType, "channel_id" to channelId, "user" to user) - } - private val messageDeliveredEventAdapter = generatedEventAdapter { + }, + EventType.MESSAGE_DELIVERED to generatedEventAdapter(moshi) { mapOf( "cid" to cid, "channel_type" to channelType, @@ -129,41 +126,40 @@ internal class EventDtoAdapter( "last_delivered_at" to lastDeliveredAt?.let(GeneratedExactDate::parseOrNull), "last_delivered_message_id" to lastDeliveredMessageId, ) - } - private val typingStartEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val typingStopEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val reactionNewEventAdapter = generatedEventAdapter { + }, + EventType.TYPING_START to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) }, + EventType.TYPING_STOP to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) }, + EventType.REACTION_NEW to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user, "message" to message, "reaction" to reaction) - } - private val reactionUpdatedEventAdapter = generatedEventAdapter { + }, + EventType.REACTION_UPDATED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user, "reaction" to reaction) - } - private val reactionDeletedEventAdapter = generatedEventAdapter { + }, + EventType.REACTION_DELETED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user, "message" to message, "reaction" to reaction) - } - private val memberAddedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val memberRemovedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val memberUpdatedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val channelUpdatedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val channelHiddenEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val channelDeletedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val channelVisibleEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val channelTruncatedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val userStartWatchingEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val userStopWatchingEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val notificationAddedToChannelEventAdapter = - generatedEventAdapter { mapOf("cid" to cid) } - private val notificationMarkReadEventAdapter = generatedEventAdapter { - mapOf("cid" to cid, "channel_type" to channelType, "channel_id" to channelId, "user" to user) - } - private val notificationMarkUnreadEventAdapter = generatedEventAdapter { + }, + EventType.MEMBER_ADDED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) }, + EventType.MEMBER_REMOVED to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.MEMBER_UPDATED to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.CHANNEL_UPDATED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.CHANNEL_HIDDEN to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.CHANNEL_DELETED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.CHANNEL_VISIBLE to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.CHANNEL_TRUNCATED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.USER_WATCHING_START to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.USER_WATCHING_STOP to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.NOTIFICATION_ADDED_TO_CHANNEL to generatedEventAdapter(moshi) { + mapOf("cid" to cid) + }, + EventType.NOTIFICATION_MARK_UNREAD to generatedEventAdapter(moshi) { mapOf( "cid" to cid, "user" to user, @@ -171,60 +167,107 @@ internal class EventDtoAdapter( "last_read_at" to lastReadAt, "unread_messages" to unreadMessages, ) - } - private val markAllReadEventAdapter = generatedEventAdapter { mapOf("user" to user) } - private val notificationMessageNewEventAdapter = generatedEventAdapter { + }, + EventType.NOTIFICATION_MESSAGE_NEW to generatedEventAdapter(moshi) { mapOf("cid" to cid, "channel_type" to channelType, "channel_id" to channelId) - } - private val notificationThreadMessageNewEventAdapter = generatedEventAdapter { + }, + EventType.NOTIFICATION_THREAD_MESSAGE_NEW to generatedEventAdapter(moshi) { mapOf("cid" to cid, "unread_threads" to unreadThreads, "unread_thread_messages" to unreadThreadMessages) - } - private val threadUpdatedEventAdapter = generatedEventAdapter { + }, + EventType.THREAD_UPDATED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "channel_type" to channelType, "channel_id" to channelId, "thread" to thread) - } - private val notificationInvitedEventAdapter = generatedEventAdapter { + }, + EventType.NOTIFICATION_INVITED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) - } - private val notificationInviteAcceptedEventAdapter = generatedEventAdapter { + }, + EventType.NOTIFICATION_INVITE_ACCEPTED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) - } - private val notificationInviteRejectedEventAdapter = generatedEventAdapter { + }, + EventType.NOTIFICATION_INVITE_REJECTED to generatedEventAdapter(moshi) { mapOf("cid" to cid, "user" to user) + }, + EventType.NOTIFICATION_REMOVED_FROM_CHANNEL to generatedEventAdapter(moshi) { + mapOf("cid" to cid) + }, + EventType.NOTIFICATION_MUTES_UPDATED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.NOTIFICATION_CHANNEL_MUTES_UPDATED to generatedEventAdapter(moshi) { + emptyMap() + }, + EventType.NOTIFICATION_CHANNEL_DELETED to generatedEventAdapter(moshi) { + mapOf("cid" to cid) + }, + EventType.NOTIFICATION_CHANNEL_TRUNCATED to generatedEventAdapter(moshi) { + mapOf("cid" to cid) + }, + EventType.USER_PRESENCE_CHANGED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.USER_UPDATED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.USER_DELETED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.USER_BANNED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.USER_UNBANNED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.POLL_UPDATED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.POLL_DELETED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.POLL_CLOSED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.POLL_VOTE_CASTED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.POLL_VOTE_CHANGED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.POLL_VOTE_REMOVED to generatedEventAdapter(moshi) { mapOf("cid" to cid) }, + EventType.REMINDER_CREATED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.REMINDER_UPDATED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.REMINDER_DELETED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.NOTIFICATION_REMINDER_DUE to generatedEventAdapter(moshi) { emptyMap() }, + EventType.USER_MESSAGES_DELETED to generatedEventAdapter(moshi) { emptyMap() }, + EventType.AI_TYPING_INDICATOR_UPDATED to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.AI_TYPING_INDICATOR_CLEAR to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, + EventType.AI_TYPING_INDICATOR_STOP to generatedEventAdapter(moshi) { + mapOf("cid" to cid, "user" to user) + }, +) + +internal inline fun generatedEventAdapter( + moshi: Moshi, + noinline required: T.() -> Map, +): JsonAdapter = GeneratedEventAdapter(moshi.adapter(T::class.java), required) + +/** + * Parses an event with its generated model. The spec makes some fields optional that the domain event + * requires (e.g. the cid, since feeds shares these events without one); an event missing any of the + * [required] fields is rejected. + */ +internal class GeneratedEventAdapter( + private val delegate: JsonAdapter, + private val required: T.() -> Map, +) : JsonAdapter() { + override fun fromJson(reader: JsonReader): ChatEventDto? = delegate.fromJson(reader)?.let { event -> + event.required().entries.firstOrNull { it.value == null }?.let { (name, _) -> + throw JsonDataException("Required value '$name' missing for ${event.getWSEventType()}") + } + GeneratedEventDto(event) } - private val notificationRemovedFromChannelEventAdapter = - generatedEventAdapter { mapOf("cid" to cid) } - private val notificationMutesUpdatedEventAdapter = - generatedEventAdapter { emptyMap() } - private val notificationChannelMutesUpdatedEventAdapter = - generatedEventAdapter { emptyMap() } - private val notificationChannelDeletedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid) } - private val notificationChannelTruncatedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid) } - private val userPresenceChangedEventAdapter = generatedEventAdapter { emptyMap() } - private val userUpdatedEventAdapter = generatedEventAdapter { emptyMap() } - private val userDeletedEventAdapter = generatedEventAdapter { emptyMap() } - private val userBannedEventAdapter = generatedEventAdapter { emptyMap() } - private val userUnbannedEventAdapter = generatedEventAdapter { emptyMap() } - private val pollUpdatedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val pollDeletedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val pollClosedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val pollVoteCastedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val pollVoteChangedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val pollVoteRemovedEventAdapter = generatedEventAdapter { mapOf("cid" to cid) } - private val reminderCreatedEventAdapter = generatedEventAdapter { emptyMap() } - private val reminderUpdatedEventAdapter = generatedEventAdapter { emptyMap() } - private val reminderDeletedEventAdapter = generatedEventAdapter { emptyMap() } - private val notificationReminderDueEventAdapter = generatedEventAdapter { emptyMap() } - private val userMessagesDeletedEventAdapter = generatedEventAdapter { emptyMap() } - private val aiTypingIndicatorUpdatedEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val aiTypingIndicatorClearEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - private val aiTypingIndicatorStopEventAdapter = - generatedEventAdapter { mapOf("cid" to cid, "user" to user) } - @Suppress("LongMethod", "ComplexMethod", "ReturnCount") + override fun toJson(writer: JsonWriter, value: ChatEventDto?): Unit = error("Can't convert this event to Json") +} + +internal class EventDtoAdapter( + private val moshi: Moshi, +) : JsonAdapter() { + + private val mapAdapter: JsonAdapter> = + moshi.adapter(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)) + + private val generatedAdapters = generatedEventAdapters(moshi) + private val connectedEventAdapter = generatedEventAdapter(moshi) { emptyMap() } + private val connectionErrorEventAdapter = moshi.adapter(ConnectionErrorEventDto::class.java) + private val healthEventAdapter = generatedEventAdapter(moshi) { emptyMap() } + private val notificationMarkReadEventAdapter = generatedEventAdapter(moshi) { + mapOf("cid" to cid, "channel_type" to channelType, "channel_id" to channelId, "user" to user) + } + private val markAllReadEventAdapter = + generatedEventAdapter(moshi) { mapOf("user" to user) } + + @Suppress("ReturnCount") override fun fromJson(reader: JsonReader): ChatEventDto? { if (reader.peek() == JsonReader.Token.NULL) { reader.nextNull() @@ -239,65 +282,11 @@ internal class EventDtoAdapter( else -> healthEventAdapter } EventType.CONNECTION_ERROR -> connectionErrorEventAdapter - EventType.DRAFT_MESSAGE_UPDATED -> draftMessageUpdatedEventAdapter - EventType.DRAFT_MESSAGE_DELETED -> draftMessageDeletedEventAdapter - EventType.MESSAGE_NEW -> newMessageEventAdapter - EventType.MESSAGE_DELETED -> messageDeletedEventAdapter - EventType.MESSAGE_UPDATED -> messageUpdatedEventAdapter - EventType.MESSAGE_READ -> messageReadEventAdapter - EventType.MESSAGE_DELIVERED -> messageDeliveredEventAdapter - EventType.TYPING_START -> typingStartEventAdapter - EventType.TYPING_STOP -> typingStopEventAdapter - EventType.REACTION_NEW -> reactionNewEventAdapter - EventType.REACTION_UPDATED -> reactionUpdatedEventAdapter - EventType.REACTION_DELETED -> reactionDeletedEventAdapter - EventType.MEMBER_ADDED -> memberAddedEventAdapter - EventType.MEMBER_REMOVED -> memberRemovedEventAdapter - EventType.MEMBER_UPDATED -> memberUpdatedEventAdapter - EventType.CHANNEL_UPDATED -> channelUpdatedEventAdapter - EventType.CHANNEL_HIDDEN -> channelHiddenEventAdapter - EventType.CHANNEL_DELETED -> channelDeletedEventAdapter - EventType.CHANNEL_VISIBLE -> channelVisibleEventAdapter - EventType.CHANNEL_TRUNCATED -> channelTruncatedEventAdapter - EventType.USER_WATCHING_START -> userStartWatchingEventAdapter - EventType.USER_WATCHING_STOP -> userStopWatchingEventAdapter - EventType.NOTIFICATION_ADDED_TO_CHANNEL -> notificationAddedToChannelEventAdapter EventType.NOTIFICATION_MARK_READ -> when { map.containsKey("cid") -> notificationMarkReadEventAdapter else -> markAllReadEventAdapter } - EventType.NOTIFICATION_MARK_UNREAD -> notificationMarkUnreadEventAdapter - EventType.NOTIFICATION_MESSAGE_NEW -> notificationMessageNewEventAdapter - EventType.NOTIFICATION_THREAD_MESSAGE_NEW -> notificationThreadMessageNewEventAdapter - EventType.THREAD_UPDATED -> threadUpdatedEventAdapter - EventType.NOTIFICATION_INVITED -> notificationInvitedEventAdapter - EventType.NOTIFICATION_INVITE_ACCEPTED -> notificationInviteAcceptedEventAdapter - EventType.NOTIFICATION_INVITE_REJECTED -> notificationInviteRejectedEventAdapter - EventType.NOTIFICATION_REMOVED_FROM_CHANNEL -> notificationRemovedFromChannelEventAdapter - EventType.NOTIFICATION_MUTES_UPDATED -> notificationMutesUpdatedEventAdapter - EventType.NOTIFICATION_CHANNEL_MUTES_UPDATED -> notificationChannelMutesUpdatedEventAdapter - EventType.NOTIFICATION_CHANNEL_DELETED -> notificationChannelDeletedEventAdapter - EventType.NOTIFICATION_CHANNEL_TRUNCATED -> notificationChannelTruncatedEventAdapter - EventType.USER_PRESENCE_CHANGED -> userPresenceChangedEventAdapter - EventType.USER_UPDATED -> userUpdatedEventAdapter - EventType.USER_DELETED -> userDeletedEventAdapter - EventType.USER_BANNED -> userBannedEventAdapter - EventType.USER_UNBANNED -> userUnbannedEventAdapter - EventType.USER_MESSAGES_DELETED -> userMessagesDeletedEventAdapter - EventType.POLL_UPDATED -> pollUpdatedEventAdapter - EventType.POLL_DELETED -> pollDeletedEventAdapter - EventType.POLL_CLOSED -> pollClosedEventAdapter - EventType.POLL_VOTE_CASTED -> pollVoteCastedEventAdapter - EventType.POLL_VOTE_CHANGED -> pollVoteChangedEventAdapter - EventType.POLL_VOTE_REMOVED -> pollVoteRemovedEventAdapter - EventType.REMINDER_CREATED -> reminderCreatedEventAdapter - EventType.REMINDER_UPDATED -> reminderUpdatedEventAdapter - EventType.REMINDER_DELETED -> reminderDeletedEventAdapter - EventType.NOTIFICATION_REMINDER_DUE -> notificationReminderDueEventAdapter - EventType.AI_TYPING_INDICATOR_UPDATED -> aiTypingIndicatorUpdatedEventAdapter - EventType.AI_TYPING_INDICATOR_CLEAR -> aiTypingIndicatorClearEventAdapter - EventType.AI_TYPING_INDICATOR_STOP -> aiTypingIndicatorStopEventAdapter - else -> // Custom case, early return + else -> generatedAdapters[type] ?: // Custom case, early return return UnknownEventDto( type = type ?: EventType.UNKNOWN, created_at = moshi.adapter(ExactDate::class.java).fromJsonValue(map["created_at"])!!, @@ -309,29 +298,6 @@ internal class EventDtoAdapter( return adapter.fromJsonValue(map) } - private inline fun generatedEventAdapter( - noinline required: T.() -> Map, - ): JsonAdapter = GeneratedEventAdapter(moshi.adapter(T::class.java), required) - - /** - * Parses an event with its generated model. The spec makes some fields optional that the domain event - * requires (e.g. the cid, since feeds shares these events without one); an event missing any of the - * [required] fields is rejected. - */ - private class GeneratedEventAdapter( - private val delegate: JsonAdapter, - private val required: T.() -> Map, - ) : JsonAdapter() { - override fun fromJson(reader: JsonReader): ChatEventDto? = delegate.fromJson(reader)?.let { event -> - event.required().entries.firstOrNull { it.value == null }?.let { (name, _) -> - throw JsonDataException("Required value '$name' missing for ${event.getWSEventType()}") - } - GeneratedEventDto(event) - } - - override fun toJson(writer: JsonWriter, value: ChatEventDto?): Unit = error("Can't convert this event to Json") - } - override fun toJson(writer: JsonWriter, value: ChatEventDto?) { error("Can't convert this event to Json $value") }