Improve MatrixRoom.createTimeline API.

This commit is contained in:
Benoit Marty 2025-02-18 10:59:26 +01:00
parent c02436d3f0
commit e35eda0c67
15 changed files with 112 additions and 94 deletions

View file

@ -14,6 +14,7 @@ import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
@ -104,7 +105,7 @@ class PinnedEventsTimelineProvider @Inject constructor(
is AsyncData.Uninitialized, is AsyncData.Failure -> { is AsyncData.Uninitialized, is AsyncData.Failure -> {
timelineStateFlow.emit(AsyncData.Loading()) timelineStateFlow.emit(AsyncData.Loading())
withContext(dispatchers.io) { withContext(dispatchers.io) {
room.createTimeline(onlyPinnedEvents = true) room.createTimeline(CreateTimelineParams.PinnedOnly)
} }
.fold( .fold(
{ timelineStateFlow.emit(AsyncData.Success(it)) }, { timelineStateFlow.emit(AsyncData.Success(it)) },

View file

@ -11,6 +11,7 @@ import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem 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.Timeline
@ -64,7 +65,7 @@ class TimelineController @Inject constructor(
} }
suspend fun focusOnEvent(eventId: EventId): Result<Unit> { suspend fun focusOnEvent(eventId: EventId): Result<Unit> {
return room.createTimeline(focusedOnEventId = eventId) return room.createTimeline(CreateTimelineParams.Focused(eventId))
.onFailure { .onFailure {
if (it is CancellationException) { if (it is CancellationException) {
throw it throw it

View file

@ -55,7 +55,7 @@ class PinnedMessagesBannerPresenterTest {
@Test @Test
fun `present - loading state`() = runTest { fun `present - loading state`() = runTest {
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) } createTimelineResult = { Result.success(FakeTimeline()) }
).apply { ).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID))) givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
} }
@ -86,7 +86,7 @@ class PinnedMessagesBannerPresenterTest {
) )
) )
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) } createTimelineResult = { Result.success(pinnedEventsTimeline) }
).apply { ).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2))) givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
} }
@ -125,7 +125,7 @@ class PinnedMessagesBannerPresenterTest {
) )
) )
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) } createTimelineResult = { Result.success(pinnedEventsTimeline) }
).apply { ).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2))) givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID, AN_EVENT_ID_2)))
} }
@ -160,7 +160,7 @@ class PinnedMessagesBannerPresenterTest {
@Test @Test
fun `present - timeline failed`() = runTest { fun `present - timeline failed`() = runTest {
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.failure(Exception()) } createTimelineResult = { Result.failure(Exception()) }
).apply { ).apply {
givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID))) givenRoomInfo(aRoomInfo(pinnedEventIds = listOf(AN_EVENT_ID)))
} }

View file

@ -83,7 +83,7 @@ class PinnedMessagesListPresenterTest {
@Test @Test
fun `present - timeline failure state`() = runTest { fun `present - timeline failure state`() = runTest {
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.failure(RuntimeException()) }, createTimelineResult = { Result.failure(RuntimeException()) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -102,7 +102,7 @@ class PinnedMessagesListPresenterTest {
@Test @Test
fun `present - empty state`() = runTest { fun `present - empty state`() = runTest {
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -122,7 +122,7 @@ class PinnedMessagesListPresenterTest {
fun `present - filled state`() = runTest { fun `present - filled state`() = runTest {
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }, createTimelineResult = { Result.success(pinnedEventsTimeline) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -149,7 +149,7 @@ class PinnedMessagesListPresenterTest {
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val analyticsService = FakeAnalyticsService() val analyticsService = FakeAnalyticsService()
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }, createTimelineResult = { Result.success(pinnedEventsTimeline) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -195,7 +195,7 @@ class PinnedMessagesListPresenterTest {
} }
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }, createTimelineResult = { Result.success(pinnedEventsTimeline) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -224,7 +224,7 @@ class PinnedMessagesListPresenterTest {
} }
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }, createTimelineResult = { Result.success(pinnedEventsTimeline) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },
@ -253,7 +253,7 @@ class PinnedMessagesListPresenterTest {
} }
val pinnedEventsTimeline = createPinnedMessagesTimeline() val pinnedEventsTimeline = createPinnedMessagesTimeline()
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(pinnedEventsTimeline) }, createTimelineResult = { Result.success(pinnedEventsTimeline) },
canRedactOwnResult = { Result.success(true) }, canRedactOwnResult = { Result.success(true) },
canRedactOtherResult = { Result.success(true) }, canRedactOtherResult = { Result.success(true) },
canUserPinUnpinResult = { Result.success(true) }, canUserPinUnpinResult = { Result.success(true) },

View file

@ -31,7 +31,7 @@ class TimelineControllerTest {
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) } createTimelineResult = { Result.success(detachedTimeline) }
) )
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
@ -63,7 +63,7 @@ class TimelineControllerTest {
var callNumber = 0 var callNumber = 0
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> createTimelineResult = {
callNumber++ callNumber++
when (callNumber) { when (callNumber) {
1 -> Result.success(detachedTimeline1) 1 -> Result.success(detachedTimeline1)
@ -117,7 +117,7 @@ class TimelineControllerTest {
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) } createTimelineResult = { Result.success(detachedTimeline) }
) )
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {
@ -167,7 +167,7 @@ class TimelineControllerTest {
} }
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) } createTimelineResult = { Result.success(detachedTimeline) }
) )
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)
sut.activeTimelineFlow().test { sut.activeTimelineFlow().test {
@ -192,7 +192,7 @@ class TimelineControllerTest {
val detachedTimeline = FakeTimeline(name = "detached") val detachedTimeline = FakeTimeline(name = "detached")
val matrixRoom = FakeMatrixRoom( val matrixRoom = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) } createTimelineResult = { Result.success(detachedTimeline) }
) )
val sut = TimelineController(matrixRoom) val sut = TimelineController(matrixRoom)

