diff --git a/changelog.d/5340.bugfix b/changelog.d/5340.bugfix new file mode 100644 index 0000000000..17fd9716c0 --- /dev/null +++ b/changelog.d/5340.bugfix @@ -0,0 +1 @@ +Support both stable and unstable prefixes \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index df57ca5681..c0ca40bc73 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -349,7 +349,7 @@ fun Event.isAttachmentMessage(): Boolean { } } -fun Event.isPoll(): Boolean = getClearType() == EventType.POLL_START || getClearType() == EventType.POLL_END +fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClearType() in EventType.POLL_END fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER @@ -372,7 +372,7 @@ fun Event.getRelationContent(): RelationDefaultContent? { * Returns the poll question or null otherwise */ fun Event.getPollQuestion(): String? = - getPollContent()?.pollCreationInfo?.question?.question + getPollContent()?.getBestPollCreationInfo()?.question?.getBestQuestion() /** * Returns the relation content for a specific type or null otherwise diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 0c77b574e7..22fb9bcbe2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -103,9 +103,9 @@ object EventType { const val REACTION = "m.reaction" // Poll - const val POLL_START = "org.matrix.msc3381.poll.start" - const val POLL_RESPONSE = "org.matrix.msc3381.poll.response" - const val POLL_END = "org.matrix.msc3381.poll.end" + val POLL_START = listOf("org.matrix.msc3381.poll.start", "m.poll.start") + val POLL_RESPONSE = listOf("org.matrix.msc3381.poll.response", "m.poll.response") + val POLL_END = listOf("org.matrix.msc3381.poll.end", "m.poll.end") // Unwedging internal const val DUMMY = "m.dummy" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index d07bd2d73a..84bf5cf7b7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -39,37 +39,46 @@ data class MessageLocationContent( */ @Json(name = "geo_uri") val geoUri: String, + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "m.new_content") override val newContent: Content? = null, /** * See https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md */ - @Json(name = "org.matrix.msc3488.location") val locationInfo: LocationInfo? = null, - - @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, - @Json(name = "m.new_content") override val newContent: Content? = null, - + @Json(name = "org.matrix.msc3488.location") val unstableLocationInfo: LocationInfo? = null, + @Json(name = "m.location") val locationInfo: LocationInfo? = null, + /** + * Exact time that the data in the event refers to (milliseconds since the UNIX epoch) + */ + @Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null, + @Json(name = "m.ts") val ts: Long? = null, + @Json(name = "org.matrix.msc1767.text") val unstableText: String? = null, + @Json(name = "m.text") val text: String? = null, /** * m.asset defines a generic asset that can be used for location tracking but also in other places like * inventories, geofencing, checkins/checkouts etc. * It should contain a mandatory namespaced type key defining what particular asset is being referred to. * For the purposes of user location tracking m.self should be used in order to avoid duplicating the mxid. */ - @Json(name = "m.asset") val locationAsset: LocationAsset? = null, - - /** - * Exact time that the data in the event refers to (milliseconds since the UNIX epoch) - */ - @Json(name = "org.matrix.msc3488.ts") val ts: Long? = null, - - @Json(name = "org.matrix.msc1767.text") val text: String? = null + @Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset? = null, + @Json(name = "m.asset") val locationAsset: LocationAsset? = null ) : MessageContent { - fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri + fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo + + fun getBestTs() = ts ?: unstableTs + + fun getBestText() = text ?: unstableText + + fun getBestLocationAsset() = locationAsset ?: unstableLocationAsset + + fun getBestGeoUri() = getBestLocationInfo()?.geoUri ?: geoUri /** * @return true if the location asset is a user location, not a generic one. */ fun isSelfLocation(): Boolean { // Should behave like m.self if locationAsset is null + val locationAsset = getBestLocationAsset() return locationAsset?.type == null || locationAsset.type == LocationAssetType.SELF } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt index a4e1317290..43c0c90068 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollContent.kt @@ -31,5 +31,9 @@ data class MessagePollContent( @Json(name = "body") override val body: String = "", @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.new_content") override val newContent: Content? = null, - @Json(name = "org.matrix.msc3381.poll.start") val pollCreationInfo: PollCreationInfo? = null -) : MessageContent + @Json(name = "org.matrix.msc3381.poll.start") val unstablePollCreationInfo: PollCreationInfo? = null, + @Json(name = "m.poll.start") val pollCreationInfo: PollCreationInfo? = null +) : MessageContent { + + fun getBestPollCreationInfo() = pollCreationInfo ?: unstablePollCreationInfo +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt index f3b4e3dc23..022915ed69 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt @@ -31,5 +31,9 @@ data class MessagePollResponseContent( @Json(name = "body") override val body: String = "", @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, @Json(name = "m.new_content") override val newContent: Content? = null, - @Json(name = "org.matrix.msc3381.poll.response") val response: PollResponse? = null -) : MessageContent + @Json(name = "org.matrix.msc3381.poll.response") val unstableResponse: PollResponse? = null, + @Json(name = "m.response") val response: PollResponse? = null +) : MessageContent { + + fun getBestResponse() = response ?: unstableResponse +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollAnswer.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollAnswer.kt index 8f5ff53c85..34614d9d15 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollAnswer.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollAnswer.kt @@ -22,5 +22,9 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class PollAnswer( @Json(name = "id") val id: String? = null, - @Json(name = "org.matrix.msc1767.text") val answer: String? = null -) + @Json(name = "org.matrix.msc1767.text") val unstableAnswer: String? = null, + @Json(name = "m.text") val answer: String? = null +) { + + fun getBestAnswer() = answer ?: unstableAnswer +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt index a82c01b159..81b034a809 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt @@ -21,8 +21,8 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class PollCreationInfo( - @Json(name = "question") val question: PollQuestion? = null, - @Json(name = "kind") val kind: PollType? = PollType.DISCLOSED, - @Json(name = "max_selections") val maxSelections: Int = 1, - @Json(name = "answers") val answers: List? = null + @Json(name = "question") val question: PollQuestion? = null, + @Json(name = "kind") val kind: PollType? = PollType.DISCLOSED_UNSTABLE, + @Json(name = "max_selections") val maxSelections: Int = 1, + @Json(name = "answers") val answers: List? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollQuestion.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollQuestion.kt index 76025f745e..df9517892b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollQuestion.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollQuestion.kt @@ -21,5 +21,9 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class PollQuestion( - @Json(name = "org.matrix.msc1767.text") val question: String? = null -) + @Json(name = "org.matrix.msc1767.text") val unstableQuestion: String? = null, + @Json(name = "m.text") val question: String? = null +) { + + fun getBestQuestion() = question ?: unstableQuestion +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollType.kt index 3a8066b9bc..54801e698d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollType.kt @@ -25,11 +25,17 @@ enum class PollType { * Voters should see results as soon as they have voted. */ @Json(name = "org.matrix.msc3381.poll.disclosed") + DISCLOSED_UNSTABLE, + + @Json(name = "m.poll.disclosed") DISCLOSED, /** * Results should be only revealed when the poll is ended. */ @Json(name = "org.matrix.msc3381.poll.undisclosed") + UNDISCLOSED_UNSTABLE, + + @Json(name = "m.poll.undisclosed") UNDISCLOSED } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt index 3bba2deae5..eaed9053ea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/summary/RoomSummaryConstants.kt @@ -32,7 +32,6 @@ object RoomSummaryConstants { EventType.CALL_ANSWER, EventType.ENCRYPTED, EventType.STICKER, - EventType.REACTION, - EventType.POLL_START - ) + EventType.REACTION + ) + EventType.POLL_START } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt index 6f8bae876b..f9398ac7b8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt @@ -134,9 +134,9 @@ fun TimelineEvent.getEditedEventId(): String? { */ fun TimelineEvent.getLastMessageContent(): MessageContent? { return when (root.getClearType()) { - EventType.STICKER -> root.getClearContent().toModel() - EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() - else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() + EventType.STICKER -> root.getClearContent().toModel() + in EventType.POLL_START -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() + else -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel() } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index 8b05d2ea62..8ae203c2b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -56,7 +56,7 @@ internal class DefaultProcessEventForPushTask @Inject constructor( val allEvents = (newJoinEvents + inviteEvents).filter { event -> when (event.type) { - EventType.POLL_START, + in EventType.POLL_START, EventType.MESSAGE, EventType.REDACTION, EventType.ENCRYPTED, 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 1e0eb8b497..8159da844f 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 @@ -86,11 +86,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor( // TODO Add ? // EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_KEY, - EventType.ENCRYPTED, - EventType.POLL_START, - EventType.POLL_RESPONSE, - EventType.POLL_END - ) + EventType.ENCRYPTED + ) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean { return allowedTypes.contains(eventType) @@ -156,7 +153,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 (event.getClearType() == EventType.POLL_RESPONSE) { + } else if (event.getClearType() in EventType.POLL_RESPONSE) { event.getClearContent().toModel(catchError = true)?.let { pollResponseContent -> Timber.v("###RESPONSE in room $roomId for event ${event.eventId}") handleResponse(realm, event, pollResponseContent, roomId, isLocalEcho, encryptedEventContent.relatesTo.eventId) @@ -177,12 +174,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor( handleVerification(realm, event, roomId, isLocalEcho, it) } } - EventType.POLL_RESPONSE -> { + in EventType.POLL_RESPONSE -> { event.getClearContent().toModel(catchError = true)?.let { handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId) } } - EventType.POLL_END -> { + in EventType.POLL_END -> { event.content.toModel(catchError = true)?.let { handleEndPoll(realm, event, it, roomId, isLocalEcho) } @@ -217,7 +214,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } } - EventType.POLL_START -> { + in EventType.POLL_START -> { val content: MessagePollContent? = event.content.toModel() if (content?.relatesTo?.type == RelationType.REPLACE) { Timber.v("###REPLACE in room $roomId for event ${event.eventId}") @@ -225,12 +222,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor( handleReplace(realm, event, content, roomId, isLocalEcho) } } - EventType.POLL_RESPONSE -> { + in EventType.POLL_RESPONSE -> { event.content.toModel(catchError = true)?.let { handleResponse(realm, event, it, roomId, isLocalEcho) } } - EventType.POLL_END -> { + in EventType.POLL_END -> { event.content.toModel(catchError = true)?.let { handleEndPoll(realm, event, it, roomId, isLocalEcho) } @@ -407,12 +404,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor( return } - val option = content.response?.answers?.first() ?: return Unit.also { + val option = content.getBestResponse()?.answers?.first() ?: return Unit.also { Timber.d("## POLL Ignoring malformed response no option eventId:$eventId content: ${event.content}") } // Check if this option is in available options - if (!targetPollContent.pollCreationInfo?.answers?.map { it.id }?.contains(option).orFalse()) { + if (!targetPollContent.getBestPollCreationInfo()?.answers?.map { it.id }?.contains(option).orFalse()) { Timber.v("## POLL $targetEventId doesn't contain option $option") return } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt index ee52fe574b..4753e12157 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt @@ -71,7 +71,7 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr when (typeToPrune) { EventType.ENCRYPTED, EventType.MESSAGE, - EventType.POLL_START -> { + in EventType.POLL_START -> { Timber.d("REDACTION for message ${eventToPrune.eventId}") val unsignedData = EventMapper.map(eventToPrune).unsignedData ?: UnsignedData(null, null) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 3c36d58710..0c014134a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -137,16 +137,11 @@ internal class LocalEchoEventFactory @Inject constructor( options: List, pollType: PollType): MessagePollContent { return MessagePollContent( - pollCreationInfo = PollCreationInfo( - question = PollQuestion( - question = question - ), + unstablePollCreationInfo = PollCreationInfo( + question = PollQuestion(unstableQuestion = question), kind = pollType, answers = options.map { option -> - PollAnswer( - id = UUID.randomUUID().toString(), - answer = option - ) + PollAnswer(id = UUID.randomUUID().toString(), unstableAnswer = option) } ) ) @@ -167,7 +162,7 @@ internal class LocalEchoEventFactory @Inject constructor( originServerTs = dummyOriginServerTs(), senderId = userId, eventId = localId, - type = EventType.POLL_START, + type = EventType.POLL_START.first(), content = newContent.toContent() ) } @@ -179,11 +174,9 @@ internal class LocalEchoEventFactory @Inject constructor( body = answerId, relatesTo = RelationDefaultContent( type = RelationType.REFERENCE, - eventId = pollEventId), - response = PollResponse( - answers = listOf(answerId) - ) - + eventId = pollEventId + ), + unstableResponse = PollResponse(answers = listOf(answerId)) ) val localId = LocalEcho.createLocalEchoId() return Event( @@ -191,7 +184,7 @@ internal class LocalEchoEventFactory @Inject constructor( originServerTs = dummyOriginServerTs(), senderId = userId, eventId = localId, - type = EventType.POLL_RESPONSE, + type = EventType.POLL_RESPONSE.first(), content = content.toContent(), unsignedData = UnsignedData(age = null, transactionId = localId)) } @@ -207,7 +200,7 @@ internal class LocalEchoEventFactory @Inject constructor( originServerTs = dummyOriginServerTs(), senderId = userId, eventId = localId, - type = EventType.POLL_START, + type = EventType.POLL_START.first(), content = content.toContent(), unsignedData = UnsignedData(age = null, transactionId = localId)) } @@ -226,7 +219,7 @@ internal class LocalEchoEventFactory @Inject constructor( originServerTs = dummyOriginServerTs(), senderId = userId, eventId = localId, - type = EventType.POLL_END, + type = EventType.POLL_END.first(), content = content.toContent(), unsignedData = UnsignedData(age = null, transactionId = localId)) } @@ -239,15 +232,10 @@ internal class LocalEchoEventFactory @Inject constructor( val content = MessageLocationContent( geoUri = geoUri, body = geoUri, - locationInfo = LocationInfo( - geoUri = geoUri, - description = geoUri - ), - locationAsset = LocationAsset( - type = LocationAssetType.SELF - ), - ts = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), - text = geoUri + unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri), + unstableLocationAsset = LocationAsset(type = LocationAssetType.SELF), + unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + unstableText = geoUri ) return createMessageEvent(roomId, content) } @@ -638,7 +626,7 @@ internal class LocalEchoEventFactory @Inject constructor( MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.") MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.") MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.") - MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.pollCreationInfo?.question?.question ?: "") + MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "") else -> return TextContent(content?.body ?: "") } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt index b1df29ebc6..28c1587b1a 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent fun TimelineEvent.canReact(): Boolean { // Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment - return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START) && + return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START && root.sendState == SendState.SYNCED && !root.isRedacted() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 662af3d546..c638666cd7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1189,7 +1189,7 @@ class TimelineFragment @Inject constructor( val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) getString(R.string.voice_message_reply_content, formattedDuration) } else if (messageContent is MessagePollContent) { - messageContent.pollCreationInfo?.question?.question + messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() } else { messageContent?.body ?: "" } @@ -2165,7 +2165,7 @@ class TimelineFragment @Inject constructor( timelineViewModel.handle(RoomDetailAction.UpdateQuickReactAction(action.eventId, action.clickedOn, action.add)) } is EventSharedAction.Edit -> { - if (action.eventType == EventType.POLL_START) { + if (action.eventType in EventType.POLL_START) { navigator.openCreatePoll(requireContext(), timelineArgs.roomId, action.eventId, PollMode.EDIT) } else if (withState(messageComposerViewModel) { it.isVoiceMessageIdle }) { messageComposerViewModel.handle(MessageComposerAction.EnterEditMode(action.eventId, views.composerLayout.text.toString())) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 5575d9b7f6..81a8150c95 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -181,7 +181,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } else { when (timelineEvent.root.getClearType()) { EventType.MESSAGE, - EventType.STICKER -> { + EventType.STICKER -> { val messageContent: MessageContent? = timelineEvent.getLastMessageContent() if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { val html = messageContent.formattedBody @@ -207,13 +207,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted EventType.CALL_INVITE, EventType.CALL_CANDIDATES, EventType.CALL_HANGUP, - EventType.CALL_ANSWER -> { + EventType.CALL_ANSWER -> { noticeEventFormatter.format(timelineEvent, room?.roomSummary()?.isDirect.orFalse()) } - EventType.POLL_START -> { - timelineEvent.root.getClearContent().toModel(catchError = true)?.pollCreationInfo?.question?.question ?: "" + in EventType.POLL_START -> { + timelineEvent.root.getClearContent().toModel(catchError = true)?.getBestPollCreationInfo()?.question?.getBestQuestion() + ?: "" } - else -> null + else -> null } } } catch (failure: Throwable) { @@ -373,7 +374,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } if (canRedact(timelineEvent, actionPermissions)) { - if (timelineEvent.root.getClearType() == EventType.POLL_START) { + if (timelineEvent.root.getClearType() in EventType.POLL_START) { add(EventSharedAction.Redact( eventId, askForReason = informationData.senderId != session.myUserId, @@ -425,7 +426,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun canReply(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean { // Only EventType.MESSAGE and EventType.POLL_START event types are supported for the moment - if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.POLL_START)) return false + if (event.root.getClearType() !in EventType.POLL_START + EventType.MESSAGE) return false if (!actionPermissions.canSendMessage) return false return when (messageContent?.msgType) { MessageType.MSGTYPE_TEXT, @@ -511,7 +512,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun canRedact(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean { // Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment - if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START)) return false + if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START) return false // Message sent by the current user can always be redacted if (event.root.senderId == session.myUserId) return true // Check permission for messages sent by other users @@ -526,13 +527,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted private fun canViewReactions(event: TimelineEvent): Boolean { // Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment - if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.POLL_START)) return false + if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START) return false return event.annotations?.reactionsSummary?.isNotEmpty() ?: false } private fun canEdit(event: TimelineEvent, myUserId: String, actionPermissions: ActionPermissions): Boolean { // Only event of type EventType.MESSAGE and EventType.POLL_START are supported for the moment - if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.POLL_START)) return false + if (event.root.getClearType() !in listOf(EventType.MESSAGE) + EventType.POLL_START) return false if (!actionPermissions.canSendMessage) return false // TODO if user is admin or moderator val messageContent = event.root.getClearContent().toModel() @@ -578,13 +579,13 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } private fun canEndPoll(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean { - return event.root.getClearType() == EventType.POLL_START && + return event.root.getClearType() in EventType.POLL_START && canRedact(event, actionPermissions) && event.annotations?.pollResponseSummary?.closedTime == null } private fun canEditPoll(event: TimelineEvent): Boolean { - return event.root.getClearType() == EventType.POLL_START && + return event.root.getClearType() in EventType.POLL_START && event.annotations?.pollResponseSummary?.closedTime == null && event.annotations?.pollResponseSummary?.aggregatedContent?.totalVotes ?: 0 == 0 } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index aa1758dd6c..5e1e9b64d2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -247,7 +247,7 @@ class MessageItemFactory @Inject constructor( val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse() val winnerVoteCount = pollResponseSummary?.winnerVoteCount val isPollSent = informationData.sendState.isSent() - val isPollUndisclosed = pollContent.pollCreationInfo?.kind == PollType.UNDISCLOSED + val isPollUndisclosed = pollContent.getBestPollCreationInfo()?.kind == PollType.UNDISCLOSED_UNSTABLE val totalVotesText = (pollResponseSummary?.totalVotes ?: 0).let { when { @@ -262,13 +262,13 @@ class MessageItemFactory @Inject constructor( } } - pollContent.pollCreationInfo?.answers?.forEach { option -> + pollContent.getBestPollCreationInfo()?.answers?.forEach { option -> val voteSummary = pollResponseSummary?.votes?.get(option.id) val isMyVote = pollResponseSummary?.myVote == option.id val voteCount = voteSummary?.total ?: 0 val votePercentage = voteSummary?.percentage ?: 0.0 val optionId = option.id ?: "" - val optionAnswer = option.answer ?: "" + val optionAnswer = option.getBestAnswer() ?: "" optionViewStates.add( if (!isPollSent) { @@ -291,7 +291,7 @@ class MessageItemFactory @Inject constructor( ) } - val question = pollContent.pollCreationInfo?.question?.question ?: "" + val question = pollContent.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "" return PollItem_() .attributes(attributes) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index b41e1d8f25..f9d2613e27 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -94,7 +94,7 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me when (event.root.getClearType()) { // Message itemsX EventType.STICKER, - EventType.POLL_START, + in EventType.POLL_START, EventType.MESSAGE -> messageItemFactory.create(params) EventType.REDACTION, EventType.KEY_VERIFICATION_ACCEPT, @@ -107,8 +107,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.CALL_SELECT_ANSWER, EventType.CALL_NEGOTIATE, EventType.REACTION, - EventType.POLL_RESPONSE, - EventType.POLL_END -> noticeItemFactory.create(params) + in EventType.POLL_RESPONSE, + in EventType.POLL_END -> noticeItemFactory.create(params) // Calls EventType.CALL_INVITE, EventType.CALL_HANGUP, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt index d5f3a74e4e..1c339e6cf4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt @@ -120,14 +120,14 @@ class DisplayableEventFormatter @Inject constructor( EventType.CALL_CANDIDATES -> { span { } } - EventType.POLL_START -> { - timelineEvent.root.getClearContent().toModel(catchError = true)?.pollCreationInfo?.question?.question + in EventType.POLL_START -> { + timelineEvent.root.getClearContent().toModel(catchError = true)?.getBestPollCreationInfo()?.question?.getBestQuestion() ?: stringProvider.getString(R.string.sent_a_poll) } - EventType.POLL_RESPONSE -> { + in EventType.POLL_RESPONSE -> { stringProvider.getString(R.string.poll_response_room_list_preview) } - EventType.POLL_END -> { + in EventType.POLL_END -> { stringProvider.getString(R.string.poll_end_room_list_preview) } else -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index c7be395693..a20c1e5f97 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -106,8 +106,8 @@ class NoticeEventFormatter @Inject constructor( EventType.STATE_SPACE_PARENT, EventType.REDACTION, EventType.STICKER, - EventType.POLL_RESPONSE, - EventType.POLL_END -> formatDebug(timelineEvent.root) + in EventType.POLL_RESPONSE, + in EventType.POLL_END -> formatDebug(timelineEvent.root) else -> { Timber.v("Type $type not handled by this formatter") null @@ -196,8 +196,8 @@ class NoticeEventFormatter @Inject constructor( } private fun formatDebug(event: Event): CharSequence { - val threadPrefix = if (event.isThread()) "thread" else "" - return "Debug: $threadPrefix event type \"${event.getClearType()}\"" + val threadPrefix = if (event.isThread()) "thread" else "" + return "Debug: $threadPrefix event type \"${event.getClearType()}\"" } private fun formatRoomCreateEvent(event: Event, isDm: Boolean): CharSequence? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt index 53a9fbbaea..96a2ca4609 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineDisplayableEvents.kt @@ -50,9 +50,8 @@ object TimelineDisplayableEvents { EventType.STATE_ROOM_TOMBSTONE, EventType.STATE_ROOM_JOIN_RULES, EventType.KEY_VERIFICATION_DONE, - EventType.KEY_VERIFICATION_CANCEL, - EventType.POLL_START - ) + EventType.KEY_VERIFICATION_CANCEL + ) + EventType.POLL_START } fun TimelineEvent.canBeMerged(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt index 8c1c308bb5..96ab89bfea 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt @@ -43,10 +43,9 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess // Can be rendered in bubbles, other types will fallback to default private val EVENT_TYPES_WITH_BUBBLE_LAYOUT = setOf( EventType.MESSAGE, - EventType.POLL_START, EventType.ENCRYPTED, EventType.STICKER - ) + ) + EventType.POLL_START // Can't be rendered in bubbles, so get back to default layout private val MSG_TYPES_WITHOUT_BUBBLE_LAYOUT = setOf( diff --git a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollController.kt b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollController.kt index b4f61dbc1f..0ef92e4d2f 100644 --- a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollController.kt +++ b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollController.kt @@ -60,9 +60,9 @@ class CreatePollController @Inject constructor( pollTypeChangedListener { _, id -> host.callback?.onPollTypeChanged( if (id == R.id.openPollTypeRadioButton) { - PollType.DISCLOSED + PollType.DISCLOSED_UNSTABLE } else { - PollType.UNDISCLOSED + PollType.UNDISCLOSED_UNSTABLE } ) } diff --git a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewModel.kt b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewModel.kt index 5c7ef72297..2358f7f9a0 100644 --- a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewModel.kt @@ -71,9 +71,10 @@ class CreatePollViewModel @AssistedInject constructor( val event = room.getTimelineEvent(eventId) ?: return val content = event.getLastMessageContent() as? MessagePollContent ?: return - val pollType = content.pollCreationInfo?.kind ?: PollType.DISCLOSED - val question = content.pollCreationInfo?.question?.question ?: "" - val options = content.pollCreationInfo?.answers?.mapNotNull { it.answer } ?: List(MIN_OPTIONS_COUNT) { "" } + val pollCreationInfo = content.getBestPollCreationInfo() + val pollType = pollCreationInfo?.kind ?: PollType.DISCLOSED_UNSTABLE + val question = pollCreationInfo?.question?.getBestQuestion() ?: "" + val options = pollCreationInfo?.answers?.mapNotNull { it.getBestAnswer() } ?: List(MIN_OPTIONS_COUNT) { "" } setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewState.kt b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewState.kt index 175d1b0116..fc3b746f32 100644 --- a/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewState.kt +++ b/vector/src/main/java/im/vector/app/features/poll/create/CreatePollViewState.kt @@ -27,7 +27,7 @@ data class CreatePollViewState( val options: List = List(CreatePollViewModel.MIN_OPTIONS_COUNT) { "" }, val canCreatePoll: Boolean = false, val canAddMoreOptions: Boolean = true, - val pollType: PollType = PollType.DISCLOSED + val pollType: PollType = PollType.DISCLOSED_UNSTABLE ) : MavericksState { constructor(args: CreatePollArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/poll/create/PollTypeSelectionItem.kt b/vector/src/main/java/im/vector/app/features/poll/create/PollTypeSelectionItem.kt index 1b24a70cb9..0736c236b5 100644 --- a/vector/src/main/java/im/vector/app/features/poll/create/PollTypeSelectionItem.kt +++ b/vector/src/main/java/im/vector/app/features/poll/create/PollTypeSelectionItem.kt @@ -28,7 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.message.PollType abstract class PollTypeSelectionItem : VectorEpoxyModel() { @EpoxyAttribute - var pollType: PollType = PollType.DISCLOSED + var pollType: PollType = PollType.DISCLOSED_UNSTABLE @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var pollTypeChangedListener: RadioGroup.OnCheckedChangeListener? = null @@ -38,8 +38,8 @@ abstract class PollTypeSelectionItem : VectorEpoxyModel R.id.openPollTypeRadioButton - PollType.UNDISCLOSED -> R.id.closedPollTypeRadioButton + PollType.DISCLOSED_UNSTABLE, PollType.DISCLOSED -> R.id.openPollTypeRadioButton + PollType.UNDISCLOSED_UNSTABLE, PollType.UNDISCLOSED -> R.id.closedPollTypeRadioButton } ) diff --git a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt index 0ae2a16b71..b1f1f6b1e0 100644 --- a/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt +++ b/vector/src/main/java/im/vector/app/features/raw/wellknown/ElementWellKnown.kt @@ -38,8 +38,16 @@ data class ElementWellKnown( val riotE2E: E2EWellKnownConfig? = null, @Json(name = "org.matrix.msc3488.tile_server") - val mapTileServerConfig: MapTileServerConfig? = null -) + val unstableMapTileServerConfig: MapTileServerConfig? = null, + + @Json(name = "m.tile_server") + val stableMapTileServerConfig: MapTileServerConfig? = null +) { + + @Transient + var mapTileServerConfig: MapTileServerConfig? = null + get() = stableMapTileServerConfig ?: unstableMapTileServerConfig +} @JsonClass(generateAdapter = true) data class E2EWellKnownConfig( diff --git a/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt index e273c0b3c9..6b97b715db 100644 --- a/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt +++ b/vector/src/test/java/im/vector/app/features/location/LocationDataTest.kt @@ -22,6 +22,7 @@ import org.amshove.kluent.shouldBeTrue import org.junit.Test import org.matrix.android.sdk.api.session.room.model.message.LocationAsset import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType +import org.matrix.android.sdk.api.session.room.model.message.LocationInfo import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent class LocationDataTest { @@ -64,13 +65,24 @@ class LocationDataTest { @Test fun selfLocationTest() { - val contentWithNullAsset = MessageLocationContent(body = "", geoUri = "", locationAsset = null) + val contentWithNullAsset = MessageLocationContent(body = "", geoUri = "") contentWithNullAsset.isSelfLocation().shouldBeTrue() - val contentWithNullAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = null)) + val contentWithNullAssetType = MessageLocationContent(body = "", geoUri = "", unstableLocationAsset = LocationAsset(type = null)) contentWithNullAssetType.isSelfLocation().shouldBeTrue() - val contentWithSelfAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = LocationAssetType.SELF)) + val contentWithSelfAssetType = MessageLocationContent(body = "", geoUri = "", unstableLocationAsset = LocationAsset(type = LocationAssetType.SELF)) contentWithSelfAssetType.isSelfLocation().shouldBeTrue() } + + @Test + fun unstablePrefixTest() { + val geoUri = "geo :12.34,56.78;13.56" + + val contentWithUnstablePrefixes = MessageLocationContent(body = "", geoUri = "", unstableLocationInfo = LocationInfo(geoUri = geoUri)) + contentWithUnstablePrefixes.getBestLocationInfo()?.geoUri.shouldBeEqualTo(geoUri) + + val contentWithStablePrefixes = MessageLocationContent(body = "", geoUri = "", locationInfo = LocationInfo(geoUri = geoUri)) + contentWithStablePrefixes.getBestLocationInfo()?.geoUri.shouldBeEqualTo(geoUri) + } }