Skip to content

Commit 560101d

Browse files
committed
Pre-compute roomMembersById
once per timeline batch
1 parent 2c3eb0d commit 560101d

2 files changed

Lines changed: 19 additions & 15 deletions

File tree

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem
1919
import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
2020
import io.element.android.libraries.androidutils.diff.MutableListDiffCache
2121
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
22+
import io.element.android.libraries.matrix.api.core.UserId
2223
import io.element.android.libraries.matrix.api.room.RoomMember
2324
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
2425
import kotlinx.collections.immutable.ImmutableList
@@ -75,19 +76,22 @@ class TimelineItemsFactory(
7576
timelineItems: List<MatrixTimelineItem>,
7677
roomMembers: List<RoomMember>,
7778
) {
79+
// Index the member list once per batch so per-item sender / receipt lookups are O(1)
80+
// rather than O(N) — N can reach tens of thousands in large public rooms.
81+
val roomMembersById = roomMembers.associateBy { it.userId }
7882
val newTimelineItemStates = ArrayList<TimelineItem>()
7983
for (index in diffCache.indices().reversed()) {
8084
val cacheItem = diffCache.get(index)
8185
if (cacheItem == null) {
82-
buildAndCacheItem(timelineItems, index, roomMembers)?.also { timelineItemState ->
86+
buildAndCacheItem(timelineItems, index, roomMembersById)?.also { timelineItemState ->
8387
newTimelineItemStates.add(timelineItemState)
8488
}
8589
} else {
86-
val updatedItem = if (cacheItem is TimelineItem.Event && roomMembers.isNotEmpty()) {
90+
val updatedItem = if (cacheItem is TimelineItem.Event && roomMembersById.isNotEmpty()) {
8791
eventItemFactory.update(
8892
timelineItem = cacheItem,
8993
receivedMatrixTimelineItem = timelineItems[index] as MatrixTimelineItem.Event,
90-
roomMembers = roomMembers
94+
roomMembersById = roomMembersById,
9195
)
9296
} else {
9397
cacheItem
@@ -102,11 +106,11 @@ class TimelineItemsFactory(
102106
private suspend fun buildAndCacheItem(
103107
timelineItems: List<MatrixTimelineItem>,
104108
index: Int,
105-
roomMembers: List<RoomMember>,
109+
roomMembersById: Map<UserId, RoomMember>,
106110
): TimelineItem? {
107111
val timelineItem =
108112
when (val currentTimelineItem = timelineItems[index]) {
109-
is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers)
113+
is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembersById)
110114
is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem)
111115
MatrixTimelineItem.Other -> null
112116
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ class TimelineItemEventFactory(
5959
currentTimelineItem: MatrixTimelineItem.Event,
6060
index: Int,
6161
timelineItems: List<MatrixTimelineItem>,
62-
roomMembers: List<RoomMember>,
62+
roomMembersById: Map<UserId, RoomMember>,
6363
): TimelineItem.Event {
6464
val currentSender = currentTimelineItem.event.sender
6565
val groupPosition =
6666
computeGroupPosition(currentTimelineItem, timelineItems, index)
6767
val (senderProfile, senderAvatarData) = resolveSender(
6868
sender = currentSender,
6969
eventSenderProfile = currentTimelineItem.event.senderProfile,
70-
roomMembers = roomMembers,
70+
roomMembersById = roomMembersById,
7171
)
7272
val sentTime = dateFormatter.format(
7373
timestamp = currentTimelineItem.event.timestamp,
@@ -117,7 +117,7 @@ class TimelineItemEventFactory(
117117
sentDate = sentDate,
118118
groupPosition = groupPosition,
119119
reactionsState = currentTimelineItem.computeReactionsState(),
120-
readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembers),
120+
readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembersById),
121121
localSendState = currentTimelineItem.event.localSendState,
122122
inReplyTo = currentTimelineItem.event.inReplyTo()?.map(permalinkParser = permalinkParser),
123123
threadInfo = mappedThreadInfo,
@@ -133,19 +133,19 @@ class TimelineItemEventFactory(
133133
fun update(
134134
timelineItem: TimelineItem.Event,
135135
receivedMatrixTimelineItem: MatrixTimelineItem.Event,
136-
roomMembers: List<RoomMember>,
136+
roomMembersById: Map<UserId, RoomMember>,
137137
): TimelineItem.Event {
138138
// Recompute the sender profile so that avatar / display name updates propagate to rows
139139
// already in the diff cache. The avatar is also rebuilt because it derives from senderProfile.
140140
val (senderProfile, senderAvatarData) = resolveSender(
141141
sender = timelineItem.senderId,
142142
eventSenderProfile = receivedMatrixTimelineItem.event.senderProfile,
143-
roomMembers = roomMembers,
143+
roomMembersById = roomMembersById,
144144
)
145145
return timelineItem.copy(
146146
senderProfile = senderProfile,
147147
senderAvatar = senderAvatarData,
148-
readReceiptState = receivedMatrixTimelineItem.computeReadReceiptState(roomMembers),
148+
readReceiptState = receivedMatrixTimelineItem.computeReadReceiptState(roomMembersById),
149149
)
150150
}
151151

@@ -158,10 +158,10 @@ class TimelineItemEventFactory(
158158
private fun resolveSender(
159159
sender: UserId,
160160
eventSenderProfile: ProfileDetails,
161-
roomMembers: List<RoomMember>,
161+
roomMembersById: Map<UserId, RoomMember>,
162162
): Pair<ProfileDetails, AvatarData> {
163163
val senderProfile = eventSenderProfile.withLiveMemberOverride(
164-
roomMembers.find { it.userId == sender }
164+
roomMembersById[sender]
165165
)
166166
val avatarData = AvatarData(
167167
id = sender.value,
@@ -212,15 +212,15 @@ class TimelineItemEventFactory(
212212
}
213213

214214
private fun MatrixTimelineItem.Event.computeReadReceiptState(
215-
roomMembers: List<RoomMember>,
215+
roomMembersById: Map<UserId, RoomMember>,
216216
): TimelineItemReadReceipts {
217217
if (!config.computeReadReceipts) {
218218
return TimelineItemReadReceipts(receipts = persistentListOf())
219219
}
220220
return TimelineItemReadReceipts(
221221
receipts = event.receipts
222222
.map { receipt ->
223-
val roomMember = roomMembers.find { it.userId == receipt.userId }
223+
val roomMember = roomMembersById[receipt.userId]
224224
ReadReceiptData(
225225
avatarData = AvatarData(
226226
id = receipt.userId.value,

0 commit comments

Comments
 (0)