View file

@ -483,7 +483,7 @@ import kotlin.time.Duration.Companion.seconds
) )
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
liveTimeline = liveTimeline, liveTimeline = liveTimeline,
createTimelineResult = { _, _, _ -> Result.success(detachedTimeline) }, createTimelineResult = { Result.success(detachedTimeline) },
canUserSendMessageResult = { _, _ -> Result.success(true) }, canUserSendMessageResult = { _, _ -> Result.success(true) },
) )
val presenter = createTimelinePresenter( val presenter = createTimelinePresenter(
@ -561,7 +561,7 @@ import kotlin.time.Duration.Companion.seconds
liveTimeline = FakeTimeline( liveTimeline = FakeTimeline(
timelineItems = flowOf(emptyList()), timelineItems = flowOf(emptyList()),
), ),
createTimelineResult = { _, _, _ -> Result.failure(Throwable("An error")) }, createTimelineResult = { Result.failure(Throwable("An error")) },
canUserSendMessageResult = { _, _ -> Result.success(true) }, canUserSendMessageResult = { _, _ -> Result.success(true) },
) )
) )

View file

@ -0,0 +1,17 @@
/*
* Copyright 2025 New Vector 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.api.room
import io.element.android.libraries.matrix.api.core.EventId
sealed interface CreateTimelineParams {
data class Focused(val focusedEventId: EventId) : CreateTimelineParams
data object MediaOnly : CreateTimelineParams
data class MediaOnlyFocused(val focusedEventId: EventId) : CreateTimelineParams
data object PinnedOnly : CreateTimelineParams
}

View file

@ -110,15 +110,10 @@ interface MatrixRoom : Closeable {
/** /**
* Create a new timeline. * Create a new timeline.
* @param focusedOnEventId The event to focus on, if any. Note: if not null, and for regular timeline, * @param createTimelineParams contains parameters about how to filter the timeline. Will also configure the date separators.
* this method should not be used directly, see `TimelineController` to manage the various timelines.
* @param onlyPinnedEvents True to get the timeline for pinned events only.
* @param onlyMedia True to get the timeline for media events only.
*/ */
suspend fun createTimeline( suspend fun createTimeline(
focusedOnEventId: EventId? = null, createTimelineParams: CreateTimelineParams,
onlyPinnedEvents: Boolean = false,
onlyMedia: Boolean = false,
): Result<Timeline> ): Result<Timeline>
fun destroy() fun destroy()

View file

