@@ -24,7 +24,6 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController
2424import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
2525import im.vector.app.features.home.room.detail.timeline.helper.MergedTimelineEventVisibilityStateChangedListener
2626import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
27- import im.vector.app.features.home.room.detail.timeline.helper.canBeMerged
2827import im.vector.app.features.home.room.detail.timeline.helper.isRoomConfiguration
2928import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem
3029import 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
3534import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
3635import org.matrix.android.sdk.api.extensions.orFalse
3736import org.matrix.android.sdk.api.query.QueryStringValue
37+ import org.matrix.android.sdk.api.session.events.model.Event
3838import org.matrix.android.sdk.api.session.events.model.EventType
3939import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
4040import org.matrix.android.sdk.api.session.events.model.toModel
@@ -53,6 +53,7 @@ class MergedHeaderItemFactory @Inject constructor(
5353 private val timelineEventVisibilityHelper : TimelineEventVisibilityHelper
5454) {
5555
56+ private val mergeableEventTypes = listOf (EventType .STATE_ROOM_MEMBER , EventType .STATE_ROOM_SERVER_ACL )
5657 private val collapsedEventIds = linkedSetOf<Long >()
5758 private val mergeItemCollapseStates = HashMap <Long , Boolean >()
5859
@@ -78,19 +79,65 @@ class MergedHeaderItemFactory @Inject constructor(
7879 callback : TimelineEventController .Callback ? ,
7980 requestModelBuild : () -> Unit
8081 ): BasedMergedItem <* >? {
81- return if (nextEvent?.root?.getClearType() == EventType .STATE_ROOM_CREATE &&
82- event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent >()?.creator)) {
83- // It's the first item before room.create
84- // Collapse all room configuration events
85- buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback)
86- } else if (! event.canBeMerged() || (nextEvent?.root?.getClearType() == event.root.getClearType() && ! addDaySeparator)) {
87- null
88- } else {
89- buildMembershipEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback)
82+ return when {
83+ isStartOfRoomCreationSummary(event, nextEvent) ->
84+ buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback)
85+ isStartOfSameTypeEventsSummary(event, nextEvent, addDaySeparator) ->
86+ buildSameTypeEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback)
87+ isStartOfRedactedEventsSummary(event, items, currentPosition, addDaySeparator) ->
88+ buildRedactedEventsMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback)
89+ else -> null
9090 }
9191 }
9292
93- private fun buildMembershipEventsMergedSummary (
93+ /* *
94+ * @param event the main timeline event
95+ * @param nextEvent is an older event than event
96+ */
97+ private fun isStartOfRoomCreationSummary (
98+ event : TimelineEvent ,
99+ nextEvent : TimelineEvent ? ,
100+ ): Boolean {
101+ // It's the first item before room.create
102+ // Collapse all room configuration events
103+ return nextEvent?.root?.getClearType() == EventType .STATE_ROOM_CREATE &&
104+ event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent >()?.creator)
105+ }
106+
107+ /* *
108+ * @param event the main timeline event
109+ * @param nextEvent is an older event than event
110+ * @param addDaySeparator true to add a day separator
111+ */
112+ private fun isStartOfSameTypeEventsSummary (
113+ event : TimelineEvent ,
114+ nextEvent : TimelineEvent ? ,
115+ addDaySeparator : Boolean ,
116+ ): Boolean {
117+ return event.root.getClearType() in mergeableEventTypes &&
118+ (nextEvent?.root?.getClearType() != event.root.getClearType() || addDaySeparator)
119+ }
120+
121+ /* *
122+ * @param event the main timeline event
123+ * @param items all known items, sorted from newer event to oldest event
124+ * @param currentPosition the current position
125+ * @param addDaySeparator true to add a day separator
126+ */
127+ private fun isStartOfRedactedEventsSummary (
128+ event : TimelineEvent ,
129+ items : List <TimelineEvent >,
130+ currentPosition : Int ,
131+ addDaySeparator : Boolean ,
132+ ): Boolean {
133+ val nextNonRedactionEvent = items
134+ .subList(fromIndex = currentPosition + 1 , toIndex = items.size)
135+ .find { it.root.getClearType() != EventType .REDACTION }
136+ return event.root.isRedacted() &&
137+ (! nextNonRedactionEvent?.root?.isRedacted().orFalse() || addDaySeparator)
138+ }
139+
140+ private fun buildSameTypeEventsMergedSummary (
94141 currentPosition : Int ,
95142 items : List <TimelineEvent >,
96143 partialState : TimelineEventController .PartialState ,
@@ -102,11 +149,42 @@ class MergedHeaderItemFactory @Inject constructor(
102149 val mergedEvents = timelineEventVisibilityHelper.prevSameTypeEvents(
103150 items,
104151 currentPosition,
105- 2 ,
152+ MIN_NUMBER_OF_MERGED_EVENTS ,
106153 eventIdToHighlight,
107154 partialState.rootThreadEventId,
108155 partialState.isFromThreadTimeline()
109156 )
157+ return buildSimilarEventsMergedSummary(mergedEvents, partialState, event, eventIdToHighlight, requestModelBuild, callback)
158+ }
159+
160+ private fun buildRedactedEventsMergedSummary (
161+ currentPosition : Int ,
162+ items : List <TimelineEvent >,
163+ partialState : TimelineEventController .PartialState ,
164+ event : TimelineEvent ,
165+ eventIdToHighlight : String? ,
166+ requestModelBuild : () -> Unit ,
167+ callback : TimelineEventController .Callback ?
168+ ): MergedSimilarEventsItem_ ? {
169+ val mergedEvents = timelineEventVisibilityHelper.prevRedactedEvents(
170+ items,
171+ currentPosition,
172+ MIN_NUMBER_OF_MERGED_EVENTS ,
173+ eventIdToHighlight,
174+ partialState.rootThreadEventId,
175+ partialState.isFromThreadTimeline()
176+ )
177+ return buildSimilarEventsMergedSummary(mergedEvents, partialState, event, eventIdToHighlight, requestModelBuild, callback)
178+ }
179+
180+ private fun buildSimilarEventsMergedSummary (
181+ mergedEvents : List <TimelineEvent >,
182+ partialState : TimelineEventController .PartialState ,
183+ event : TimelineEvent ,
184+ eventIdToHighlight : String? ,
185+ requestModelBuild : () -> Unit ,
186+ callback : TimelineEventController .Callback ?
187+ ): MergedSimilarEventsItem_ ? {
110188 return if (mergedEvents.isEmpty()) {
111189 null
112190 } else {
@@ -127,7 +205,7 @@ class MergedHeaderItemFactory @Inject constructor(
127205 )
128206 mergedData.add(data)
129207 }
130- val mergedEventIds = mergedEvents.map { it.localId }
208+ val mergedEventIds = mergedEvents.map { it.localId }.toSet()
131209 // We try to find if one of the item id were used as mergeItemCollapseStates key
132210 // => handle case where paginating from mergeable events and we get more
133211 val previousCollapseStateKey = mergedEventIds.intersect(mergeItemCollapseStates.keys).firstOrNull()
@@ -140,12 +218,7 @@ class MergedHeaderItemFactory @Inject constructor(
140218 collapsedEventIds.removeAll(mergedEventIds)
141219 }
142220 val mergeId = mergedEventIds.joinToString(separator = " _" ) { it.toString() }
143- val summaryTitleResId = when (event.root.getClearType()) {
144- EventType .STATE_ROOM_MEMBER -> R .plurals.membership_changes
145- EventType .STATE_ROOM_SERVER_ACL -> R .plurals.notice_room_server_acl_changes
146- else -> null
147- }
148- summaryTitleResId?.let { summaryTitle ->
221+ getSummaryTitleResId(event.root)?.let { summaryTitle ->
149222 val attributes = MergedSimilarEventsItem .Attributes (
150223 summaryTitleResId = summaryTitle,
151224 isCollapsed = isCollapsed,
@@ -168,6 +241,16 @@ class MergedHeaderItemFactory @Inject constructor(
168241 }
169242 }
170243
244+ private fun getSummaryTitleResId (event : Event ): Int? {
245+ val type = event.getClearType()
246+ return when {
247+ type == EventType .STATE_ROOM_MEMBER -> R .plurals.membership_changes
248+ type == EventType .STATE_ROOM_SERVER_ACL -> R .plurals.notice_room_server_acl_changes
249+ event.isRedacted() -> R .plurals.room_removed_messages
250+ else -> null
251+ }
252+ }
253+
171254 private fun buildRoomCreationMergedSummary (
172255 currentPosition : Int ,
173256 items : List <TimelineEvent >,
@@ -191,7 +274,7 @@ class MergedHeaderItemFactory @Inject constructor(
191274 tmpPos--
192275 prevEvent = items.getOrNull(tmpPos)
193276 }
194- return if (mergedEvents.size > 2 ) {
277+ return if (mergedEvents.size > MIN_NUMBER_OF_MERGED_EVENTS ) {
195278 var highlighted = false
196279 val mergedData = ArrayList <BasedMergedItem .Data >(mergedEvents.size)
197280 mergedEvents.reversed()
@@ -264,4 +347,8 @@ class MergedHeaderItemFactory @Inject constructor(
264347 fun isCollapsed (localId : Long ): Boolean {
265348 return collapsedEventIds.contains(localId)
266349 }
350+
351+ companion object {
352+ private const val MIN_NUMBER_OF_MERGED_EVENTS = 2
353+ }
267354}
0 commit comments