Skip to content

Commit cc11677

Browse files
committed
Improve MatrixRoom.createTimeline API.
1 parent 2e9a158 commit cc11677

File tree

15 files changed

+112
-94
lines changed

15 files changed

+112
-94
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/PinnedEventsTimelineProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import io.element.android.libraries.di.RoomScope
1414
import io.element.android.libraries.di.SingleIn
1515
import io.element.android.libraries.featureflag.api.FeatureFlagService
1616
import io.element.android.libraries.featureflag.api.FeatureFlags
17+
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
1718
import io.element.android.libraries.matrix.api.room.MatrixRoom
1819
import io.element.android.libraries.matrix.api.sync.SyncService
1920
import io.element.android.libraries.matrix.api.timeline.Timeline
@@ -104,7 +105,7 @@ class PinnedEventsTimelineProvider @Inject constructor(
104105
is AsyncData.Uninitialized, is AsyncData.Failure -> {
105106
timelineStateFlow.emit(AsyncData.Loading())
106107
withContext(dispatchers.io) {
107-
room.createTimeline(onlyPinnedEvents = true)
108+
room.createTimeline(CreateTimelineParams.PinnedOnly)
108109
}
109110
.fold(
110111
{ timelineStateFlow.emit(AsyncData.Success(it)) },

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.squareup.anvil.annotations.ContributesBinding
1111
import io.element.android.libraries.di.RoomScope
1212
import io.element.android.libraries.di.SingleIn
1313
import io.element.android.libraries.matrix.api.core.EventId
14+
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
1415
import io.element.android.libraries.matrix.api.room.MatrixRoom
1516
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
1617
import io.element.android.libraries.matrix.api.timeline.Timeline
@@ -64,7 +65,7 @@ class TimelineController @Inject constructor(
6465
}
6566

6667
suspend fun focusOnEvent(eventId: EventId): Result<Unit> {
67-
return room.createTimeline(focusedOnEventId = eventId)
68+
return room.createTimeline(CreateTimelineParams.Focused(eventId))
6869
.onFailure {
6970
if (it is CancellationException) {
7071
throw it

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/banner/PinnedMessagesBannerPresenterTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class PinnedMessagesBannerPresenterTest {
5555
@Test
5656
fun `present - loading state`() = runTest {
5757
val room = FakeMatrixRoom(
58-
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }
58+
createTimelineResult = { Result.success(FakeTimeline()) }
5959
).apply {
6060
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
6161
}
@@ -86,7 +86,7 @@ class PinnedMessagesBannerPresenterTest {
8686
)
8787
)
8888
val room = FakeMatrixRoom(
89-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }
89+
createTimelineResult = { Result.success(pinnedEventsTimeline) }
9090
).apply {
9191
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
9292
}
@@ -125,7 +125,7 @@ class PinnedMessagesBannerPresenterTest {
125125
)
126126
)
127127
val room = FakeMatrixRoom(
128-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }
128+
createTimelineResult = { Result.success(pinnedEventsTimeline) }
129129
).apply {
130130
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
131131
}
@@ -160,7 +160,7 @@ class PinnedMessagesBannerPresenterTest {
160160
@Test
161161
fun `present - timeline failed`() = runTest {
162162
val room = FakeMatrixRoom(
163-
createTimelineResult = { _, _, _ -> Result.failure(Exception()) }
163+
createTimelineResult = { Result.failure(Exception()) }
164164
).apply {
165165
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
166166
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListPresenterTest.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class PinnedMessagesListPresenterTest {
8383
@Test
8484
fun `present - timeline failure state`() = runTest {
8585
val room = FakeMatrixRoom(
86-
createTimelineResult = { _, _, _ -> Result.failure(RuntimeException()) },
86+
createTimelineResult = { Result.failure(RuntimeException()) },
8787
canRedactOwnResult = { Result.success(true) },
8888
canRedactOtherResult = { Result.success(true) },
8989
canUserPinUnpinResult = { Result.success(true) },
@@ -102,7 +102,7 @@ class PinnedMessagesListPresenterTest {
102102
@Test
103103
fun `present - empty state`() = runTest {
104104
val room = FakeMatrixRoom(
105-
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) },
105+
createTimelineResult = { Result.success(FakeTimeline()) },
106106
canRedactOwnResult = { Result.success(true) },
107107
canRedactOtherResult = { Result.success(true) },
108108
canUserPinUnpinResult = { Result.success(true) },
@@ -122,7 +122,7 @@ class PinnedMessagesListPresenterTest {
122122
fun `present - filled state`() = runTest {
123123
val pinnedEventsTimeline = createPinnedMessagesTimeline()
124124
val room = FakeMatrixRoom(
125-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) },
125+
createTimelineResult = { Result.success(pinnedEventsTimeline) },
126126
canRedactOwnResult = { Result.success(true) },
127127
canRedactOtherResult = { Result.success(true) },
128128
canUserPinUnpinResult = { Result.success(true) },
@@ -149,7 +149,7 @@ class PinnedMessagesListPresenterTest {
149149
val pinnedEventsTimeline = createPinnedMessagesTimeline()
150150
val analyticsService = FakeAnalyticsService()
151151
val room = FakeMatrixRoom(
152-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) },
152+
createTimelineResult = { Result.success(pinnedEventsTimeline) },
153153
canRedactOwnResult = { Result.success(true) },
154154
canRedactOtherResult = { Result.success(true) },
155155
canUserPinUnpinResult = { Result.success(true) },
@@ -195,7 +195,7 @@ class PinnedMessagesListPresenterTest {
195195
}
196196
val pinnedEventsTimeline = createPinnedMessagesTimeline()
197197
val room = FakeMatrixRoom(
198-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) },
198+
createTimelineResult = { Result.success(pinnedEventsTimeline) },
199199
canRedactOwnResult = { Result.success(true) },
200200
canRedactOtherResult = { Result.success(true) },
201201
canUserPinUnpinResult = { Result.success(true) },
@@ -224,7 +224,7 @@ class PinnedMessagesListPresenterTest {
224224
}
225225
val pinnedEventsTimeline = createPinnedMessagesTimeline()
226226
val room = FakeMatrixRoom(
227-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) },
227+
createTimelineResult = { Result.success(pinnedEventsTimeline) },
228228
canRedactOwnResult = { Result.success(true) },
229229
canRedactOtherResult = { Result.success(true) },
230230
canUserPinUnpinResult = { Result.success(true) },
@@ -253,7 +253,7 @@ class PinnedMessagesListPresenterTest {
253253
}
254254
val pinnedEventsTimeline = createPinnedMessagesTimeline()
255255
val room = FakeMatrixRoom(
256-
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) },
256+
createTimelineResult = { Result.success(pinnedEventsTimeline) },
257257
canRedactOwnResult = { Result.success(true) },
258258
canRedactOtherResult = { Result.success(true) },
259259
canUserPinUnpinResult = { Result.success(true) },

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineControllerTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class TimelineControllerTest {
3131
val detachedTimeline = FakeTimeline(name = "detached")
3232
val matrixRoom = FakeMatrixRoom(
3333
liveTimeline = liveTimeline,
34-
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) }
34+
createTimelineResult = { Result.success(detachedTimeline) }
3535
)
3636
val sut = TimelineController(matrixRoom)
3737

@@ -63,7 +63,7 @@ class TimelineControllerTest {
6363
var callNumber = 0
6464
val matrixRoom = FakeMatrixRoom(
6565
liveTimeline = liveTimeline,
66-
createTimelineResult = { _, _, _ ->
66+
createTimelineResult = {
6767
callNumber++
6868
when (callNumber) {
6969
1 -> Result.success(detachedTimeline1)
@@ -117,7 +117,7 @@ class TimelineControllerTest {
117117
val detachedTimeline = FakeTimeline(name = "detached")
118118
val matrixRoom = FakeMatrixRoom(
119119
liveTimeline = liveTimeline,
120-
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) }
120+
createTimelineResult = { Result.success(detachedTimeline) }
121121
)
122122
val sut = TimelineController(matrixRoom)
123123
sut.activeTimelineFlow().test {
@@ -167,7 +167,7 @@ class TimelineControllerTest {
167167
}
168168
val matrixRoom = FakeMatrixRoom(
169169
liveTimeline = liveTimeline,
170-
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) }
170+
createTimelineResult = { Result.success(detachedTimeline) }
171171
)
172172
val sut = TimelineController(matrixRoom)
173173
sut.activeTimelineFlow().test {
@@ -192,7 +192,7 @@ class TimelineControllerTest {
192192
val detachedTimeline = FakeTimeline(name = "detached")
193193
val matrixRoom = FakeMatrixRoom(
194194
liveTimeline = liveTimeline,
195-
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) }
195+
createTimelineResult = { Result.success(detachedTimeline) }
196196
)
197197
val sut = TimelineController(matrixRoom)
198198

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ import kotlin.time.Duration.Companion.seconds
483483
)
484484
val room = FakeMatrixRoom(
485485
liveTimeline = liveTimeline,
486-
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) },
486+
createTimelineResult = { Result.success(detachedTimeline) },
487487
canUserSendMessageResult = { _, _ -> Result.success(true) },
488488
)
489489
val presenter = createTimelinePresenter(
@@ -561,7 +561,7 @@ import kotlin.time.Duration.Companion.seconds
561561
liveTimeline = FakeTimeline(
562562
timelineItems = flowOf(emptyList()),
563563
),
564-
createTimelineResult = { _, _, _ -> Result.failure(Throwable("An error")) },
564+
createTimelineResult = { Result.failure(Throwable("An error")) },
565565
canUserSendMessageResult = { _, _ -> Result.success(true) },
566566
)
567567
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2025 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.api.room
9+
10+
import io.element.android.libraries.matrix.api.core.EventId
11+
12+
sealed interface CreateTimelineParams {
13+
data class Focused(val focusedEventId: EventId) : CreateTimelineParams
14+
data object MediaOnly : CreateTimelineParams
15+
data class MediaOnlyFocused(val focusedEventId: EventId) : CreateTimelineParams
16+
data object PinnedOnly : CreateTimelineParams
17+
}

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,10 @@ interface MatrixRoom : Closeable {
110110

111111
/**
112112
* Create a new timeline.
113-
* @param focusedOnEventId The event to focus on, if any. Note: if not null, and for regular timeline,
114-
* this method should not be used directly, see `TimelineController` to manage the various timelines.
115-
* @param onlyPinnedEvents True to get the timeline for pinned events only.
116-
* @param onlyMedia True to get the timeline for media events only.
113+
* @param createTimelineParams contains parameters about how to filter the timeline. Will also configure the date separators.
117114
*/
118115
suspend fun createTimeline(
119-
focusedOnEventId: EventId? = null,
120-
onlyPinnedEvents: Boolean = false,
121-
onlyMedia: Boolean = false,
116+
createTimelineParams: CreateTimelineParams,
122117
): Result<Timeline>
123118

124119
fun destroy()

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
2828
import io.element.android.libraries.matrix.api.media.VideoInfo
2929
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
3030
import io.element.android.libraries.matrix.api.poll.PollKind
31+
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
3132
import io.element.android.libraries.matrix.api.room.IntentionalMention
3233
import io.element.android.libraries.matrix.api.room.MatrixRoom
3334
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@@ -215,54 +216,49 @@ class RustMatrixRoom(
215216
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
216217

217218
override suspend fun createTimeline(
218-
focusedOnEventId: EventId?,
219-
onlyPinnedEvents: Boolean,
220-
onlyMedia: Boolean,
219+
createTimelineParams: CreateTimelineParams,
221220
): Result<Timeline> = withContext(roomDispatcher) {
222-
val focus = if (onlyPinnedEvents) {
223-
TimelineFocus.PinnedEvents(
221+
val focus = when (createTimelineParams) {
222+
is CreateTimelineParams.PinnedOnly -> TimelineFocus.PinnedEvents(
224223
maxEventsToLoad = 100u,
225224
maxConcurrentRequests = 10u,
226225
)
227-
} else if (focusedOnEventId != null) {
228-
TimelineFocus.Event(
229-
eventId = focusedOnEventId.value,
226+
is CreateTimelineParams.MediaOnly -> TimelineFocus.Live
227+
is CreateTimelineParams.Focused -> TimelineFocus.Event(
228+
eventId = createTimelineParams.focusedEventId.value,
229+
numContextEvents = 50u,
230+
)
231+
is CreateTimelineParams.MediaOnlyFocused -> TimelineFocus.Event(
232+
eventId = createTimelineParams.focusedEventId.value,
230233
numContextEvents = 50u,
231234
)
232-
} else {
233-
TimelineFocus.Live
234235
}
235-
val allowedMessageTypes = if (onlyMedia) {
236-
AllowedMessageTypes.Only(
236+
val allowedMessageTypes = when (createTimelineParams) {
237+
is CreateTimelineParams.MediaOnly,
238+
is CreateTimelineParams.MediaOnlyFocused -> AllowedMessageTypes.Only(
237239
types = listOf(
238240
RoomMessageEventMessageType.FILE,
239241
RoomMessageEventMessageType.IMAGE,
240242
RoomMessageEventMessageType.VIDEO,
241243
RoomMessageEventMessageType.AUDIO,
242244
)
243245
)
244-
} else {
245-
AllowedMessageTypes.All
246-
}
247-
val internalIdPrefix = if (onlyPinnedEvents) {
248-
"pinned_events"
249-
} else if (focusedOnEventId != null) {
250-
"focus_$focusedOnEventId"
251-
} else if (onlyMedia) {
252-
"MediaGallery_"
253-
} else {
254-
"live"
246+
is CreateTimelineParams.Focused,
247+
CreateTimelineParams.PinnedOnly -> AllowedMessageTypes.All
255248
}
256-
val dateDividerMode = if (onlyMedia) {
257-
DateDividerMode.MONTHLY
258-
} else {
259-
DateDividerMode.DAILY
249+
val internalIdPrefix = when (createTimelineParams) {
250+
is CreateTimelineParams.PinnedOnly -> "pinned_events"
251+
is CreateTimelineParams.Focused -> "focus_${createTimelineParams.focusedEventId}"
252+
is CreateTimelineParams.MediaOnly -> "MediaGallery_"
253+
is CreateTimelineParams.MediaOnlyFocused -> "MediaGallery_${createTimelineParams.focusedEventId}"
260254
}
261-
val mode = when {
262-
onlyPinnedEvents -> Timeline.Mode.PINNED_EVENTS
263-
focusedOnEventId != null -> Timeline.Mode.FOCUSED_ON_EVENT
264-
onlyMedia -> Timeline.Mode.MEDIA
265-
else -> Timeline.Mode.LIVE
255+
// Note that for TimelineFilter.MediaOnlyFocused, the date separator will be filtered out,
256+
// but there is no way to exclude data separator at the moment.
257+
val dateDividerMode = when (createTimelineParams) {
258+
is CreateTimelineParams.MediaOnly,
259+
is CreateTimelineParams.MediaOnlyFocused -> DateDividerMode.MONTHLY
260+
is CreateTimelineParams.Focused,
261+
CreateTimelineParams.PinnedOnly -> DateDividerMode.DAILY
266262
}
267263
runCatching {
268264
innerRoom.timelineWithConfiguration(
@@ -273,13 +269,20 @@ class RustMatrixRoom(
273269
dateDividerMode = dateDividerMode,
274270
)
275271
).let { inner ->
272+
val mode = when (createTimelineParams) {
273+
is CreateTimelineParams.Focused -> Timeline.Mode.FOCUSED_ON_EVENT
274+
is CreateTimelineParams.MediaOnly -> Timeline.Mode.MEDIA
275+
is CreateTimelineParams.MediaOnlyFocused -> Timeline.Mode.FOCUSED_ON_EVENT
276+
CreateTimelineParams.PinnedOnly -> Timeline.Mode.PINNED_EVENTS
277+
}
276278
createTimeline(inner, mode = mode)
277279
}
278280
}.mapFailure {
279-
if (focusedOnEventId != null) {
280-
it.toFocusEventException()
281-
} else {
282-
it
281+
when (createTimelineParams) {
282+
is CreateTimelineParams.Focused,
283+
is CreateTimelineParams.MediaOnlyFocused -> it.toFocusEventException()
284+
CreateTimelineParams.MediaOnly,
285+
CreateTimelineParams.PinnedOnly -> it
283286
}
284287
}.onFailure {
285288
if (it is CancellationException) {
@@ -604,6 +607,7 @@ class RustMatrixRoom(
604607

605608
override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = withContext(roomDispatcher) {
606609
runCatching {
610+
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
607611
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
608612
if (blockUserId != null) {
609613
innerRoom.ignoreUser(blockUserId.value)

libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
2424
import io.element.android.libraries.matrix.api.media.VideoInfo
2525
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
2626
import io.element.android.libraries.matrix.api.poll.PollKind
27+
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
2728
import io.element.android.libraries.matrix.api.room.IntentionalMention
2829
import io.element.android.libraries.matrix.api.room.MatrixRoom
2930
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@@ -138,7 +139,7 @@ class FakeMatrixRoom(
138139
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
139140
private val updateMembersResult: () -> Unit = { lambdaError() },
140141
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
141-
private val createTimelineResult: (EventId?, Boolean, Boolean) -> Result<Timeline> = { _, _, _ -> lambdaError() },
142+
private val createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
142143
private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> },
143144
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
144145
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
@@ -219,11 +220,9 @@ class FakeMatrixRoom(
219220
}
220221

221222
override suspend fun createTimeline(
222-
focusedOnEventId: EventId?,
223-
onlyPinnedEvents: Boolean,
224-
onlyMedia: Boolean,
223+
createTimelineParams: CreateTimelineParams,
225224
): Result<Timeline> = simulateLongTask {
226-
createTimelineResult(focusedOnEventId, onlyPinnedEvents, onlyMedia)
225+
createTimelineResult(createTimelineParams)
227226
}
228227

229228
override suspend fun subscribeToSync() {

0 commit comments

Comments
 (0)