Remove some state events at the start of DMs (#2252)
* Remove some initial events for DMs
This commit is contained in:
parent
69b290f6fd
commit
5d462d5ba9
5 changed files with 175 additions and 1 deletions
1
changelog.d/2217.misc
Normal file
1
changelog.d/2217.misc
Normal file
|
|
@ -0,0 +1 @@
|
|||
Remove room creation, self-join of room creator and 'this is the beginning of X' timeline items for DMs.
|
||||
|
|
@ -145,7 +145,7 @@ fun TimelineView(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (state.paginationState.beginningOfRoomReached) {
|
||||
if (state.paginationState.beginningOfRoomReached && !state.timelineRoomInfo.isDirect) {
|
||||
item(contentType = "BeginningOfRoomReached") {
|
||||
TimelineItemRoomBeginningView(roomName = roomName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessage
|
|||
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.DmBeginningTimelineProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.FilterHiddenStateEventsProcessor
|
||||
import io.element.android.libraries.matrix.impl.timeline.postprocessor.TimelineEncryptedHistoryPostProcessor
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
|
|
@ -85,6 +86,8 @@ class RustMatrixTimeline(
|
|||
|
||||
private val filterHiddenStateEventsProcessor = FilterHiddenStateEventsProcessor()
|
||||
|
||||
private val dmBeginningTimelineProcessor = DmBeginningTimelineProcessor()
|
||||
|
||||
private val timelineItemFactory = MatrixTimelineItemMapper(
|
||||
fetchDetailsForEvent = this::fetchDetailsForEvent,
|
||||
roomCoroutineScope = roomCoroutineScope,
|
||||
|
|
@ -107,6 +110,13 @@ class RustMatrixTimeline(
|
|||
override val timelineItems: Flow<List<MatrixTimelineItem>> = _timelineItems
|
||||
.mapLatest { items -> encryptedHistoryPostProcessor.process(items) }
|
||||
.mapLatest { items -> filterHiddenStateEventsProcessor.process(items) }
|
||||
.mapLatest { items ->
|
||||
dmBeginningTimelineProcessor.process(
|
||||
items = items,
|
||||
isDm = matrixRoom.isDirect && matrixRoom.isOneToOne,
|
||||
isAtStartOfTimeline = paginationState.value.beginningOfRoomReached
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
Timber.d("Initialize timeline for room ${matrixRoom.roomId}")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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 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.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
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.
|
||||
*/
|
||||
class DmBeginningTimelineProcessor {
|
||||
fun process(
|
||||
items: List<MatrixTimelineItem>,
|
||||
isDm: Boolean,
|
||||
isAtStartOfTimeline: Boolean
|
||||
): List<MatrixTimelineItem> {
|
||||
if (!isDm || !isAtStartOfTimeline) return items
|
||||
|
||||
// Find room creation event. This is usually index 0
|
||||
val roomCreationEventIndex = items.indexOfFirst {
|
||||
val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? StateContent
|
||||
stateEventContent?.content is OtherState.RoomCreate
|
||||
}
|
||||
|
||||
// Find self-join event for room creator. This is usually index 1
|
||||
val roomCreatorUserId = (items.getOrNull(roomCreationEventIndex) as? MatrixTimelineItem.Event)?.event?.sender
|
||||
val selfUserJoinedEventIndex = roomCreatorUserId?.let { creatorUserId ->
|
||||
items.indexOfFirst {
|
||||
val stateEventContent = (it as? MatrixTimelineItem.Event)?.event?.content as? RoomMembershipContent
|
||||
stateEventContent?.change == MembershipChange.JOINED && stateEventContent.userId == creatorUserId
|
||||
}
|
||||
} ?: -1
|
||||
|
||||
// Remove items at the indices we found
|
||||
val newItems = items.toMutableList()
|
||||
if (selfUserJoinedEventIndex in newItems.indices) {
|
||||
newItems.removeAt(selfUserJoinedEventIndex)
|
||||
}
|
||||
if (roomCreationEventIndex in newItems.indices) {
|
||||
newItems.removeAt(roomCreationEventIndex)
|
||||
}
|
||||
return newItems
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.timeline.postprocessor
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
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.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.timeline.aMessageContent
|
||||
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
|
||||
import org.junit.Test
|
||||
|
||||
class DmBeginningTimelineProcessorTest {
|
||||
@Test
|
||||
fun `processor removes room creation event and self-join event from DM timeline`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, MembershipChange.JOINED))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = true, isAtStartOfTimeline = true)
|
||||
assertThat(processedItems).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `processor removes room creation event and self-join event from DM timeline even if they're not the first items`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.member_other", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, MembershipChange.JOINED))),
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.message", anEventTimelineItem(content = aMessageContent("hi"))),
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, MembershipChange.JOINED))),
|
||||
)
|
||||
val expected = listOf(
|
||||
MatrixTimelineItem.Event("m.room.member_other", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, MembershipChange.JOINED))),
|
||||
MatrixTimelineItem.Event("m.room.message", anEventTimelineItem(content = aMessageContent("hi"))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = true, isAtStartOfTimeline = true)
|
||||
assertThat(processedItems).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `processor won't remove items if it's not a DM`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, MembershipChange.JOINED))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = false, isAtStartOfTimeline = true)
|
||||
assertThat(processedItems).isEqualTo(timelineItems)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `processor won't remove items if it's not at the start of the timeline`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, MembershipChange.JOINED))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = true, isAtStartOfTimeline = false)
|
||||
assertThat(processedItems).isEqualTo(timelineItems)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `processor won't remove the first member join event if it can't find the room creation event`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID, MembershipChange.JOINED))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = true, isAtStartOfTimeline = false)
|
||||
assertThat(processedItems).isEqualTo(timelineItems)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `processor won't remove the first member join event if it's not from the room creator`() {
|
||||
val timelineItems = listOf(
|
||||
MatrixTimelineItem.Event("m.room.create", anEventTimelineItem(sender = A_USER_ID, content = StateContent("", OtherState.RoomCreate))),
|
||||
MatrixTimelineItem.Event("m.room.member", anEventTimelineItem(content = RoomMembershipContent(A_USER_ID_2, MembershipChange.JOINED))),
|
||||
)
|
||||
val processor = DmBeginningTimelineProcessor()
|
||||
val processedItems = processor.process(timelineItems, isDm = true, isAtStartOfTimeline = false)
|
||||
assertThat(processedItems).isEqualTo(timelineItems)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue