Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ import io.getstream.chat.android.models.querysort.QuerySorter
import io.getstream.chat.android.models.querysort.SortDirection
import io.getstream.chat.android.network.models.AppResponseFields
import io.getstream.chat.android.network.models.BlockUsersResponse
import io.getstream.chat.android.network.models.ChannelConfigWithInfo
import io.getstream.chat.android.network.models.ChannelMemberResponse
import io.getstream.chat.android.network.models.ChannelOwnCapability
import io.getstream.chat.android.network.models.ChannelPushPreferencesResponse
import io.getstream.chat.android.network.models.ChannelResponse
import io.getstream.chat.android.network.models.ChatPreferencesResponse
import io.getstream.chat.android.network.models.DeviceResponse
import io.getstream.chat.android.network.models.GetApplicationResponse
Expand Down Expand Up @@ -225,6 +229,53 @@ internal class DomainMapping(
image = image,
)

/**
* Transforms [ChannelResponse] into [Channel]. Channel-level wire fields only: the type carries
* no `messages`, `watchers`, `read`, `pinned_messages`, `membership` or `active_live_locations`,
* so those stay empty. `name` and `image` are custom data on the wire.
*/
internal fun ChannelResponse.toDomain(): Channel =
Channel(
id = id,
type = type,
name = custom["name"] as? String ?: "",
image = custom["image"] as? String ?: "",
filterTags = filterTags.orEmpty(),
frozen = frozen,
createdAt = createdAt,
deletedAt = deletedAt,
updatedAt = updatedAt,
memberCount = memberCount ?: 0,
members = members.orEmpty().map { it.toDomain() },
hidden = hidden,
hiddenMessagesBefore = hideMessagesBefore,
truncatedAt = truncatedAt,
disabled = disabled,
blocked = blocked,
config = config?.toDomain() ?: Config(),
createdBy = createdBy?.toDomain() ?: User(),
team = team.orEmpty(),
cooldown = cooldown ?: 0,
ownCapabilities = ownCapabilities.orEmpty().mapTo(mutableSetOf(), ChannelOwnCapability::value),
messageCount = messageCount,
lastMessageAt = lastMessageAt,
extraData = custom.mapNotNull { (key, value) -> value?.let { key to it } }
.toMap()
.minus(listOf("name", "image"))
.toMutableMap(),
).syncUnreadCountWithReads(currentUserIdProvider())
.let(channelTransformer::transform)

internal fun ChannelResponse.toChannelInfo(): ChannelInfo =
ChannelInfo(
cid = cid,
id = id,
memberCount = memberCount ?: 0,
name = custom["name"] as? String,
type = type,
image = custom["image"] as? String,
)

/**
* Transforms [DownstreamMessageDto] to [Message].
*/
Expand Down Expand Up @@ -446,6 +497,25 @@ internal class DomainMapping(
extraData = extraData,
)

internal fun ChannelMemberResponse.toDomain(): Member =
Member(
user = user?.toDomain() ?: User(id = userId.orEmpty()),
createdAt = createdAt,
updatedAt = updatedAt,
isInvited = invited,
inviteAcceptedAt = inviteAcceptedAt,
inviteRejectedAt = inviteRejectedAt,
shadowBanned = shadowBanned,
banned = banned,
channelRole = channelRole,
notificationsMuted = notificationsMuted,
status = status,
banExpires = banExpires,
pinnedAt = pinnedAt,
archivedAt = archivedAt,
extraData = custom.mapNotNull { (key, value) -> value?.let { key to it } }.toMap(),
)

