Skip to content
Merged
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 @@ -143,6 +143,7 @@ import io.getstream.chat.android.network.models.MessageRequest
import io.getstream.chat.android.network.models.MuteChannelRequest
import io.getstream.chat.android.network.models.PollOptionInput
import io.getstream.chat.android.network.models.PollOptionRequest
import io.getstream.chat.android.network.models.PollVoteResponse
import io.getstream.chat.android.network.models.PushPreferenceInput
import io.getstream.chat.android.network.models.QueryDraftsRequest
import io.getstream.chat.android.network.models.QueryMembersPayload
Expand Down Expand Up @@ -1814,14 +1815,14 @@ constructor(
messageId,
pollId,
CastPollVoteRequest(vote),
).mapDomain { it.vote.toDomain() }
).flatMapDomain { toVoteCall(it) }

override fun removePollVote(messageId: String, pollId: String, voteId: String): Call<Vote> =
pollsApi.removePollVote(
messageId,
pollId,
voteId,
).mapDomain { it.vote.toDomain() }
).flatMapDomain { toVoteCall(it) }

override fun partialUpdatePoll(pollId: String, set: Map<String, Any>, unset: List<String>): Call<Poll> {
val request = UpdatePollPartialRequest(set = set, unset = unset)
Expand Down Expand Up @@ -2070,6 +2071,20 @@ constructor(
}
}

/**
* The vote is optional in the response schema, so a missing one is surfaced as a failure rather
* than crashing the mapping.
*/
private fun DomainMapping.toVoteCall(response: PollVoteResponse): Call<Vote> {
val vote = response.vote
return if (vote != null) {
CoroutineCall(coroutineScope) { Result.Success(vote.toDomain()) }
} else {
val error = Error.GenericError("The vote is missing from the response")
ErrorCall(coroutineScope, error)
}
}

