Code review fixes.
This commit is contained in:
parent
0f11e498a0
commit
04a7590804
@ -27,17 +27,17 @@ data class PollSummaryContent(
|
|||||||
var myVote: String? = null,
|
var myVote: String? = null,
|
||||||
// Array of VoteInfo, list is constructed so that there is only one vote by user
|
// Array of VoteInfo, list is constructed so that there is only one vote by user
|
||||||
// And that optionIndex is valid
|
// And that optionIndex is valid
|
||||||
var votes: List<VoteInfo>? = null
|
var votes: List<VoteInfo>? = null,
|
||||||
) {
|
var votesSummary: Map<String, VoteSummary>? = null,
|
||||||
|
var totalVotes: Int = 0,
|
||||||
|
var winnerVoteCount: Int = 0
|
||||||
|
)
|
||||||
|
|
||||||
fun voteCount(): Int {
|
@JsonClass(generateAdapter = true)
|
||||||
return votes?.size ?: 0
|
data class VoteSummary(
|
||||||
}
|
val total: Int = 0,
|
||||||
|
val percentage: Double = 0.0
|
||||||
fun voteCountForOption(option: String): Int {
|
)
|
||||||
return votes?.filter { it.option == option }?.count() ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class VoteInfo(
|
data class VoteInfo(
|
||||||
|
@ -23,6 +23,10 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
|
|||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MessagePollContent(
|
data class MessagePollContent(
|
||||||
|
/**
|
||||||
|
* Local message type, not from server
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
override val msgType: String = MessageType.MSGTYPE_POLL_START,
|
override val msgType: String = MessageType.MSGTYPE_POLL_START,
|
||||||
@Json(name = "body") override val body: String = "",
|
@Json(name = "body") override val body: String = "",
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
|
@ -23,6 +23,10 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
|
|||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class MessagePollResponseContent(
|
data class MessagePollResponseContent(
|
||||||
|
/**
|
||||||
|
* Local message type, not from server
|
||||||
|
*/
|
||||||
|
@Transient
|
||||||
override val msgType: String = MessageType.MSGTYPE_POLL_RESPONSE,
|
override val msgType: String = MessageType.MSGTYPE_POLL_RESPONSE,
|
||||||
@Json(name = "body") override val body: String = "",
|
@Json(name = "body") override val body: String = "",
|
||||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null,
|
||||||
|
@ -25,14 +25,18 @@ object MessageType {
|
|||||||
const val MSGTYPE_VIDEO = "m.video"
|
const val MSGTYPE_VIDEO = "m.video"
|
||||||
const val MSGTYPE_LOCATION = "m.location"
|
const val MSGTYPE_LOCATION = "m.location"
|
||||||
const val MSGTYPE_FILE = "m.file"
|
const val MSGTYPE_FILE = "m.file"
|
||||||
const val MSGTYPE_POLL_START = "org.matrix.msc3381.poll.start"
|
|
||||||
const val MSGTYPE_POLL_RESPONSE = "org.matrix.msc3381.poll.response"
|
|
||||||
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
const val MSGTYPE_VERIFICATION_REQUEST = "m.key.verification.request"
|
||||||
|
|
||||||
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
|
||||||
// Because sticker isn't a message type but a event type without msgtype field
|
// Because sticker isn't a message type but a event type without msgtype field
|
||||||
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
|
||||||
|
|
||||||
|
// Fake message types for poll events to be able to inherit them from MessageContent
|
||||||
|
// Because poll events are not message events and they don't hanve msgtype field
|
||||||
|
const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start"
|
||||||
|
const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response"
|
||||||
|
|
||||||
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
|
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
|
||||||
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
||||||
}
|
}
|
||||||
|
@ -91,10 +91,10 @@ interface SendService {
|
|||||||
/**
|
/**
|
||||||
* Method to send a poll response.
|
* Method to send a poll response.
|
||||||
* @param pollEventId the poll currently replied to
|
* @param pollEventId the poll currently replied to
|
||||||
* @param optionKey The option key
|
* @param answerId The id of the answer
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun registerVoteToPoll(pollEventId: String, optionKey: String): Cancelable
|
fun voteToPoll(pollEventId: String, answerId: String): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End a poll in the room.
|
* End a poll in the room.
|
||||||
|
@ -17,6 +17,8 @@ package org.matrix.android.sdk.internal.session.room
|
|||||||
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import org.matrix.android.sdk.api.crypto.VerificationState
|
import org.matrix.android.sdk.api.crypto.VerificationState
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
|
import org.matrix.android.sdk.api.session.events.model.AggregatedAnnotation
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
@ -26,13 +28,16 @@ import org.matrix.android.sdk.api.session.events.model.getRelationContent
|
|||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
|
import org.matrix.android.sdk.api.session.room.model.PollSummaryContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
import org.matrix.android.sdk.api.session.room.model.VoteInfo
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.VoteSummary
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.toState
|
import org.matrix.android.sdk.internal.crypto.verification.toState
|
||||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||||
@ -52,11 +57,13 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate
|
|||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||||
|
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class EventRelationsAggregationProcessor @Inject constructor(
|
internal class EventRelationsAggregationProcessor @Inject constructor(
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String,
|
||||||
|
private val stateEventDataSource: StateEventDataSource
|
||||||
) : EventInsertLiveProcessor {
|
) : EventInsertLiveProcessor {
|
||||||
|
|
||||||
private val allowedTypes = listOf(
|
private val allowedTypes = listOf(
|
||||||
@ -111,9 +118,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
||||||
// A replace!
|
// A replace!
|
||||||
handleReplace(realm, event, content, roomId, isLocalEcho)
|
handleReplace(realm, event, content, roomId, isLocalEcho)
|
||||||
} else if (content is MessagePollResponseContent) {
|
|
||||||
Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
|
|
||||||
handleResponse(realm, event, content, roomId, isLocalEcho)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,9 +147,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
Timber.v("###REPLACE in room $roomId for event ${event.eventId}")
|
||||||
// A replace!
|
// A replace!
|
||||||
handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
||||||
} else if (it is MessagePollResponseContent) {
|
} else if (event.getClearType() == EventType.POLL_RESPONSE) {
|
||||||
Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
|
event.getClearContent().toModel<MessagePollResponseContent>(catchError = true)?.let { pollResponseContent ->
|
||||||
handleResponse(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
Timber.v("###RESPONSE in room $roomId for event ${event.eventId}")
|
||||||
|
handleResponse(realm, event, pollResponseContent, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (encryptedEventContent?.relatesTo?.type == RelationType.REFERENCE) {
|
} else if (encryptedEventContent?.relatesTo?.type == RelationType.REFERENCE) {
|
||||||
@ -372,6 +378,20 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
|
Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ")
|
||||||
}
|
}
|
||||||
sumModel.votes = votes
|
sumModel.votes = votes
|
||||||
|
|
||||||
|
// Precompute the percentage of votes for all options
|
||||||
|
val totalVotes = votes.size
|
||||||
|
sumModel.totalVotes = totalVotes
|
||||||
|
sumModel.votesSummary = votes
|
||||||
|
.groupBy({ it.option }, { it.userId })
|
||||||
|
.mapValues {
|
||||||
|
VoteSummary(
|
||||||
|
total = it.value.size,
|
||||||
|
percentage = if (totalVotes == 0 && it.value.isEmpty()) 0.0 else it.value.size.toDouble() / totalVotes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sumModel.winnerVoteCount = sumModel.votesSummary?.maxOf { it.value.total } ?: 0
|
||||||
|
|
||||||
if (isLocalEcho) {
|
if (isLocalEcho) {
|
||||||
existingPollSummary.sourceLocalEchoEvents.add(eventId)
|
existingPollSummary.sourceLocalEchoEvents.add(eventId)
|
||||||
} else {
|
} else {
|
||||||
@ -405,6 +425,14 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
|
||||||
|
?.content?.toModel<PowerLevelsContent>()
|
||||||
|
?.let { PowerLevelsHelper(it) }
|
||||||
|
if (!powerLevelsHelper?.isUserAbleToRedact(event.senderId ?: "").orFalse()) {
|
||||||
|
Timber.v("## Received poll.end event $pollEventId but user ${event.senderId} doesn't have enough power level in room $roomId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val txId = event.unsignedData?.transactionId
|
val txId = event.unsignedData?.transactionId
|
||||||
// is it a remote echo?
|
// is it a remote echo?
|
||||||
if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) {
|
if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) {
|
||||||
|
@ -103,8 +103,8 @@ internal class DefaultSendService @AssistedInject constructor(
|
|||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerVoteToPoll(pollEventId: String, optionKey: String): Cancelable {
|
override fun voteToPoll(pollEventId: String, answerId: String): Cancelable {
|
||||||
return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, optionKey)
|
return localEchoEventFactory.createPollReplyEvent(roomId, pollEventId, answerId)
|
||||||
.also { createLocalEcho(it) }
|
.also { createLocalEcho(it) }
|
||||||
.let { sendEvent(it) }
|
.let { sendEvent(it) }
|
||||||
}
|
}
|
||||||
|
@ -126,14 +126,14 @@ internal class LocalEchoEventFactory @Inject constructor(
|
|||||||
|
|
||||||
fun createPollReplyEvent(roomId: String,
|
fun createPollReplyEvent(roomId: String,
|
||||||
pollEventId: String,
|
pollEventId: String,
|
||||||
optionLabel: String): Event {
|
answerId: String): Event {
|
||||||
val content = MessagePollResponseContent(
|
val content = MessagePollResponseContent(
|
||||||
body = optionLabel,
|
body = answerId,
|
||||||
relatesTo = RelationDefaultContent(
|
relatesTo = RelationDefaultContent(
|
||||||
type = RelationType.REFERENCE,
|
type = RelationType.REFERENCE,
|
||||||
eventId = pollEventId),
|
eventId = pollEventId),
|
||||||
response = PollResponse(
|
response = PollResponse(
|
||||||
answers = listOf(optionLabel)
|
answers = listOf(answerId)
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -38,7 +38,6 @@ import androidx.core.view.isVisible
|
|||||||
import com.amulyakhare.textdrawable.TextDrawable
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.exhaustive
|
|
||||||
import im.vector.app.core.extensions.getMeasurements
|
import im.vector.app.core.extensions.getMeasurements
|
||||||
import im.vector.app.core.utils.PERMISSIONS_EMPTY
|
import im.vector.app.core.utils.PERMISSIONS_EMPTY
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT
|
import im.vector.app.core.utils.PERMISSIONS_FOR_PICKING_CONTACT
|
||||||
@ -132,7 +131,7 @@ class AttachmentTypeSelectorView(context: Context,
|
|||||||
Type.AUDIO -> views.attachmentAudioButtonContainer
|
Type.AUDIO -> views.attachmentAudioButtonContainer
|
||||||
Type.CONTACT -> views.attachmentContactButtonContainer
|
Type.CONTACT -> views.attachmentContactButtonContainer
|
||||||
Type.POLL -> views.attachmentPollButtonContainer
|
Type.POLL -> views.attachmentPollButtonContainer
|
||||||
}.exhaustive.let {
|
}.let {
|
||||||
it.isVisible = isVisible
|
it.isVisible = isVisible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||||||
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
data class RemoveFailedEcho(val eventId: String) : RoomDetailAction()
|
||||||
data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction()
|
data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction()
|
||||||
|
|
||||||
data class RegisterVoteToPoll(val eventId: String, val optionKey: String) : RoomDetailAction()
|
data class VoteToPoll(val eventId: String, val optionKey: String) : RoomDetailAction()
|
||||||
|
|
||||||
data class ReportContent(
|
data class ReportContent(
|
||||||
val eventId: String,
|
val eventId: String,
|
||||||
|
@ -289,7 +289,7 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
|
||||||
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
|
||||||
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
|
||||||
is RoomDetailAction.RegisterVoteToPoll -> handleRegisterVoteToPoll(action)
|
is RoomDetailAction.VoteToPoll -> handleVoteToPoll(action)
|
||||||
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
|
||||||
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
|
||||||
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
|
||||||
@ -908,10 +908,10 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRegisterVoteToPoll(action: RoomDetailAction.RegisterVoteToPoll) {
|
private fun handleVoteToPoll(action: RoomDetailAction.VoteToPoll) {
|
||||||
// Do not allow to reply to unsent local echo
|
// Do not allow to vote unsent local echo of the poll event
|
||||||
if (LocalEcho.isLocalEchoId(action.eventId)) return
|
if (LocalEcho.isLocalEchoId(action.eventId)) return
|
||||||
room.registerVoteToPoll(action.eventId, action.optionKey)
|
room.voteToPoll(action.eventId, action.optionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEndPoll(eventId: String) {
|
private fun handleEndPoll(eventId: String) {
|
||||||
|
@ -23,6 +23,7 @@ import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFact
|
|||||||
import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
|
import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
|
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
|
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
|
||||||
@ -108,9 +109,14 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
|
|||||||
PollResponseData(
|
PollResponseData(
|
||||||
myVote = it.aggregatedContent?.myVote,
|
myVote = it.aggregatedContent?.myVote,
|
||||||
isClosed = it.closedTime != null,
|
isClosed = it.closedTime != null,
|
||||||
votes = it.aggregatedContent?.votes
|
votes = it.aggregatedContent?.votesSummary?.mapValues { votesSummary ->
|
||||||
?.groupBy({ it.option }, { it.userId })
|
PollVoteSummaryData(
|
||||||
?.mapValues { it.value.size }
|
total = votesSummary.value.total,
|
||||||
|
percentage = votesSummary.value.percentage
|
||||||
|
)
|
||||||
|
},
|
||||||
|
winnerVoteCount = it.aggregatedContent?.winnerVoteCount ?: 0,
|
||||||
|
totalVotes = it.aggregatedContent?.totalVotes ?: 0
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
hasBeenEdited = event.hasBeenEdited(),
|
hasBeenEdited = event.hasBeenEdited(),
|
||||||
|
@ -72,10 +72,18 @@ data class ReadReceiptData(
|
|||||||
@Parcelize
|
@Parcelize
|
||||||
data class PollResponseData(
|
data class PollResponseData(
|
||||||
val myVote: String?,
|
val myVote: String?,
|
||||||
val votes: Map<String, Int>?,
|
val votes: Map<String, PollVoteSummaryData>?,
|
||||||
|
val totalVotes: Int = 0,
|
||||||
|
val winnerVoteCount: Int = 0,
|
||||||
val isClosed: Boolean = false
|
val isClosed: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class PollVoteSummaryData(
|
||||||
|
val total: Int = 0,
|
||||||
|
val percentage: Double = 0.0
|
||||||
|
) : Parcelable
|
||||||
|
|
||||||
enum class E2EDecoration {
|
enum class E2EDecoration {
|
||||||
NONE,
|
NONE,
|
||||||
WARN_IN_CLEAR,
|
WARN_IN_CLEAR,
|
||||||
|
@ -54,13 +54,14 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||||||
val isEnded = pollResponseSummary?.isClosed.orFalse()
|
val isEnded = pollResponseSummary?.isClosed.orFalse()
|
||||||
val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse()
|
val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse()
|
||||||
val showVotes = didUserVoted || isEnded
|
val showVotes = didUserVoted || isEnded
|
||||||
val totalVotes = pollResponseSummary?.votes?.map { it.value }?.sum() ?: 0
|
val totalVotes = pollResponseSummary?.totalVotes ?: 0
|
||||||
val winnerVoteCount = pollResponseSummary?.votes?.map { it.value }?.maxOrNull() ?: -1
|
val winnerVoteCount = pollResponseSummary?.winnerVoteCount
|
||||||
|
|
||||||
pollContent?.pollCreationInfo?.answers?.forEach { option ->
|
pollContent?.pollCreationInfo?.answers?.forEach { option ->
|
||||||
val isMyVote = pollResponseSummary?.myVote?.let { option.id == it }.orFalse()
|
val voteSummary = pollResponseSummary?.votes?.get(option.id)
|
||||||
val voteCount = pollResponseSummary?.votes?.get(option.id) ?: 0
|
val isMyVote = pollResponseSummary?.myVote == option.id
|
||||||
val votePercentage = if (voteCount == 0 && totalVotes == 0) 0.0 else voteCount.toDouble() / totalVotes
|
val voteCount = voteSummary?.total ?: 0
|
||||||
|
val votePercentage = voteSummary?.percentage ?: 0.0
|
||||||
|
|
||||||
holder.optionsContainer.addView(
|
holder.optionsContainer.addView(
|
||||||
PollOptionItem(holder.view.context).apply {
|
PollOptionItem(holder.view.context).apply {
|
||||||
@ -73,7 +74,7 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||||||
votePercentage = votePercentage,
|
votePercentage = votePercentage,
|
||||||
callback = object : PollOptionItem.Callback {
|
callback = object : PollOptionItem.Callback {
|
||||||
override fun onOptionClicked() {
|
override fun onOptionClicked() {
|
||||||
callback?.onTimelineItemAction(RoomDetailAction.RegisterVoteToPoll(relatedEventId, option.id ?: ""))
|
callback?.onTimelineItemAction(RoomDetailAction.VoteToPoll(relatedEventId, option.id ?: ""))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
android:id="@+id/optionBorderImageView"
|
android:id="@+id/optionBorderImageView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/bg_poll_option"
|
android:src="@drawable/bg_poll_option"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/optionCheckImageView"
|
android:id="@+id/optionCheckImageView"
|
||||||
@ -23,10 +23,10 @@
|
|||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/poll_option_unchecked"
|
android:src="@drawable/poll_option_unchecked"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/optionNameTextView"
|
android:id="@+id/optionNameTextView"
|
||||||
|
@ -3654,26 +3654,26 @@
|
|||||||
<item quantity="other">At least %1$s options are required</item>
|
<item quantity="other">At least %1$s options are required</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="poll_option_vote_count">
|
<plurals name="poll_option_vote_count">
|
||||||
<item quantity="one">%1$s vote</item>
|
<item quantity="one">%1$d vote</item>
|
||||||
<item quantity="other">%1$s votes</item>
|
<item quantity="other">%1$d votes</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="poll_total_vote_count_before_ended_and_voted">
|
<plurals name="poll_total_vote_count_before_ended_and_voted">
|
||||||
<item quantity="one">Based on %1$s vote</item>
|
<item quantity="one">Based on %1$d vote</item>
|
||||||
<item quantity="other">Based on %1$s votes</item>
|
<item quantity="other">Based on %1$d votes</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
|
<plurals name="poll_total_vote_count_before_ended_and_not_voted">
|
||||||
<item quantity="zero">No votes cast</item>
|
<item quantity="zero">No votes cast</item>
|
||||||
<item quantity="one">%1$s vote cast. Vote to the see the results</item>
|
<item quantity="one">%1$d vote cast. Vote to the see the results</item>
|
||||||
<item quantity="other">%1$s votes cast. Vote to the see the results</item>
|
<item quantity="other">%1$d votes cast. Vote to the see the results</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="poll_total_vote_count_after_ended">
|
<plurals name="poll_total_vote_count_after_ended">
|
||||||
<item quantity="one">Final result based on %1$s vote</item>
|
<item quantity="one">Final result based on %1$d vote</item>
|
||||||
<item quantity="other">Final result based on %1$s votes</item>
|
<item quantity="other">Final result based on %1$d votes</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="poll_end_action">End poll</string>
|
<string name="poll_end_action">End poll</string>
|
||||||
<string name="a11y_poll_winner_option">winner option</string>
|
<string name="a11y_poll_winner_option">winner option</string>
|
||||||
<string name="end_poll_confirmation_title">End poll</string>
|
<string name="end_poll_confirmation_title">End this poll?</string>
|
||||||
<string name="end_poll_confirmation_description">Are you sure you want to end this poll? This will stop people from being able to vote and will display the final results of the poll.</string>
|
<string name="end_poll_confirmation_description">This will stop people from being able to vote and will display the final results of the poll.</string>
|
||||||
<string name="end_poll_confirmation_approve_button">End poll</string>
|
<string name="end_poll_confirmation_approve_button">End poll</string>
|
||||||
<string name="labs_enable_polls">Enable Polls</string>
|
<string name="labs_enable_polls">Enable Polls</string>
|
||||||
<string name="poll_response_room_list_preview">Vote casted</string>
|
<string name="poll_response_room_list_preview">Vote casted</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user