internal fun UserResponse.toDomain(): User =
User(
id = id,
Expand Down Expand Up @@ -718,6 +788,40 @@ internal class DomainMapping(
pushLevel = push_level,
)

/**
* Transforms [ChannelConfigWithInfo] to [Config]. The wire carries `message_retention`, but Go
* tags it `openapi:"-"` so it is absent from the generated model; we keep the domain default
* until the spec describes it.
*/
internal fun ChannelConfigWithInfo.toDomain(): Config = Config(
createdAt = createdAt,
updatedAt = updatedAt,
name = name,
typingEventsEnabled = typingEvents,
readEventsEnabled = readEvents,
deliveryEventsEnabled = deliveryEvents,
connectEventsEnabled = connectEvents,
searchEnabled = search,
isReactionsEnabled = reactions,
isThreadEnabled = replies,
muteEnabled = mutes,
uploadsEnabled = uploads,
urlEnrichmentEnabled = urlEnrichment,
customEventsEnabled = customEvents,
pushNotificationsEnabled = pushNotifications,
skipLastMsgUpdateForSystemMsgs = skipLastMsgUpdateForSystemMsgs,
pollsEnabled = polls,
maxMessageLength = maxMessageLength,
automod = automod.value,
automodBehavior = automodBehavior.value,
blocklistBehavior = blocklistBehavior?.value.orEmpty(),
commands = commands.map { it.toDomain() },
messageRemindersEnabled = userMessageReminders,
sharedLocationsEnabled = sharedLocations,
markMessagesPending = markMessagesPending,
pushLevel = pushLevel?.value,
)

/**
* Transforms [DeviceResponse] to [Device].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.getstream.chat.android.client.api2.model.dto

import com.squareup.moshi.JsonClass
import io.getstream.chat.android.network.models.ChannelResponse
import io.getstream.chat.android.network.models.ThreadParticipant
import java.util.Date

Expand Down Expand Up @@ -47,7 +48,7 @@ import java.util.Date
@JsonClass(generateAdapter = true)
internal data class DownstreamThreadDto(
val active_participant_count: Int?,
val channel: DownstreamChannelDto?,
val channel: ChannelResponse?,
val channel_cid: String,
val created_at: Date,
val created_by: DownstreamUserDto?,
Expand Down Expand Up @@ -91,7 +92,7 @@ internal data class DownstreamThreadDto(
@JsonClass(generateAdapter = true)
internal data class DownstreamThreadInfoDto(
val channel_cid: String,
val channel: DownstreamChannelDto?,
val channel: ChannelResponse?,
val parent_message_id: String,
val parent_message: DownstreamMessageDto?,
val created_by_user_id: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import io.getstream.chat.android.client.events.ConnectedEvent
import io.getstream.chat.android.client.extensions.internal.enrichIfNeeded
import io.getstream.chat.android.client.parser.ChatParser
import io.getstream.chat.android.client.parser2.adapters.AttachmentDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.ChannelMemberResponseAdapter
import io.getstream.chat.android.client.parser2.adapters.ChannelResponseAdapter
import io.getstream.chat.android.client.parser2.adapters.CreatePollOptionRequestAdapter
import io.getstream.chat.android.client.parser2.adapters.CreatePollRequestAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamChannelDtoAdapter
Expand Down Expand Up @@ -61,6 +63,9 @@ import io.getstream.chat.android.client.parser2.adapters.UserResponseAdapter
import io.getstream.chat.android.client.socket.ErrorResponse
import io.getstream.chat.android.client.socket.SocketErrorMessage
import io.getstream.chat.android.network.infrastructure.Serializer
import io.getstream.chat.android.network.models.BlockListOptions
import io.getstream.chat.android.network.models.ChannelConfigWithInfo
import io.getstream.chat.android.network.models.ChannelOwnCapability
import io.getstream.chat.android.network.models.CreatePollRequest
import io.getstream.chat.android.network.models.UpdatePollRequest
import retrofit2.Retrofit
Expand All @@ -87,6 +92,8 @@ internal class MoshiChatParser(
.add(UpstreamUserDtoAdapter)
.add(UserResponseAdapter)
.add(UserRequestAdapter)
.add(ChannelResponseAdapter)
.add(ChannelMemberResponseAdapter)
.add(DownstreamMemberDtoAdapter)
.add(UpstreamMemberDtoAdapter)
.add(UpstreamMemberDataDtoAdapter)
Expand All @@ -110,6 +117,30 @@ internal class MoshiChatParser(
UpdatePollRequest.VotingVisibility::class.java,
UpdatePollRequest.VotingVisibility.VotingVisibilityAdapter(),
)
.add(
ChannelOwnCapability::class.java,
ChannelOwnCapability.ChannelOwnCapabilityAdapter(),
)
.add(
ChannelConfigWithInfo.Automod::class.java,
ChannelConfigWithInfo.Automod.AutomodAdapter(),
)
.add(
ChannelConfigWithInfo.AutomodBehavior::class.java,
ChannelConfigWithInfo.AutomodBehavior.AutomodBehaviorAdapter(),
)
.add(
ChannelConfigWithInfo.BlocklistBehavior::class.java,
ChannelConfigWithInfo.BlocklistBehavior.BlocklistBehaviorAdapter(),
)
.add(
ChannelConfigWithInfo.PushLevel::class.java,
ChannelConfigWithInfo.PushLevel.PushLevelAdapter(),
)
.add(
BlockListOptions.Behavior::class.java,
BlockListOptions.Behavior.BehaviorAdapter(),
)
// Registered last so the model-specific adapters above keep precedence and delegate into it.
.add(NullCollectionsAsEmptyFactory)
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.chat.android.client.parser2.adapters

import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.ToJson
import io.getstream.chat.android.network.models.ChannelMemberResponse

// Downstream (read-only) adapter for the generated ChannelMemberResponse: collects root-level custom fields
// into `custom`, matching the wire's flattened extra data. extraDataPropertyName is its @Json name.
internal object ChannelMemberResponseAdapter :
CustomObjectDtoAdapter<ChannelMemberResponse>(ChannelMemberResponse::class, extraDataPropertyName = "custom") {

@FromJson
fun fromJson(
jsonReader: JsonReader,
mapAdapter: JsonAdapter<MutableMap<String, Any>>,
valueAdapter: JsonAdapter<ChannelMemberResponse>,
): ChannelMemberResponse? = parseWithExtraData(jsonReader, mapAdapter, valueAdapter)

@ToJson
@Suppress("UNUSED_PARAMETER")
fun toJson(jsonWriter: JsonWriter, value: ChannelMemberResponse): Unit = error("Can't convert this to Json")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.chat.android.client.parser2.adapters

import com.squareup.moshi.FromJson
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.ToJson
import io.getstream.chat.android.network.models.ChannelResponse

// Downstream (read-only) adapter for the generated ChannelResponse: collects root-level custom fields
// into `custom`, matching the wire's flattened extra data. extraDataPropertyName is its @Json name.
internal object ChannelResponseAdapter :
CustomObjectDtoAdapter<ChannelResponse>(
ChannelResponse::class,
extraDataPropertyName = "custom",
alsoKeepInExtraData = LEGACY_CHANNEL_EXTRA_DATA_KEYS,
) {

@FromJson
fun fromJson(
jsonReader: JsonReader,
mapAdapter: JsonAdapter<MutableMap<String, Any>>,
valueAdapter: JsonAdapter<ChannelResponse>,
): ChannelResponse? = parseWithExtraData(jsonReader, mapAdapter, valueAdapter)

@ToJson
@Suppress("UNUSED_PARAMETER")
fun toJson(jsonWriter: JsonWriter, value: ChannelResponse): Unit = error("Can't convert this to Json")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport",
)

package io.getstream.chat.android.network.models

import com.squareup.moshi.FromJson
import com.squareup.moshi.Json
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.ToJson

/**
*
*/
@com.squareup.moshi.JsonClass(generateAdapter = true)
internal data class BlockListOptions(
@Json(name = "behavior")
internal val behavior: Behavior,

@Json(name = "blocklist")
internal val blocklist: String,
) {

/**
* Behavior Enum
*/
internal sealed class Behavior(internal val value: String) {
override fun toString(): String = value

internal companion object {
internal fun fromString(s: String): Behavior = when (s) {
"block" -> Block
"flag" -> Flag
"shadow_block" -> ShadowBlock
else -> Unknown(s)
}
}
internal object Block : Behavior("block")
internal object Flag : Behavior("flag")
internal object ShadowBlock : Behavior("shadow_block")
internal data class Unknown(val unknownValue: String) : Behavior(unknownValue)

internal class BehaviorAdapter : JsonAdapter<Behavior>() {
@FromJson
override fun fromJson(reader: JsonReader): Behavior? {
val s = reader.nextString() ?: return null
return Behavior.fromString(s)
}

@ToJson
override fun toJson(writer: JsonWriter, value: Behavior?) {
writer.value(value?.value)
}
}
}
}
Loading
Loading