diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 5a1eb190a8..95de6fe9b7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -22,12 +22,14 @@ 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.LocalEcho import org.matrix.android.sdk.api.session.events.model.RelationType +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.toModel import org.matrix.android.sdk.api.session.room.model.PollSummaryContent 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.message.MessageContent +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.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent @@ -69,7 +71,9 @@ internal class EventRelationsAggregationProcessor @Inject constructor( // TODO Add ? // EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_KEY, - EventType.ENCRYPTED + EventType.ENCRYPTED, + EventType.POLL_RESPONSE, + EventType.POLL_END ) override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean { @@ -107,7 +111,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( Timber.v("###REPLACE in room $roomId for event ${event.eventId}") // A replace! handleReplace(realm, event, content, roomId, isLocalEcho) - } else if (content?.relatesTo?.type == RelationType.RESPONSE) { + } else if (content is MessagePollResponseContent) { Timber.v("###RESPONSE in room $roomId for event ${event.eventId}") handleResponse(realm, event, content, roomId, isLocalEcho) } @@ -139,7 +143,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( Timber.v("###REPLACE in room $roomId for event ${event.eventId}") // A replace! handleReplace(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId) - } else if (encryptedEventContent.relatesTo.type == RelationType.RESPONSE) { + } else if (it is MessagePollResponseContent) { Timber.v("###RESPONSE in room $roomId for event ${event.eventId}") handleResponse(realm, event, it, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId) } @@ -158,6 +162,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor( handleVerification(realm, event, roomId, isLocalEcho, it) } } + EventType.POLL_RESPONSE -> { + event.getClearContent().toModel(catchError = true)?.let { + handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId) + } + } + EventType.POLL_END -> { + event.content.toModel(catchError = true)?.let { + handleEndPoll(realm, event, it, roomId, isLocalEcho) + } + } } } else if (encryptedEventContent?.relatesTo?.type == RelationType.ANNOTATION) { // Reaction @@ -188,6 +202,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } } + EventType.POLL_RESPONSE -> { + event.content.toModel(catchError = true)?.let { + handleResponse(realm, event, it, roomId, isLocalEcho) + } + } + EventType.POLL_END -> { + event.content.toModel(catchError = true)?.let { + handleEndPoll(realm, event, it, roomId, isLocalEcho) + } + } else -> Timber.v("UnHandled event ${event.eventId}") } } catch (t: Throwable) { @@ -276,7 +300,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( private fun handleResponse(realm: Realm, event: Event, - content: MessageContent, + content: MessagePollResponseContent, roomId: String, isLocalEcho: Boolean, relatedEventId: String? = null) { @@ -321,11 +345,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( return } - val responseContent = event.content.toModel() ?: return Unit.also { - Timber.d("## POLL Receiving malformed response eventId:$eventId content: ${event.content}") - } - - val optionIndex = responseContent.relatesTo?.option ?: return Unit.also { + val option = content.response?.answers?.first() ?: return Unit.also { Timber.d("## POLL Ignoring malformed response no option eventId:$eventId content: ${event.content}") } @@ -336,20 +356,20 @@ internal class EventRelationsAggregationProcessor @Inject constructor( val existingVote = votes[existingVoteIndex] if (existingVote.voteTimestamp < eventTimestamp) { // Take the new one - votes[existingVoteIndex] = VoteInfo(senderId, optionIndex, eventTimestamp) + votes[existingVoteIndex] = VoteInfo(senderId, option, eventTimestamp) if (userId == senderId) { - sumModel.myVote = optionIndex + sumModel.myVote = option } - Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ") + Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ") } else { Timber.v("## POLL Ignoring vote (older than known one) eventId:$eventId ") } } else { - votes.add(VoteInfo(senderId, optionIndex, eventTimestamp)) + votes.add(VoteInfo(senderId, option, eventTimestamp)) if (userId == senderId) { - sumModel.myVote = optionIndex + sumModel.myVote = option } - Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ") + Timber.v("## POLL adding vote $option for user $senderId in poll :$targetEventId ") } sumModel.votes = votes if (isLocalEcho) { @@ -361,6 +381,43 @@ internal class EventRelationsAggregationProcessor @Inject constructor( existingPollSummary.aggregatedContent = ContentMapper.map(sumModel.toContent()) } + private fun handleEndPoll(realm: Realm, + event: Event, + content: MessageEndPollContent, + roomId: String, + isLocalEcho: Boolean) { + val pollEventId = content.eventId + + var existing = EventAnnotationsSummaryEntity.where(realm, roomId, pollEventId).findFirst() + if (existing == null) { + Timber.v("## POLL creating new relation summary for $pollEventId") + existing = EventAnnotationsSummaryEntity.create(realm, roomId, pollEventId) + } + + // we have it + val existingPollSummary = existing.pollResponseSummary + ?: realm.createObject(PollResponseAggregatedSummaryEntity::class.java).also { + existing.pollResponseSummary = it + } + + if (existingPollSummary.closedTime != null) { + Timber.v("## Received poll.end event for already ended poll $pollEventId") + return + } + + val txId = event.unsignedData?.transactionId + // is it a remote echo? + if (!isLocalEcho && existingPollSummary.sourceLocalEchoEvents.contains(txId)) { + // ok it has already been managed + Timber.v("## POLL Receiving remote echo of response eventId:$pollEventId") + existingPollSummary.sourceLocalEchoEvents.remove(txId) + existingPollSummary.sourceEvents.add(event.eventId) + return + } + + existingPollSummary.closedTime = event.originServerTs + } + private fun handleInitialAggregatedRelations(realm: Realm, event: Event, roomId: String,