From 578d90e46310aae670d19265b85f5e1946f63c6c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 6 Jul 2022 16:38:52 +0200 Subject: [PATCH 1/7] Adding changelog entry --- changelog.d/6487.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6487.feature diff --git a/changelog.d/6487.feature b/changelog.d/6487.feature new file mode 100644 index 0000000000..3d58e80bd5 --- /dev/null +++ b/changelog.d/6487.feature @@ -0,0 +1 @@ +[Timeline] - Collapse redacted events From 8d8ee051eb0f8c8b71265c7b05506f25de1dd921 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 7 Jul 2022 11:28:45 +0200 Subject: [PATCH 2/7] Improve readability of merged Header factory code --- .../factory/MergedHeaderItemFactory.kt | 64 ++++++++++++++----- .../helper/TimelineDisplayableEvents.kt | 5 -- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 800fc27e77..d1b97e388e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -24,7 +24,6 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.MergedTimelineEventVisibilityStateChangedListener import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper -import im.vector.app.features.home.room.detail.timeline.helper.canBeMerged import im.vector.app.features.home.room.detail.timeline.helper.isRoomConfiguration import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem @@ -35,6 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovement import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.query.QueryStringValue +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.content.EncryptionEventContent import org.matrix.android.sdk.api.session.events.model.toModel @@ -53,6 +53,7 @@ class MergedHeaderItemFactory @Inject constructor( private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper ) { + private val mergeableEventTypes = listOf(EventType.STATE_ROOM_MEMBER, EventType.STATE_ROOM_SERVER_ACL) private val collapsedEventIds = linkedSetOf() private val mergeItemCollapseStates = HashMap() @@ -78,19 +79,44 @@ class MergedHeaderItemFactory @Inject constructor( callback: TimelineEventController.Callback?, requestModelBuild: () -> Unit ): BasedMergedItem<*>? { - return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE && - event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel()?.creator)) { - // It's the first item before room.create - // Collapse all room configuration events - buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) - } else if (!event.canBeMerged() || (nextEvent?.root?.getClearType() == event.root.getClearType() && !addDaySeparator)) { - null - } else { - buildMembershipEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) + return when { + isRoomCreationSummary(event, nextEvent) -> + buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) + isSimilarEventSummary(event, nextEvent, addDaySeparator) -> + buildSimilarEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) + else -> null } } - private fun buildMembershipEventsMergedSummary( + /** + * @param event the main timeline event + * @param nextEvent is an older event than event + */ + private fun isRoomCreationSummary( + event: TimelineEvent, + nextEvent: TimelineEvent?, + ): Boolean { + // It's the first item before room.create + // Collapse all room configuration events + return nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE && + event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel()?.creator) + } + + /** + * @param event the main timeline event + * @param nextEvent is an older event than event + * @param addDaySeparator true to add a day separator + */ + private fun isSimilarEventSummary( + event: TimelineEvent, + nextEvent: TimelineEvent?, + addDaySeparator: Boolean, + ): Boolean { + return event.root.getClearType() in mergeableEventTypes && + (nextEvent?.root?.getClearType() != event.root.getClearType() || addDaySeparator) + } + + private fun buildSimilarEventsMergedSummary( currentPosition: Int, items: List, partialState: TimelineEventController.PartialState, @@ -140,11 +166,7 @@ class MergedHeaderItemFactory @Inject constructor( collapsedEventIds.removeAll(mergedEventIds) } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } - val summaryTitleResId = when (event.root.getClearType()) { - EventType.STATE_ROOM_MEMBER -> R.plurals.membership_changes - EventType.STATE_ROOM_SERVER_ACL -> R.plurals.notice_room_server_acl_changes - else -> null - } + val summaryTitleResId = getSummaryTitleResId(event.root) summaryTitleResId?.let { summaryTitle -> val attributes = MergedSimilarEventsItem.Attributes( summaryTitleResId = summaryTitle, @@ -168,6 +190,16 @@ class MergedHeaderItemFactory @Inject constructor( } } + private fun getSummaryTitleResId(event: Event): Int? { + val type = event.getClearType() + return when { + type == EventType.STATE_ROOM_MEMBER -> R.plurals.membership_changes + type == EventType.STATE_ROOM_SERVER_ACL -> R.plurals.notice_room_server_acl_changes + event.isRedacted() -> R.plurals.room_removed_messages + else -> null + } + } + private fun buildRoomCreationMergedSummary( currentPosition: Int, items: List, 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 85ffd7295c..e9f8e35dc9 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 @@ -54,11 +54,6 @@ object TimelineDisplayableEvents { ) + EventType.POLL_START + EventType.STATE_ROOM_BEACON_INFO } -fun TimelineEvent.canBeMerged(): Boolean { - return root.getClearType() == EventType.STATE_ROOM_MEMBER || - root.getClearType() == EventType.STATE_ROOM_SERVER_ACL -} - fun TimelineEvent.isRoomConfiguration(roomCreatorUserId: String?): Boolean { return root.isStateEvent() && when (root.getClearType()) { EventType.STATE_ROOM_GUEST_ACCESS, From b412b9f4e97610492b030899bc9c3d29977254e6 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 7 Jul 2022 17:34:19 +0200 Subject: [PATCH 3/7] Merging redacted events by reusing existing mechanism for same type events --- .../factory/MergedHeaderItemFactory.kt | 67 +++++++++++++--- .../helper/TimelineEventVisibilityHelper.kt | 80 +++++++++++++++---- vector/src/main/res/values/strings.xml | 4 + 3 files changed, 127 insertions(+), 24 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index d1b97e388e..668c7cdd82 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -46,6 +46,8 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject +private const val MIN_NUMBER_OF_MERGED_EVENTS = 2 + class MergedHeaderItemFactory @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val avatarRenderer: AvatarRenderer, @@ -80,10 +82,12 @@ class MergedHeaderItemFactory @Inject constructor( requestModelBuild: () -> Unit ): BasedMergedItem<*>? { return when { - isRoomCreationSummary(event, nextEvent) -> + isStartOfRoomCreationSummary(event, nextEvent) -> buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) - isSimilarEventSummary(event, nextEvent, addDaySeparator) -> - buildSimilarEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) + isStartOfSameTypeEventsSummary(event, nextEvent, addDaySeparator) -> + buildSameTypeEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) + isStartOfRedactedEventsSummary(event, nextEvent, addDaySeparator) -> + buildRedactedEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) else -> null } } @@ -92,7 +96,7 @@ class MergedHeaderItemFactory @Inject constructor( * @param event the main timeline event * @param nextEvent is an older event than event */ - private fun isRoomCreationSummary( + private fun isStartOfRoomCreationSummary( event: TimelineEvent, nextEvent: TimelineEvent?, ): Boolean { @@ -107,7 +111,7 @@ class MergedHeaderItemFactory @Inject constructor( * @param nextEvent is an older event than event * @param addDaySeparator true to add a day separator */ - private fun isSimilarEventSummary( + private fun isStartOfSameTypeEventsSummary( event: TimelineEvent, nextEvent: TimelineEvent?, addDaySeparator: Boolean, @@ -116,7 +120,21 @@ class MergedHeaderItemFactory @Inject constructor( (nextEvent?.root?.getClearType() != event.root.getClearType() || addDaySeparator) } - private fun buildSimilarEventsMergedSummary( + /** + * @param event the main timeline event + * @param nextEvent is an older event than event + * @param addDaySeparator true to add a day separator + */ + private fun isStartOfRedactedEventsSummary( + event: TimelineEvent, + nextEvent: TimelineEvent?, + addDaySeparator: Boolean, + ): Boolean { + return event.root.isRedacted() && + ((nextEvent?.root?.getClearType() != EventType.REDACTION && !nextEvent?.root?.isRedacted().orFalse()) || addDaySeparator) + } + + private fun buildSameTypeEventsMergedSummary( currentPosition: Int, items: List, partialState: TimelineEventController.PartialState, @@ -128,11 +146,42 @@ class MergedHeaderItemFactory @Inject constructor( val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents( items, currentPosition, - 2, + MIN_NUMBER_OF_MERGED_EVENTS, eventIdToHighlight, partialState.rootThreadEventId, partialState.isFromThreadTimeline() ) + return buildSimilarEventsMergedSummary(mergedEvents, partialState, event, eventIdToHighlight, requestModelBuild, callback) + } + + private fun buildRedactedEventsMergedSummary( + currentPosition: Int, + items: List, + partialState: TimelineEventController.PartialState, + event: TimelineEvent, + eventIdToHighlight: String?, + requestModelBuild: () -> Unit, + callback: TimelineEventController.Callback? + ): MergedSimilarEventsItem_? { + val mergedEvents = timelineEventVisibilityHelper.prevRedactedEvents( + items, + currentPosition, + MIN_NUMBER_OF_MERGED_EVENTS, + eventIdToHighlight, + partialState.rootThreadEventId, + partialState.isFromThreadTimeline() + ) + return buildSimilarEventsMergedSummary(mergedEvents, partialState, event, eventIdToHighlight, requestModelBuild, callback) + } + + private fun buildSimilarEventsMergedSummary( + mergedEvents: List, + partialState: TimelineEventController.PartialState, + event: TimelineEvent, + eventIdToHighlight: String?, + requestModelBuild: () -> Unit, + callback: TimelineEventController.Callback? + ): MergedSimilarEventsItem_? { return if (mergedEvents.isEmpty()) { null } else { @@ -153,7 +202,7 @@ class MergedHeaderItemFactory @Inject constructor( ) mergedData.add(data) } - val mergedEventIds = mergedEvents.map { it.localId } + val mergedEventIds = mergedEvents.map { it.localId }.toSet() // We try to find if one of the item id were used as mergeItemCollapseStates key // => handle case where paginating from mergeable events and we get more val previousCollapseStateKey = mergedEventIds.intersect(mergeItemCollapseStates.keys).firstOrNull() @@ -223,7 +272,7 @@ class MergedHeaderItemFactory @Inject constructor( tmpPos-- prevEvent = items.getOrNull(tmpPos) } - return if (mergedEvents.size > 2) { + return if (mergedEvents.size > MIN_NUMBER_OF_MERGED_EVENTS) { var highlighted = false val mergedData = ArrayList(mergedEvents.size) mergedEvents.reversed() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt index 488aebde13..3ab7d6a763 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.helper import im.vector.app.core.extensions.localDateTime import im.vector.app.core.resources.UserPreferencesProvider +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.RelationType import org.matrix.android.sdk.api.session.events.model.getRelationContent @@ -30,25 +31,38 @@ import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject -class TimelineEventVisibilityHelper @Inject constructor(private val userPreferencesProvider: UserPreferencesProvider) { +class TimelineEventVisibilityHelper @Inject constructor( + private val userPreferencesProvider: UserPreferencesProvider, +) { + + private interface PredicateToStopSearch { + /** + * Indicate whether a search on events should stop by comparing to given successive events. + * @param oldEvent the oldest event between the 2 events to compare + * @param newEvent the more recent event between the 2 events to compare + */ + fun shouldStopSearch(oldEvent: Event, newEvent: Event): Boolean + } /** - * @param timelineEvents the events to search in + * @param timelineEvents the events to search in, sorted from oldest event to newer event * @param index the index to start computing (inclusive) * @param minSize the minimum number of same type events to have sequentially, otherwise will return an empty list * @param eventIdToHighlight used to compute visibility * @param rootThreadEventId the root thread event id if in a thread timeline * @param isFromThreadTimeline true if the timeline is a thread + * @param predicateToStop events are taken until this condition is met * - * @return a list of timeline events which have sequentially the same type following the next direction. + * @return a list of timeline events which meet sequentially the same criteria following the next direction. */ - private fun nextSameTypeEvents( + private fun nextEventsUntil( timelineEvents: List, index: Int, minSize: Int, eventIdToHighlight: String?, rootThreadEventId: String?, - isFromThreadTimeline: Boolean + isFromThreadTimeline: Boolean, + predicateToStop: PredicateToStopSearch ): List { if (index >= timelineEvents.size - 1) { return emptyList() @@ -65,13 +79,15 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen } else { nextSubList.subList(0, indexOfNextDay) } - val indexOfFirstDifferentEventType = nextSameDayEvents.indexOfFirst { it.root.getClearType() != timelineEvent.root.getClearType() } - val sameTypeEvents = if (indexOfFirstDifferentEventType == -1) { + val indexOfFirstDifferentEvent = nextSameDayEvents.indexOfFirst { + predicateToStop.shouldStopSearch(oldEvent = timelineEvent.root, newEvent = it.root) + } + val similarEvents = if (indexOfFirstDifferentEvent == -1) { nextSameDayEvents } else { - nextSameDayEvents.subList(0, indexOfFirstDifferentEventType) + nextSameDayEvents.subList(0, indexOfFirstDifferentEvent) } - val filteredSameTypeEvents = sameTypeEvents.filter { + val filteredSimilarEvents = similarEvents.filter { shouldShowEvent( timelineEvent = it, highlightedEventId = eventIdToHighlight, @@ -79,14 +95,11 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen rootThreadEventId = rootThreadEventId ) } - if (filteredSameTypeEvents.size < minSize) { - return emptyList() - } - return filteredSameTypeEvents + return if (filteredSimilarEvents.size < minSize) emptyList() else filteredSimilarEvents } /** - * @param timelineEvents the events to search in + * @param timelineEvents the events to search in, sorted from newer event to oldest event * @param index the index to start computing (inclusive) * @param minSize the minimum number of same type events to have sequentially, otherwise will return an empty list * @param eventIdToHighlight used to compute visibility @@ -107,7 +120,44 @@ class TimelineEventVisibilityHelper @Inject constructor(private val userPreferen return prevSub .reversed() .let { - nextSameTypeEvents(it, 0, minSize, eventIdToHighlight, rootThreadEventId, isFromThreadTimeline) + nextEventsUntil(it, 0, minSize, eventIdToHighlight, rootThreadEventId, isFromThreadTimeline, object : PredicateToStopSearch { + override fun shouldStopSearch(oldEvent: Event, newEvent: Event): Boolean { + return oldEvent.getClearType() != newEvent.getClearType() + } + }) + } + } + + /** + * @param timelineEvents the events to search in, sorted from newer event to oldest event + * @param index the index to start computing (inclusive) + * @param minSize the minimum number of same type events to have sequentially, otherwise will return an empty list + * @param eventIdToHighlight used to compute visibility + * @param rootThreadEventId the root thread eventId + * @param isFromThreadTimeline true if the timeline is a thread + * + * @return a list of timeline events which are all redacted following the prev direction. + */ + fun prevRedactedEvents( + timelineEvents: List, + index: Int, + minSize: Int, + eventIdToHighlight: String?, + rootThreadEventId: String?, + isFromThreadTimeline: Boolean + ): List { + val prevSub = timelineEvents + .subList(0, index + 1) + // Ensure to not take the REDACTION event into account + .filter { it.root.getClearType() != EventType.REDACTION } + return prevSub + .reversed() + .let { + nextEventsUntil(it, 0, minSize, eventIdToHighlight, rootThreadEventId, isFromThreadTimeline, object : PredicateToStopSearch { + override fun shouldStopSearch(oldEvent: Event, newEvent: Event): Boolean { + return oldEvent.isRedacted() && !newEvent.isRedacted() + } + }) } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ae5834c250..47efd22e25 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3103,4 +3103,8 @@ Please note: this is a labs feature using a temporary implementation. This means you will not be able to delete your location history, and advanced users will be able to see your location history even after you stop sharing your live location with this room. Enable location sharing + + %d message removed + %d messages removed + From c9794d8280fa678f52177284bc871938dac5d980 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 8 Jul 2022 10:22:59 +0200 Subject: [PATCH 4/7] Small fixes in comments --- .../detail/timeline/helper/TimelineEventVisibilityHelper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt index 3ab7d6a763..8af708fca1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineEventVisibilityHelper.kt @@ -37,7 +37,7 @@ class TimelineEventVisibilityHelper @Inject constructor( private interface PredicateToStopSearch { /** - * Indicate whether a search on events should stop by comparing to given successive events. + * Indicate whether a search on events should stop by comparing 2 given successive events. * @param oldEvent the oldest event between the 2 events to compare * @param newEvent the more recent event between the 2 events to compare */ @@ -148,7 +148,7 @@ class TimelineEventVisibilityHelper @Inject constructor( ): List { val prevSub = timelineEvents .subList(0, index + 1) - // Ensure to not take the REDACTION event into account + // Ensure to not take the REDACTION events into account .filter { it.root.getClearType() != EventType.REDACTION } return prevSub .reversed() From fe4174f005f7900d88a714f43333da8b228e18ea Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 11 Jul 2022 16:28:49 +0200 Subject: [PATCH 5/7] Small improvements in code --- .../detail/timeline/factory/MergedHeaderItemFactory.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 668c7cdd82..08d01c8614 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -46,8 +46,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import javax.inject.Inject -private const val MIN_NUMBER_OF_MERGED_EVENTS = 2 - class MergedHeaderItemFactory @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val avatarRenderer: AvatarRenderer, @@ -215,8 +213,7 @@ class MergedHeaderItemFactory @Inject constructor( collapsedEventIds.removeAll(mergedEventIds) } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() } - val summaryTitleResId = getSummaryTitleResId(event.root) - summaryTitleResId?.let { summaryTitle -> + getSummaryTitleResId(event.root)?.let { summaryTitle -> val attributes = MergedSimilarEventsItem.Attributes( summaryTitleResId = summaryTitle, isCollapsed = isCollapsed, @@ -345,4 +342,8 @@ class MergedHeaderItemFactory @Inject constructor( fun isCollapsed(localId: Long): Boolean { return collapsedEventIds.contains(localId) } + + companion object { + private const val MIN_NUMBER_OF_MERGED_EVENTS = 2 + } } From 4a5fe3c0f918408abc25fce91d0652ddff6e067f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 12 Jul 2022 14:49:20 +0200 Subject: [PATCH 6/7] Fix the criteria to start merging redacted events --- .../timeline/factory/MergedHeaderItemFactory.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index 08d01c8614..2149857ec2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -84,7 +84,7 @@ class MergedHeaderItemFactory @Inject constructor( buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) isStartOfSameTypeEventsSummary(event, nextEvent, addDaySeparator) -> buildSameTypeEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) - isStartOfRedactedEventsSummary(event, nextEvent, addDaySeparator) -> + isStartOfRedactedEventsSummary(event, items, currentPosition, addDaySeparator) -> buildRedactedEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) else -> null } @@ -120,16 +120,21 @@ class MergedHeaderItemFactory @Inject constructor( /** * @param event the main timeline event - * @param nextEvent is an older event than event + * @param items all known items, sorted from newer event to oldest event + * @param currentPosition the current position * @param addDaySeparator true to add a day separator */ private fun isStartOfRedactedEventsSummary( event: TimelineEvent, - nextEvent: TimelineEvent?, + items: List, + currentPosition: Int, addDaySeparator: Boolean, ): Boolean { + val nextNonRedactionEvent = items + .subList(fromIndex = currentPosition + 1, toIndex = items.size) + .find { it.root.getClearType() != EventType.REDACTION } return event.root.isRedacted() && - ((nextEvent?.root?.getClearType() != EventType.REDACTION && !nextEvent?.root?.isRedacted().orFalse()) || addDaySeparator) + (!nextNonRedactionEvent?.root?.isRedacted().orFalse() || addDaySeparator) } private fun buildSameTypeEventsMergedSummary( From 9c619001a5c4e180a672e1fd64000d62bb407cc3 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 19 Jul 2022 09:28:30 +0200 Subject: [PATCH 7/7] Updating string value for redacted event to be more consistent --- .../org/matrix/android/sdk/api/session/events/model/Event.kt | 2 +- vector/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 7f9ab4c6dd..554dc2ec9d 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 @@ -202,7 +202,7 @@ data class Event( * It will return a decrypted text message or an empty string otherwise. */ fun getDecryptedTextSummary(): String? { - if (isRedacted()) return "Message Deleted" + if (isRedacted()) return "Message removed" val text = getDecryptedValue() ?: run { if (isPoll()) { return getPollQuestion() ?: "created a poll." diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 47efd22e25..66bcddd371 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1608,7 +1608,7 @@ View Reactions Reactions - Message deleted + Message removed Show removed messages Show a placeholder for removed messages Event deleted by user