From 3f1e5b9b1e0d2e28cceec7dc5556de5901733529 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jun 2020 15:54:01 +0200 Subject: [PATCH 1/3] Hide "X made no changes" event by default in timeline (#1430) --- CHANGES.md | 1 + .../android/api/session/room/timeline/TimelineSettings.kt | 5 ++++- .../matrix/android/internal/database/mapper/EventMapper.kt | 2 ++ .../matrix/android/internal/database/model/EventEntity.kt | 1 + .../internal/session/room/timeline/DefaultTimeline.kt | 3 +++ .../session/room/timeline/TimelineHiddenReadReceipts.kt | 5 +++++ .../riotx/features/home/room/detail/RoomDetailViewModel.kt | 2 ++ 7 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ef31216264..4b6dd7ac90 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ Features ✨: Improvements 🙌: - New wording for notice when current user is the sender + - Hide "X made no changes" event by default in timeline (#1430) Bugfix 🐛: - Switch theme is not fully taken into account without restarting the app diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineSettings.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineSettings.kt index 154074b722..60ccec3074 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineSettings.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/timeline/TimelineSettings.kt @@ -32,6 +32,10 @@ data class TimelineSettings( * A flag to filter redacted events */ val filterRedacted: Boolean = false, + /** + * A flag to filter useless events, such as membership events without any change + */ + val filterUseless: Boolean = false, /** * A flag to filter by types. It should be used with [allowedTypes] field */ @@ -44,5 +48,4 @@ data class TimelineSettings( * If true, will build read receipts for each event. */ val buildReadReceipts: Boolean = true - ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index e6a082c720..6c90a67161 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.database.mapper import com.squareup.moshi.JsonDataException import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.UnsignedData import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult @@ -38,6 +39,7 @@ internal object EventMapper { eventEntity.content = ContentMapper.map(event.content) val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent eventEntity.prevContent = ContentMapper.map(resolvedPrevContent) + eventEntity.isUseless = event.type == EventType.STATE_ROOM_MEMBER && eventEntity.content == eventEntity.prevContent eventEntity.stateKey = event.stateKey eventEntity.type = event.type eventEntity.sender = event.senderId diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index 72015afc43..7e69e84840 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -28,6 +28,7 @@ internal open class EventEntity(@Index var eventId: String = "", @Index var type: String = "", var content: String? = null, var prevContent: String? = null, + var isUseless: Boolean = false, @Index var stateKey: String? = null, var originServerTs: Long? = null, @Index var sender: String? = null, diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt index 95a8581c2b..9b66d0c4c0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimeline.kt @@ -775,6 +775,9 @@ internal class DefaultTimeline( if (settings.filterTypes) { `in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray()) } + if (settings.filterUseless) { + not().equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true) + } if (settings.filterEdits) { not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT) not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt index 72e99701cd..ddfa7e91fe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineHiddenReadReceipts.kt @@ -154,6 +154,11 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray()) needOr = true } + if (settings.filterUseless) { + if (needOr) or() + equalTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.IS_USELESS}", true) + needOr = true + } if (settings.filterEdits) { if (needOr) or() like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index df6f46b431..34c7d2e22d 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -100,12 +100,14 @@ class RoomDetailViewModel @AssistedInject constructor( TimelineSettings(30, filterEdits = false, filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(), + filterUseless = false, filterTypes = false, buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts()) } else { TimelineSettings(30, filterEdits = true, filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(), + filterUseless = true, filterTypes = true, allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES, buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts()) From 9a592e9c7e905c9ce64ebaccea41f347088aedb4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2020 01:13:02 +0200 Subject: [PATCH 2/3] Create IsUselessResolver object --- .../android/api/session/events/model/Event.kt | 2 + .../internal/database/mapper/EventMapper.kt | 6 +-- .../database/mapper/IsUselessResolver.kt | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index d3780ebe60..ddf016b015 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -162,6 +162,8 @@ data class Event( */ fun isRedactedBySameUser() = senderId == unsignedData?.redactedEvent?.senderId + fun resolvedPrevContent(): Content? = prevContent ?: unsignedData?.prevContent + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 6c90a67161..e28de760fd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -19,7 +19,6 @@ package im.vector.matrix.android.internal.database.mapper import com.squareup.moshi.JsonDataException import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.UnsignedData import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult @@ -37,9 +36,8 @@ internal object EventMapper { eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}" eventEntity.roomId = event.roomId ?: roomId eventEntity.content = ContentMapper.map(event.content) - val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent - eventEntity.prevContent = ContentMapper.map(resolvedPrevContent) - eventEntity.isUseless = event.type == EventType.STATE_ROOM_MEMBER && eventEntity.content == eventEntity.prevContent + eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent()) + eventEntity.isUseless = IsUselessResolver.isUseless(event) eventEntity.stateKey = event.stateKey eventEntity.type = event.type eventEntity.sender = event.senderId diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt new file mode 100644 index 0000000000..eb149467b0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.matrix.android.internal.database.mapper + +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.EventType +import im.vector.matrix.android.api.session.events.model.toContent + +internal object IsUselessResolver { + + /** + * @return true if the event is useless + */ + fun isUseless(event: Event): Boolean { + return when (event.type) { + EventType.STATE_ROOM_MEMBER -> { + // Call toContent(), to filter out null value + event.content?.toContent() == event.resolvedPrevContent()?.toContent() + } + else -> false + } + } +} From 870c4bf765568b57d0a703ab7195d6f71e6e12d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 3 Jun 2020 09:32:20 +0200 Subject: [PATCH 3/3] Check that content is not null --- .../android/internal/database/mapper/IsUselessResolver.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt index eb149467b0..56ce488f1b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/IsUselessResolver.kt @@ -29,7 +29,8 @@ internal object IsUselessResolver { return when (event.type) { EventType.STATE_ROOM_MEMBER -> { // Call toContent(), to filter out null value - event.content?.toContent() == event.resolvedPrevContent()?.toContent() + event.content != null + && event.content.toContent() == event.resolvedPrevContent()?.toContent() } else -> false }