private fun <T : Any, R : Any> RetrofitCall<T>.flatMapDomain(transform: DomainMapping.(T) -> Call<R>): Call<R> =
flatMap { domainMapping.transform(it) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
package io.getstream.chat.android.client.api2.endpoint

import io.getstream.chat.android.client.api.AuthenticatedApi
import io.getstream.chat.android.client.api2.model.response.PollResponse
import io.getstream.chat.android.client.api2.model.response.PollVoteResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollVotesResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollsResponse
import io.getstream.chat.android.client.call.RetrofitCall
import io.getstream.chat.android.network.models.CastPollVoteRequest
import io.getstream.chat.android.network.models.CreatePollOptionRequest
import io.getstream.chat.android.network.models.CreatePollRequest
import io.getstream.chat.android.network.models.PollOptionResponse
import io.getstream.chat.android.network.models.PollResponse
import io.getstream.chat.android.network.models.PollVoteResponse
import io.getstream.chat.android.network.models.PollVotesResponse
import io.getstream.chat.android.network.models.QueryPollVotesRequest
import io.getstream.chat.android.network.models.QueryPollsRequest
import io.getstream.chat.android.network.models.QueryPollsResponse
import io.getstream.chat.android.network.models.Response
import io.getstream.chat.android.network.models.UpdatePollOptionRequest
import io.getstream.chat.android.network.models.UpdatePollPartialRequest
Expand Down Expand Up @@ -165,7 +165,7 @@ internal interface PollsApi {
fun queryPollVotes(
@Path("poll_id") pollId: String,
@Body body: QueryPollVotesRequest,
): RetrofitCall<QueryPollVotesResponse>
): RetrofitCall<PollVotesResponse>

/**
* Queries polls based on the provided criteria.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ import io.getstream.chat.android.client.api2.model.dto.ReadReceiptsDto
import io.getstream.chat.android.client.api2.model.dto.SearchWarningDto
import io.getstream.chat.android.client.api2.model.dto.TypingIndicatorsDto
import io.getstream.chat.android.client.api2.model.response.MessageResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollVotesResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollsResponse
import io.getstream.chat.android.client.api2.model.response.QueryRemindersResponse
import io.getstream.chat.android.client.extensions.enrichWithCid
import io.getstream.chat.android.client.extensions.internal.sortedByLastReply
Expand Down Expand Up @@ -141,8 +139,12 @@ import io.getstream.chat.android.network.models.FullUserResponse
import io.getstream.chat.android.network.models.GetApplicationResponse
import io.getstream.chat.android.network.models.GetOGResponse
import io.getstream.chat.android.network.models.PollOptionResponseData
import io.getstream.chat.android.network.models.PollResponseData
import io.getstream.chat.android.network.models.PollVoteResponseData
import io.getstream.chat.android.network.models.PollVotesResponse
import io.getstream.chat.android.network.models.PrivacySettingsResponse
import io.getstream.chat.android.network.models.PushPreferencesResponse
import io.getstream.chat.android.network.models.QueryPollsResponse
import io.getstream.chat.android.network.models.UnreadCountsChannel
import io.getstream.chat.android.network.models.UnreadCountsChannelType
import io.getstream.chat.android.network.models.UnreadCountsThread
Expand Down Expand Up @@ -676,6 +678,101 @@ internal class DomainMapping(
)
}

/**
* Transforms [PollResponseData] into [Poll]
*/
internal fun PollResponseData.toDomain(): Poll {
val ownUserId = currentUserIdProvider() ?: ownVotes.firstOrNull()?.user?.id
val allVotes = latestVotesByOption.values
.flatten()
.filter { it.isAnswer != true }
.map { it.toDomain() }
val resolvedOwnVotes = (
ownVotes
.filter { it.isAnswer != true }
.map { it.toDomain() } +
allVotes.filter { it.user?.id == ownUserId }
)
.associateBy { it.id }
.values
.toList()

return Poll(
id = id,
name = name,
description = description,
options = options.map { it.toOption() },
votingVisibility = votingVisibility.toVotingVisibility(),
enforceUniqueVote = enforceUniqueVote,
maxVotesAllowed = maxVotesAllowed,
allowUserSuggestedOptions = allowUserSuggestedOptions,
allowAnswers = allowAnswers,
voteCount = voteCount,
voteCountsByOption = voteCountsByOption,
votes = allVotes,
ownVotes = resolvedOwnVotes,
createdAt = createdAt,
updatedAt = updatedAt,
closed = isClosed ?: false,
answersCount = answersCount,
answers = latestAnswers.map { it.toAnswerDomain() },
createdBy = createdBy?.toDomain(),
extraData = custom.mapNotNull { (key, value) -> value?.let { key to it } }.toMap(),
)
}

/**
* Transforms [PollVoteResponseData] into [Vote]
*/
internal fun PollVoteResponseData.toDomain(): Vote = Vote(
id = id,
pollId = pollId,
optionId = optionId,
createdAt = createdAt,
updatedAt = updatedAt,
user = user?.toDomain(),
)

/**
* Transforms [PollVoteResponseData] into [Answer]
*/
internal fun PollVoteResponseData.toAnswerDomain(): Answer = Answer(
id = id,
pollId = pollId,
text = answerText ?: "",
createdAt = createdAt,
updatedAt = updatedAt,
user = user?.toDomain(),
)

/**
* Transforms [PollVotesResponse] into [QueryPollVotesResult]
*/
internal fun PollVotesResponse.toDomain(): QueryPollVotesResult = QueryPollVotesResult(
votes = votes.map { it.toDomain() },
next = next,
)

/**
* Transforms [QueryPollsResponse] into [QueryPollsResult]
*/
internal fun QueryPollsResponse.toDomain(): QueryPollsResult = QueryPollsResult(
polls = polls.map { it.toDomain() },
next = next,
)

/**
* Transforms [PollOptionResponseData] to [Option].
*
* Not named `toDomain` because that maps to [PollOption], mirroring the hand-written
* [DownstreamPollOptionDto.toDomain] / [DownstreamPollOptionDto.toPollOption] pair.
*/
internal fun PollOptionResponseData.toOption(): Option = Option(
id = id,
text = text,
extraData = custom.mapNotNull { (key, value) -> value?.let { key to it } }.toMap(),
)

/**
* Transforms [PollOptionResponseData] into [PollOption]
*/
Expand Down Expand Up @@ -748,22 +845,6 @@ internal class DomainMapping(
else -> throw IllegalArgumentException("Unknown voting visibility: $this")
}

/**
* Transforms [QueryPollVotesResponse] to [QueryPollVotesResult].
*/
internal fun QueryPollVotesResponse.toDomain(): QueryPollVotesResult = QueryPollVotesResult(
votes = votes.map { it.toDomain() },
next = next,
)

/**
* Transforms the network [QueryPollsResponse] to a domain [QueryPollsResult].
*/
internal fun QueryPollsResponse.toDomain(): QueryPollsResult = QueryPollsResult(
polls = polls.map { it.toDomain() },
next = next,
)

/**
* Transform [DownstreamChannelUserRead] to [ChannelUserRead].
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import io.getstream.chat.android.client.parser2.adapters.NullCollectionsAsEmptyF
import io.getstream.chat.android.client.parser2.adapters.PollOptionInputAdapter
import io.getstream.chat.android.client.parser2.adapters.PollOptionRequestAdapter
import io.getstream.chat.android.client.parser2.adapters.PollOptionResponseDataAdapter
import io.getstream.chat.android.client.parser2.adapters.PollResponseDataAdapter
import io.getstream.chat.android.client.parser2.adapters.UpdatePollOptionRequestAdapter
import io.getstream.chat.android.client.parser2.adapters.UpdatePollRequestAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamMemberDataDtoAdapter
Expand Down Expand Up @@ -129,6 +130,7 @@ internal class MoshiChatParser(
.add(EventRequestAdapter)
.add(GetOGResponseAdapter)
.add(PollOptionResponseDataAdapter)
.add(PollResponseDataAdapter)
.add(
CreatePollRequest.VotingVisibility::class.java,
CreatePollRequest.VotingVisibility.VotingVisibilityAdapter(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.PollResponseData

/**
* Collects the root-level custom fields of a poll into `custom`, which is how the API sends
* them.
*/
internal object PollResponseDataAdapter :
CustomObjectDtoAdapter<PollResponseData>(
PollResponseData::class,
extraDataPropertyName = "custom",
) {

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

@ToJson
@Suppress("UNUSED_PARAMETER")
fun toJson(jsonWriter: JsonWriter, value: PollResponseData): Unit =
error("Can't convert this to Json")
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@
* limitations under the License.
*/

package io.getstream.chat.android.client.api2.model.response
@file:Suppress(
"ArrayInDataClass",
"EnumEntryName",
"RemoveRedundantQualifierName",
"UnusedImport",
)

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

import com.squareup.moshi.JsonClass
import io.getstream.chat.android.client.api2.model.dto.DownstreamPollDto
import com.squareup.moshi.Json

/**
* Response for creating a new poll.
*
* @property duration The duration of the request.
* @property poll The poll object.
*/
@JsonClass(generateAdapter = true)
@com.squareup.moshi.JsonClass(generateAdapter = true)
internal data class PollResponse(
val duration: String,
val poll: DownstreamPollDto,
@Json(name = "duration")
internal val duration: String,

@Json(name = "poll")
internal val poll: PollResponseData,
)
Loading
Loading