Merge pull request #1436 from vector-im/feature/Fix_1430_X_made_no_change

Hide "X made no changes" event by default in timeline (#1430)
This commit is contained in:
Benoit Marty 2020-06-03 09:47:51 +02:00 committed by GitHub
commit 617e945afa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 58 additions and 3 deletions

View file

@ -6,6 +6,7 @@ Features ✨:
Improvements 🙌: Improvements 🙌:
- New wording for notice when current user is the sender - New wording for notice when current user is the sender
- Hide "X made no changes" event by default in timeline (#1430)
Bugfix 🐛: Bugfix 🐛:
- Switch theme is not fully taken into account without restarting the app - Switch theme is not fully taken into account without restarting the app

View file

@ -162,6 +162,8 @@ data class Event(
*/ */
fun isRedactedBySameUser() = senderId == unsignedData?.redactedEvent?.senderId fun isRedactedBySameUser() = senderId == unsignedData?.redactedEvent?.senderId
fun resolvedPrevContent(): Content? = prevContent ?: unsignedData?.prevContent
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false

View file

@ -32,6 +32,10 @@ data class TimelineSettings(
* A flag to filter redacted events * A flag to filter redacted events
*/ */
val filterRedacted: Boolean = false, 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 * 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. * If true, will build read receipts for each event.
*/ */
val buildReadReceipts: Boolean = true val buildReadReceipts: Boolean = true
) )

View file

@ -36,8 +36,8 @@ internal object EventMapper {
eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}" eventEntity.eventId = event.eventId ?: "$$roomId-${System.currentTimeMillis()}-${event.hashCode()}"
eventEntity.roomId = event.roomId ?: roomId eventEntity.roomId = event.roomId ?: roomId
eventEntity.content = ContentMapper.map(event.content) eventEntity.content = ContentMapper.map(event.content)
val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent eventEntity.prevContent = ContentMapper.map(event.resolvedPrevContent())
eventEntity.prevContent = ContentMapper.map(resolvedPrevContent) eventEntity.isUseless = IsUselessResolver.isUseless(event)
eventEntity.stateKey = event.stateKey eventEntity.stateKey = event.stateKey
eventEntity.type = event.type eventEntity.type = event.type
eventEntity.sender = event.senderId eventEntity.sender = event.senderId

View file

@ -0,0 +1,38 @@
/*
* 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 != null
&& event.content.toContent() == event.resolvedPrevContent()?.toContent()
}
else -> false
}
}
}

View file

@ -28,6 +28,7 @@ internal open class EventEntity(@Index var eventId: String = "",
@Index var type: String = "", @Index var type: String = "",
var content: String? = null, var content: String? = null,
var prevContent: String? = null, var prevContent: String? = null,
var isUseless: Boolean = false,
@Index var stateKey: String? = null, @Index var stateKey: String? = null,
var originServerTs: Long? = null, var originServerTs: Long? = null,
@Index var sender: String? = null, @Index var sender: String? = null,

View file

@ -775,6 +775,9 @@ internal class DefaultTimeline(
if (settings.filterTypes) { if (settings.filterTypes) {
`in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray()) `in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray())
} }
if (settings.filterUseless) {
not().equalTo(TimelineEventEntityFields.ROOT.IS_USELESS, true)
}
if (settings.filterEdits) { if (settings.filterEdits) {
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT) not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE) not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)

View file

@ -154,6 +154,11 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray()) not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
needOr = true needOr = true
} }
if (settings.filterUseless) {
if (needOr) or()
equalTo("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.IS_USELESS}", true)
needOr = true
}
if (settings.filterEdits) { if (settings.filterEdits) {
if (needOr) or() if (needOr) or()
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT) like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT)

View file

@ -100,12 +100,14 @@ class RoomDetailViewModel @AssistedInject constructor(
TimelineSettings(30, TimelineSettings(30,
filterEdits = false, filterEdits = false,
filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(), filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(),
filterUseless = false,
filterTypes = false, filterTypes = false,
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts()) buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
} else { } else {
TimelineSettings(30, TimelineSettings(30,
filterEdits = true, filterEdits = true,
filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(), filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(),
filterUseless = true,
filterTypes = true, filterTypes = true,
allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES, allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES,
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts()) buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())