Move empty day separator filtering to a timeline post-processor (#6866)
* Move empty day separator filtering to a timeline post-processor * Split `FilterPublicMembershipChangesPostProcessor` from `RoomBeginningPostProcessor`
This commit is contained in:
parent
1f927b9965
commit
6cd0491292
9 changed files with 292 additions and 293 deletions
|
|
@ -38,6 +38,8 @@ import io.element.android.libraries.matrix.impl.room.location.into
|
|||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterEmptyDayPostProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterPublicMembershipChangesPostProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LastForwardIndicatorsPostProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.LoadingIndicatorsPostProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.RoomBeginningPostProcessor
|
||||
|
|
@ -84,7 +86,7 @@ private const val PAGINATION_SIZE = 50
|
|||
class RustTimeline(
|
||||
private val inner: InnerTimeline,
|
||||
override val mode: Timeline.Mode,
|
||||
private val systemClock: SystemClock,
|
||||
systemClock: SystemClock,
|
||||
private val joinedRoom: JoinedRoom,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val dispatcher: CoroutineDispatcher,
|
||||
|
|
@ -123,6 +125,8 @@ class RustTimeline(
|
|||
private val loadingIndicatorsPostProcessor = LoadingIndicatorsPostProcessor(systemClock)
|
||||
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
|
||||
private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode)
|
||||
private val publicMembershipChangesPostProcessor = FilterPublicMembershipChangesPostProcessor()
|
||||
private val emptyDayPostProcessor = FilterEmptyDayPostProcessor()
|
||||
|
||||
private data class RoomTimelineInfo(
|
||||
val roomCreators: ImmutableList<UserId>,
|
||||
|
|
@ -245,11 +249,21 @@ class RustTimeline(
|
|||
items = items,
|
||||
isDm = isDm,
|
||||
roomCreator = roomCreators.firstOrNull(),
|
||||
joinRule = joinRule,
|
||||
isEncrypted = isEncrypted,
|
||||
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
|
||||
)
|
||||
}
|
||||
// This should be the first post processor after room beginning.
|
||||
.let { items ->
|
||||
publicMembershipChangesPostProcessor.process(
|
||||
items = items,
|
||||
joinRule = joinRule,
|
||||
isEncrypted = isEncrypted,
|
||||
)
|
||||
}
|
||||
// After removing public membership changes, we might end up with empty days, so we need to filter them out.
|
||||
.let { items ->
|
||||
emptyDayPostProcessor.process(items)
|
||||
}
|
||||
.let { items ->
|
||||
loadingIndicatorsPostProcessor.process(
|
||||
items = items,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.timeline.postprocessor
|
||||
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem
|
||||
|
||||
/**
|
||||
* Post-processor to filter out day separators for days that don't contain any events.
|
||||
*/
|
||||
class FilterEmptyDayPostProcessor {
|
||||
/**
|
||||
* Filters out day separators from [items] for days that don't contain any events.
|
||||
*/
|
||||
fun process(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> = buildList {
|
||||
// The timeline is ordered by descending timestamp, so events that happened during a day appear before the day separator for that day.
|
||||
// We can use this to determine if a day separator should be kept or not.
|
||||
var hasEvent = false
|
||||
for (item in items) {
|
||||
if (item is MatrixTimelineItem.Event) {
|
||||
hasEvent = true
|
||||
add(item)
|
||||
} else if (item is MatrixTimelineItem.Virtual && item.virtual is VirtualTimelineItem.DayDivider) {
|
||||
if (hasEvent) {
|
||||
add(item)
|
||||
hasEvent = false
|
||||
}
|
||||
} else {
|
||||
add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.timeline.postprocessor
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
|
||||
/**
|
||||
* Post-processor to filter out public membership changes for non-encrypted, publicly joinable rooms.
|
||||
*/
|
||||
class FilterPublicMembershipChangesPostProcessor {
|
||||
/**
|
||||
* Filters out public membership changes from [items] if the room is publicly joinable and not encrypted.
|
||||
*/
|
||||
fun process(
|
||||
items: List<MatrixTimelineItem>,
|
||||
joinRule: JoinRule?,
|
||||
isEncrypted: Boolean?,
|
||||
): List<MatrixTimelineItem> {
|
||||
return if (joinRule !is JoinRule.Invite && isEncrypted == false) {
|
||||
filterMembershipEvents(items)
|
||||
} else {
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterMembershipEvents(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> = items.filter { item ->
|
||||
val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content ?: return@filter true
|
||||
when (eventContent) {
|
||||
is RoomMembershipContent -> eventContent.change != null && eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT)
|
||||
is ProfileChangeContent -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,10 @@
|
|||
package io.element.android.libraries.matrix.impl.timeline.postprocessor
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
|
||||
|
|
@ -27,31 +25,17 @@ class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
|
|||
items: List<MatrixTimelineItem>,
|
||||
isDm: Boolean,
|
||||
roomCreator: UserId?,
|
||||
joinRule: JoinRule?,
|
||||
isEncrypted: Boolean?,
|
||||
hasMoreToLoadBackwards: Boolean,
|
||||
): List<MatrixTimelineItem> {
|
||||
return when {
|
||||
items.isEmpty() -> items
|
||||
mode == Timeline.Mode.PinnedEvents -> items
|
||||
joinRule !is JoinRule.Invite && isEncrypted == false -> filterRoomMemberEvents(items)
|
||||
isDm -> processForDM(items, roomCreator)
|
||||
hasMoreToLoadBackwards -> items
|
||||
else -> processForRoom(items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterRoomMemberEvents(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
|
||||
return items.filter { item ->
|
||||
val eventContent = (item as? MatrixTimelineItem.Event)?.event?.content
|
||||
when (eventContent) {
|
||||
is RoomMembershipContent -> eventContent.change !in listOf(MembershipChange.JOINED, MembershipChange.LEFT)
|
||||
is ProfileChangeContent -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processForRoom(items: List<MatrixTimelineItem>): List<MatrixTimelineItem> {
|
||||
// No changes needed, timeline start item is already added by the SDK
|
||||
return items
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue