diff --git a/CHANGES.md b/CHANGES.md index 101276278c..4cfe3f1af2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,7 +7,7 @@ Features ✨: Improvements 🙌: - You can now join room through permalink and within room directory search - Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774) - - Fix several issues when uploading bug files (#1889) + - Fix several issues when uploading big files (#1889) - Do not propose to verify session if there is only one session and 4S is not configured (#1901) - Call screen does not use proximity sensor (#1735) @@ -27,6 +27,8 @@ Bugfix 🐛: - Support for image compression on Android 10 - Verification popup won't show - Android 6: App crash when read Contact permission is granted (#2064) + - JSON for verification events leaks in to the room list (#1246) + - Replies to poll appears in timeline as unsupported events during sending (#1004) Translations 🗣: - The SDK is now using SAS string translations from [Weblate Matrix-doc project](https://translate.riot.im/projects/matrix-doc/) (#1909) 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 4893947fc3..4a196193ba 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 @@ -347,7 +347,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr if (userId == senderId) { sumModel.myVote = optionIndex } - Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$relatedEventId ") + Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ") } else { Timber.v("## POLL Ignoring vote (older than known one) eventId:$eventId ") } @@ -356,7 +356,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(@UserId pr if (userId == senderId) { sumModel.myVote = optionIndex } - Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$relatedEventId ") + Timber.v("## POLL adding vote $optionIndex for user $senderId in poll :$targetEventId ") } sumModel.votes = votes if (isLocalEcho) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index a569b775a4..421cd1b063 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -17,6 +17,16 @@ package org.matrix.android.sdk.internal.session.room.timeline +import io.realm.OrderedCollectionChangeSet +import io.realm.OrderedRealmCollectionChangeListener +import io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.RealmQuery +import io.realm.RealmResults +import io.realm.Sort +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.EventType @@ -44,16 +54,6 @@ import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.Debouncer import org.matrix.android.sdk.internal.util.createBackgroundHandler import org.matrix.android.sdk.internal.util.createUIHandler -import io.realm.OrderedCollectionChangeSet -import io.realm.OrderedRealmCollectionChangeListener -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.RealmQuery -import io.realm.RealmResults -import io.realm.Sort -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode import timber.log.Timber import java.util.Collections import java.util.UUID @@ -317,12 +317,15 @@ internal class DefaultTimeline( @Subscribe(threadMode = ThreadMode.MAIN) fun onLocalEchoCreated(onLocalEchoCreated: OnLocalEchoCreated) { if (isLive && onLocalEchoCreated.roomId == roomId) { - listeners.forEach { - it.onNewTimelineEvents(listOf(onLocalEchoCreated.timelineEvent.eventId)) + // do not add events that would have been filtered + if (listOf(onLocalEchoCreated.timelineEvent).filterEventsWithSettings().isNotEmpty()) { + listeners.forEach { + it.onNewTimelineEvents(listOf(onLocalEchoCreated.timelineEvent.eventId)) + } + Timber.v("On local echo created: ${onLocalEchoCreated.timelineEvent.eventId}") + inMemorySendingEvents.add(0, onLocalEchoCreated.timelineEvent) + postSnapshot() } - Timber.v("On local echo created: $onLocalEchoCreated") - inMemorySendingEvents.add(0, onLocalEchoCreated.timelineEvent) - postSnapshot() } } @@ -778,7 +781,7 @@ internal class DefaultTimeline( val filterEdits = if (settings.filterEdits && it.root.type == EventType.MESSAGE) { val messageContent = it.root.content.toModel() - messageContent?.relatesTo?.type != RelationType.REPLACE + messageContent?.relatesTo?.type != RelationType.REPLACE && messageContent?.relatesTo?.type != RelationType.RESPONSE } else { true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 0120cbb2bb..eab0007080 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -63,6 +63,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage +import org.matrix.android.sdk.api.session.events.model.LocalEcho import org.matrix.android.sdk.api.session.events.model.isTextMessage import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel @@ -1218,6 +1219,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleReplyToOptions(action: RoomDetailAction.ReplyToOptions) { + // Do not allow to reply to unsent local echo + if (LocalEcho.isLocalEchoId(action.eventId)) return room.sendOptionsReply(action.eventId, action.optionIndex, action.optionValue) } 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 bf333b0417..cb9f583a2a 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 @@ -20,15 +20,17 @@ import im.vector.app.EmojiCompatWrapper import im.vector.app.R import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import me.gujun.android.span.span import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageOptionsContent import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_BUTTONS import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent import org.matrix.android.sdk.api.session.room.timeline.isReply -import me.gujun.android.span.span import javax.inject.Inject class DisplayableEventFormatter @Inject constructor( @@ -51,16 +53,16 @@ class DisplayableEventFormatter @Inject constructor( val senderName = timelineEvent.senderInfo.disambiguatedDisplayName when (timelineEvent.root.getClearType()) { - EventType.STICKER -> { + EventType.STICKER -> { return simpleFormat(senderName, stringProvider.getString(R.string.send_a_sticker), appendAuthor) } - EventType.REACTION -> { + EventType.REACTION -> { timelineEvent.root.getClearContent().toModel()?.relatesTo?.let { - val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(it.key) + val emojiSpanned = emojiCompatWrapper.safeEmojiSpanify(stringProvider.getString(R.string.sent_a_reaction, it.key)) return simpleFormat(senderName, emojiSpanned, appendAuthor) } } - EventType.MESSAGE -> { + EventType.MESSAGE -> { timelineEvent.getLastMessageContent()?.let { messageContent -> when (messageContent.msgType) { MessageType.MSGTYPE_VERIFICATION_REQUEST -> { @@ -88,13 +90,45 @@ class DisplayableEventFormatter @Inject constructor( simpleFormat(senderName, messageContent.body, appendAuthor) } } + MessageType.MSGTYPE_RESPONSE -> { + // do not show that? + return span { } + } + MessageType.MSGTYPE_OPTIONS -> { + return when (messageContent) { + is MessageOptionsContent -> { + val previewText = if (messageContent.optionType == OPTION_TYPE_BUTTONS) { + stringProvider.getString(R.string.sent_a_bot_buttons) + } else { + stringProvider.getString(R.string.sent_a_poll) + } + simpleFormat(senderName, previewText, appendAuthor) + } + else -> { + span { } + } + } + } else -> { return simpleFormat(senderName, messageContent.body, appendAuthor) } } } } - else -> { + EventType.KEY_VERIFICATION_CANCEL, + EventType.KEY_VERIFICATION_DONE -> { + // cancel and done can appear in timeline, so should have representation + return simpleFormat(senderName, stringProvider.getString(R.string.sent_verification_conclusion), appendAuthor) + } + EventType.KEY_VERIFICATION_START, + EventType.KEY_VERIFICATION_ACCEPT, + EventType.KEY_VERIFICATION_MAC, + EventType.KEY_VERIFICATION_KEY, + EventType.KEY_VERIFICATION_READY, + EventType.CALL_CANDIDATES -> { + return span { } + } + else -> { return span { text = noticeEventFormatter.format(timelineEvent) ?: "" textStyle = "italic" diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt index 8bc0e7d478..9d500b99b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessagePollItem.kt @@ -75,6 +75,8 @@ abstract class MessagePollItem : AbsMessageItem() { optionsContent?.options?.forEachIndexed { index, item -> if (index < buttons.size) { buttons[index].let { + // current limitation, have to wait for event to be sent in order to reply + it.isEnabled = informationData?.sendState?.isSent() ?: false it.text = item.label it.isVisible = true } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 06e9530ad5..267079fdc6 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2067,6 +2067,10 @@ Audio File Sticker + Poll + Bot Buttons + Reacted with: %s + Verification Conclusion Waiting… %s cancelled