Do not show membership/profile events in public rooms (#6360)

* Filter some membership/profile/topic events in public rooms: don't display join and leave membership events in publicly joinable rooms, and hide display name and avatar url changes in non encrypted and publicly joinable rooms.

* Add empty day post-processing to the timeline based on bxdxnn's code, tweaked.

---------

Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
bxdxnn 2026-05-25 11:31:53 +03:00 committed by GitHub
parent f297ce3d52
commit ff263e611e
6 changed files with 339 additions and 8 deletions

View file

@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.hash.hash
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
@ -20,6 +21,7 @@ import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.IntentionalMention
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.join.JoinRule
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.MsgType
@ -43,6 +45,7 @@ import io.element.android.libraries.matrix.impl.timeline.postprocessor.TypingNot
import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper
import io.element.android.libraries.matrix.impl.util.MessageEventContent
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -121,6 +124,13 @@ class RustTimeline(
private val lastForwardIndicatorsPostProcessor = LastForwardIndicatorsPostProcessor(mode)
private val typingNotificationPostProcessor = TypingNotificationPostProcessor(mode)
private data class RoomTimelineInfo(
val roomCreators: ImmutableList<UserId>,
val isDm: Boolean,
val joinRule: JoinRule?,
val isEncrypted: Boolean?,
)
override val backwardPaginationStatus = MutableStateFlow(
Timeline.PaginationStatus(isPaginating = false, hasMoreToLoad = mode != Timeline.Mode.PinnedEvents)
)
@ -220,20 +230,23 @@ class RustTimeline(
_timelineItems,
backwardPaginationStatus,
forwardPaginationStatus,
joinedRoom.roomInfoFlow.map { it.creators to it.isDm }.distinctUntilChanged(),
joinedRoom.roomInfoFlow.map { RoomTimelineInfo(it.creators, it.isDm, it.joinRule, it.isEncrypted) }.distinctUntilChanged(),
) {
timelineItems,
backwardPaginationStatus,
forwardPaginationStatus,
(roomCreators, isDm),
roomInfo,
->
withContext(dispatcher) {
val (roomCreators, isDm, joinRule, isEncrypted) = roomInfo
timelineItems
.let { items ->
roomBeginningPostProcessor.process(
items = items,
isDm = isDm,
roomCreator = roomCreators.firstOrNull(),
joinRule = joinRule,
isEncrypted = isEncrypted,
hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad,
)
}

View file

@ -9,33 +9,49 @@
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
/**
* This timeline post-processor removes the room creation event and the self-join event from the timeline for DMs
* or add the RoomBeginning item.
* or add the RoomBeginning item. For rooms that aren't invite-only and aren't encrypted, it also removes join/leave and profile change events.
*/
class RoomBeginningPostProcessor(private val mode: Timeline.Mode) {
fun process(
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