@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.IntentionalMention
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@ -215,25 +216,26 @@ class RustMatrixRoom(
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId) override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
override suspend fun createTimeline( override suspend fun createTimeline(
focusedOnEventId: EventId?, createTimelineParams: CreateTimelineParams,
onlyPinnedEvents: Boolean,
onlyMedia: Boolean,
): Result<Timeline> = withContext(roomDispatcher) { ): Result<Timeline> = withContext(roomDispatcher) {
val focus = if (onlyPinnedEvents) { val focus = when (createTimelineParams) {
TimelineFocus.PinnedEvents( is CreateTimelineParams.PinnedOnly -> TimelineFocus.PinnedEvents(
maxEventsToLoad = 100u, maxEventsToLoad = 100u,
maxConcurrentRequests = 10u, maxConcurrentRequests = 10u,
) )
} else if (focusedOnEventId != null) { is CreateTimelineParams.MediaOnly -> TimelineFocus.Live
TimelineFocus.Event( is CreateTimelineParams.Focused -> TimelineFocus.Event(
eventId = focusedOnEventId.value, eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u,
)
is CreateTimelineParams.MediaOnlyFocused -> TimelineFocus.Event(
eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u, numContextEvents = 50u,
) )
} else {
TimelineFocus.Live
} }
val allowedMessageTypes = if (onlyMedia) { val allowedMessageTypes = when (createTimelineParams) {
AllowedMessageTypes.Only( is CreateTimelineParams.MediaOnly,
is CreateTimelineParams.MediaOnlyFocused -> AllowedMessageTypes.Only(
types = listOf( types = listOf(
RoomMessageEventMessageType.FILE, RoomMessageEventMessageType.FILE,
RoomMessageEventMessageType.IMAGE, RoomMessageEventMessageType.IMAGE,
@ -241,28 +243,22 @@ class RustMatrixRoom(
RoomMessageEventMessageType.AUDIO, RoomMessageEventMessageType.AUDIO,
) )
) )
} else { is CreateTimelineParams.Focused,
AllowedMessageTypes.All CreateTimelineParams.PinnedOnly -> AllowedMessageTypes.All
} }
val internalIdPrefix = if (onlyPinnedEvents) { val internalIdPrefix = when (createTimelineParams) {
"pinned_events" is CreateTimelineParams.PinnedOnly -> "pinned_events"
} else if (focusedOnEventId != null) { is CreateTimelineParams.Focused -> "focus_${createTimelineParams.focusedEventId}"
"focus_$focusedOnEventId" is CreateTimelineParams.MediaOnly -> "MediaGallery_"
} else if (onlyMedia) { is CreateTimelineParams.MediaOnlyFocused -> "MediaGallery_${createTimelineParams.focusedEventId}"
"MediaGallery_"
} else {
"live"
} }
val dateDividerMode = if (onlyMedia) { // Note that for TimelineFilter.MediaOnlyFocused, the date separator will be filtered out,
DateDividerMode.MONTHLY // but there is no way to exclude data separator at the moment.
} else { val dateDividerMode = when (createTimelineParams) {
DateDividerMode.DAILY is CreateTimelineParams.MediaOnly,
} is CreateTimelineParams.MediaOnlyFocused -> DateDividerMode.MONTHLY
val mode = when { is CreateTimelineParams.Focused,
onlyPinnedEvents -> Timeline.Mode.PINNED_EVENTS CreateTimelineParams.PinnedOnly -> DateDividerMode.DAILY
focusedOnEventId != null -> Timeline.Mode.FOCUSED_ON_EVENT
onlyMedia -> Timeline.Mode.MEDIA
else -> Timeline.Mode.LIVE
} }
runCatching { runCatching {
innerRoom.timelineWithConfiguration( innerRoom.timelineWithConfiguration(
@ -273,13 +269,20 @@ class RustMatrixRoom(
dateDividerMode = dateDividerMode, dateDividerMode = dateDividerMode,
) )
).let { inner -> ).let { inner ->
val mode = when (createTimelineParams) {
is CreateTimelineParams.Focused -> Timeline.Mode.FOCUSED_ON_EVENT
is CreateTimelineParams.MediaOnly -> Timeline.Mode.MEDIA
is CreateTimelineParams.MediaOnlyFocused -> Timeline.Mode.FOCUSED_ON_EVENT
CreateTimelineParams.PinnedOnly -> Timeline.Mode.PINNED_EVENTS
}
createTimeline(inner, mode = mode) createTimeline(inner, mode = mode)
} }
}.mapFailure { }.mapFailure {
if (focusedOnEventId != null) { when (createTimelineParams) {
it.toFocusEventException() is CreateTimelineParams.Focused,
} else { is CreateTimelineParams.MediaOnlyFocused -> it.toFocusEventException()
it CreateTimelineParams.MediaOnly,
CreateTimelineParams.PinnedOnly -> it
} }
}.onFailure { }.onFailure {
if (it is CancellationException) { if (it is CancellationException) {
@ -604,6 +607,7 @@ class RustMatrixRoom(
override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = withContext(roomDispatcher) { override suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit> = withContext(roomDispatcher) {
runCatching { runCatching {
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason) innerRoom.reportContent(eventId = eventId.value, score = null, reason = reason)
if (blockUserId != null) { if (blockUserId != null) {
innerRoom.ignoreUser(blockUserId.value) innerRoom.ignoreUser(blockUserId.value)

View file

@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.IntentionalMention
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@ -138,7 +139,7 @@ class FakeMatrixRoom(
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() }, private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
private val updateMembersResult: () -> Unit = { lambdaError() }, private val updateMembersResult: () -> Unit = { lambdaError() },
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() }, private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
private val createTimelineResult: (EventId?, Boolean, Boolean) -> Result<Timeline> = { _, _, _ -> lambdaError() }, private val createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> }, private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> },
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) }, private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) }, private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
@ -219,11 +220,9 @@ class FakeMatrixRoom(
} }
override suspend fun createTimeline( override suspend fun createTimeline(
focusedOnEventId: EventId?, createTimelineParams: CreateTimelineParams,
onlyPinnedEvents: Boolean,
onlyMedia: Boolean,
): Result<Timeline> = simulateLongTask { ): Result<Timeline> = simulateLongTask {
createTimelineResult(focusedOnEventId, onlyPinnedEvents, onlyMedia) createTimelineResult(createTimelineParams)
} }
override suspend fun subscribeToSync() { override suspend fun subscribeToSync() {

View file

@ -12,6 +12,7 @@ import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems import io.element.android.libraries.mediaviewer.impl.model.GroupedMediaItems
@ -44,7 +45,7 @@ class LiveMediaTimeline @Inject constructor(
override suspend fun getTimeline(): Result<Timeline> = mutex.withLock { override suspend fun getTimeline(): Result<Timeline> = mutex.withLock {
val currentTimeline = timeline val currentTimeline = timeline
if (currentTimeline == null) { if (currentTimeline == null) {
room.createTimeline(onlyMedia = true) room.createTimeline(CreateTimelineParams.MediaOnly)
.onSuccess { timeline = it } .onSuccess { timeline = it }
} else { } else {
Result.success(currentTimeline) Result.success(currentTimeline)
@ -68,9 +69,11 @@ class FocusedMediaTimeline(
) : MediaTimeline { ) : MediaTimeline {
override suspend fun getTimeline(): Result<Timeline> { override suspend fun getTimeline(): Result<Timeline> {
return room.createTimeline( return room.createTimeline(
focusedOnEventId = eventId, createTimelineParams = if (onlyPinnedEvents) {
onlyPinnedEvents = onlyPinnedEvents, CreateTimelineParams.PinnedOnly
onlyMedia = true, } else {
CreateTimelineParams.MediaOnlyFocused(eventId)
},
) )
} }

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.datasource
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID
@ -75,7 +76,7 @@ class FocusedMediaTimelineTest {
@Test @Test
fun `getTimeline returns the timeline provided by the room`() = runTest { fun `getTimeline returns the timeline provided by the room`() = runTest {
val createTimelineResult = lambdaRecorder<EventId?, Boolean, Boolean, Result<Timeline>> { _, _, _ -> val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline()) Result.success(FakeTimeline())
} }
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
@ -87,13 +88,12 @@ class FocusedMediaTimelineTest {
) )
val timeline = sut.getTimeline() val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue() assertThat(timeline.isSuccess).isTrue()
createTimelineResult.assertions().isCalledOnce() createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.MediaOnlyFocused(AN_EVENT_ID)))
.with(value(AN_EVENT_ID), value(false), value(true))
} }
@Test @Test
fun `getTimeline returns the timeline provided by the room for pinned Events`() = runTest { fun `getTimeline returns the timeline provided by the room for pinned Events`() = runTest {
val createTimelineResult = lambdaRecorder<EventId?, Boolean, Boolean, Result<Timeline>> { _, _, _ -> val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline()) Result.success(FakeTimeline())
} }
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
@ -106,8 +106,7 @@ class FocusedMediaTimelineTest {
) )
val timeline = sut.getTimeline() val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue() assertThat(timeline.isSuccess).isTrue()
createTimelineResult.assertions().isCalledOnce() createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.PinnedOnly))
.with(value(AN_EVENT_ID), value(true), value(true))
} }
private fun createFocusedMediaTimeline( private fun createFocusedMediaTimeline(

View file

@ -8,7 +8,7 @@
package io.element.android.libraries.mediaviewer.impl.datasource package io.element.android.libraries.mediaviewer.impl.datasource
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.CreateTimelineParams
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@ -28,7 +28,7 @@ class LiveMediaTimelineTest {
@Test @Test
fun `getTimeline returns the timeline provided by the room, then from cache`() = runTest { fun `getTimeline returns the timeline provided by the room, then from cache`() = runTest {
val createTimelineResult = lambdaRecorder<EventId?, Boolean, Boolean, Result<Timeline>> { _, _, _ -> val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline()) Result.success(FakeTimeline())
} }
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
@ -39,8 +39,7 @@ class LiveMediaTimelineTest {
) )
val timeline = sut.getTimeline() val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue() assertThat(timeline.isSuccess).isTrue()
createTimelineResult.assertions().isCalledOnce() createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.MediaOnly))
.with(value(null), value(false), value(true))
val timeline2 = sut.getTimeline() val timeline2 = sut.getTimeline()
assertThat(timeline2.isSuccess).isTrue() assertThat(timeline2.isSuccess).isTrue()
// No called another time // No called another time

View file

@ -57,7 +57,7 @@ class TimelineMediaGalleryDataSourceTest {
val fakeTimeline = FakeTimeline() val fakeTimeline = FakeTimeline()
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(fakeTimeline) }, createTimelineResult = { Result.success(fakeTimeline) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )
@ -75,7 +75,7 @@ class TimelineMediaGalleryDataSourceTest {
runTest { runTest {
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(fakeTimeline) }, createTimelineResult = { Result.success(fakeTimeline) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )
@ -112,7 +112,7 @@ class TimelineMediaGalleryDataSourceTest {
} }
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(fakeTimeline) }, createTimelineResult = { Result.success(fakeTimeline) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )
@ -135,7 +135,7 @@ class TimelineMediaGalleryDataSourceTest {
} }
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(fakeTimeline) }, createTimelineResult = { Result.success(fakeTimeline) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )
@ -154,7 +154,7 @@ class TimelineMediaGalleryDataSourceTest {
fun `test - failing to load timeline should emit an error`() = runTest { fun `test - failing to load timeline should emit an error`() = runTest {
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.failure(AN_EXCEPTION) }, createTimelineResult = { Result.failure(AN_EXCEPTION) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )
@ -176,7 +176,7 @@ class TimelineMediaGalleryDataSourceTest {
) )
val sut = createTimelineMediaGalleryDataSource( val sut = createTimelineMediaGalleryDataSource(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(fakeTimeline) }, createTimelineResult = { Result.success(fakeTimeline) },
roomCoroutineScope = backgroundScope, roomCoroutineScope = backgroundScope,
) )
) )

View file

@ -52,7 +52,7 @@ class MediaGalleryPresenterTest {
), ),
room = FakeMatrixRoom( room = FakeMatrixRoom(
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
) )
) )
presenter.test { presenter.test {
@ -71,7 +71,7 @@ class MediaGalleryPresenterTest {
val presenter = createMediaGalleryPresenter( val presenter = createMediaGalleryPresenter(
room = FakeMatrixRoom( room = FakeMatrixRoom(
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
) )
) )
presenter.test { presenter.test {
@ -101,7 +101,7 @@ class MediaGalleryPresenterTest {
room = FakeMatrixRoom( room = FakeMatrixRoom(
sessionId = A_USER_ID, sessionId = A_USER_ID,
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
canRedactOwnResult = { Result.success(canDeleteOwn) } canRedactOwnResult = { Result.success(canDeleteOwn) }
) )
) )
@ -144,7 +144,7 @@ class MediaGalleryPresenterTest {
room = FakeMatrixRoom( room = FakeMatrixRoom(
sessionId = A_USER_ID, sessionId = A_USER_ID,
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
canRedactOtherResult = { Result.success(canDeleteOther) } canRedactOtherResult = { Result.success(canDeleteOther) }
) )
) )
@ -177,7 +177,7 @@ class MediaGalleryPresenterTest {
val presenter = createMediaGalleryPresenter( val presenter = createMediaGalleryPresenter(
room = FakeMatrixRoom( room = FakeMatrixRoom(
displayName = A_ROOM_NAME, displayName = A_ROOM_NAME,
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
) )
) )
presenter.test { presenter.test {
@ -244,7 +244,7 @@ class MediaGalleryPresenterTest {
) )
val presenter = createMediaGalleryPresenter( val presenter = createMediaGalleryPresenter(
room = FakeMatrixRoom( room = FakeMatrixRoom(
createTimelineResult = { _, _, _ -> Result.success(FakeTimeline()) }, createTimelineResult = { Result.success(FakeTimeline()) },
), ),
navigator = navigator, navigator = navigator,
) )