Merge pull request #4274 from element-hq/feature/bma/mediaTimelineImprovment

Update Matrix Room API and allow media swipe on pinned event only.
This commit is contained in:
Benoit Marty 2025-02-19 09:41:27 +01:00 committed by GitHub
commit 86afffb4bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 296 additions and 185 deletions

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

@ -109,21 +109,12 @@ interface MatrixRoom : Closeable {
val liveTimeline: Timeline
/**
* Create a new timeline, focused on the provided Event.
* Should not be used directly, see `TimelineController` to manage the various timelines.
* Create a new timeline.
* @param createTimelineParams contains parameters about how to filter the timeline. Will also configure the date separators.
*/
suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline>
/**
* Create a new timeline for the pinned events of the room.
*/
suspend fun pinnedEventsTimeline(): Result<Timeline>
/**
* Create a new timeline for the media events of the room.
* @param eventId The event to focus on, if any.
*/
suspend fun mediaTimeline(eventId: EventId?): Result<Timeline>
suspend fun createTimeline(
createTimelineParams: CreateTimelineParams,
): Result<Timeline>
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.notificationsettings.NotificationSettingsService
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.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@ -214,80 +215,81 @@ class RustMatrixRoom(
override suspend fun subscribeToSync() = roomSyncSubscriber.subscribe(roomId)
override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> = withContext(roomDispatcher) {
runCatching {
innerRoom.timelineWithConfiguration(
configuration = TimelineConfiguration(
focus = TimelineFocus.Event(
eventId = eventId.value,
numContextEvents = 50u,
),
allowedMessageTypes = AllowedMessageTypes.All,
internalIdPrefix = "focus_$eventId",
dateDividerMode = DateDividerMode.DAILY,
)
).let { inner ->
createTimeline(inner, mode = Timeline.Mode.FOCUSED_ON_EVENT)
}
}.mapFailure {
it.toFocusEventException()
}.onFailure {
if (it is CancellationException) {
throw it
}
}
}
override suspend fun pinnedEventsTimeline(): Result<Timeline> = withContext(roomDispatcher) {
runCatching {
innerRoom.timelineWithConfiguration(
configuration = TimelineConfiguration(
focus = TimelineFocus.PinnedEvents(
maxEventsToLoad = 100u,
maxConcurrentRequests = 10u,
),
allowedMessageTypes = AllowedMessageTypes.All,
internalIdPrefix = "pinned_events",
dateDividerMode = DateDividerMode.DAILY,
)
).let { inner ->
createTimeline(inner, mode = Timeline.Mode.PINNED_EVENTS)
}
}.onFailure {
if (it is CancellationException) {
throw it
}
}
}
override suspend fun mediaTimeline(
eventId: EventId?,
override suspend fun createTimeline(
createTimelineParams: CreateTimelineParams,
): Result<Timeline> = withContext(roomDispatcher) {
val focus = if (eventId != null) {
TimelineFocus.Event(
eventId = eventId.value,
val focus = when (createTimelineParams) {
is CreateTimelineParams.PinnedOnly -> TimelineFocus.PinnedEvents(
maxEventsToLoad = 100u,
maxConcurrentRequests = 10u,
)
is CreateTimelineParams.MediaOnly -> TimelineFocus.Live
is CreateTimelineParams.Focused -> TimelineFocus.Event(
eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u,
)
is CreateTimelineParams.MediaOnlyFocused -> TimelineFocus.Event(
eventId = createTimelineParams.focusedEventId.value,
numContextEvents = 50u,
)
} else {
TimelineFocus.Live
}
val allowedMessageTypes = when (createTimelineParams) {
is CreateTimelineParams.MediaOnly,
is CreateTimelineParams.MediaOnlyFocused -> AllowedMessageTypes.Only(
types = listOf(
RoomMessageEventMessageType.FILE,
RoomMessageEventMessageType.IMAGE,
RoomMessageEventMessageType.VIDEO,
RoomMessageEventMessageType.AUDIO,
)
)
is CreateTimelineParams.Focused,
CreateTimelineParams.PinnedOnly -> AllowedMessageTypes.All
}
val internalIdPrefix = when (createTimelineParams) {
is CreateTimelineParams.PinnedOnly -> "pinned_events"
is CreateTimelineParams.Focused -> "focus_${createTimelineParams.focusedEventId}"
is CreateTimelineParams.MediaOnly -> "MediaGallery_"
is CreateTimelineParams.MediaOnlyFocused -> "MediaGallery_${createTimelineParams.focusedEventId}"
}
// Note that for TimelineFilter.MediaOnlyFocused, the date separator will be filtered out,
// but there is no way to exclude data separator at the moment.
val dateDividerMode = when (createTimelineParams) {
is CreateTimelineParams.MediaOnly,
is CreateTimelineParams.MediaOnlyFocused -> DateDividerMode.MONTHLY
is CreateTimelineParams.Focused,
CreateTimelineParams.PinnedOnly -> DateDividerMode.DAILY
}
runCatching {
innerRoom.timelineWithConfiguration(
configuration = TimelineConfiguration(
focus = focus,
allowedMessageTypes = AllowedMessageTypes.Only(
types = listOf(
RoomMessageEventMessageType.FILE,
RoomMessageEventMessageType.IMAGE,
RoomMessageEventMessageType.VIDEO,
RoomMessageEventMessageType.AUDIO,
)
),
internalIdPrefix = "MediaGallery_",
dateDividerMode = DateDividerMode.MONTHLY,
allowedMessageTypes = allowedMessageTypes,
internalIdPrefix = internalIdPrefix,
dateDividerMode = dateDividerMode,
)
).let { inner ->
createTimeline(inner, mode = if (eventId != null) Timeline.Mode.FOCUSED_ON_EVENT else Timeline.Mode.MEDIA)
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(
timeline = inner,
mode = mode,
)
}
}.mapFailure {
when (createTimelineParams) {
is CreateTimelineParams.Focused,
is CreateTimelineParams.MediaOnlyFocused -> it.toFocusEventException()
CreateTimelineParams.MediaOnly,
CreateTimelineParams.PinnedOnly -> it
}
}.onFailure {
if (it is CancellationException) {

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.notificationsettings.NotificationSettingsService
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.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
@ -138,9 +139,7 @@ class FakeMatrixRoom(
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
private val updateMembersResult: () -> Unit = { lambdaError() },
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
private val timelineFocusedOnEventResult: (EventId) -> Result<Timeline> = { lambdaError() },
private val pinnedEventsTimelineResult: () -> Result<Timeline> = { lambdaError() },
private val mediaTimelineResult: (EventId?) -> Result<Timeline> = { lambdaError() },
private val createTimelineResult: (CreateTimelineParams) -> Result<Timeline> = { lambdaError() },
private val setSendQueueEnabledLambda: (Boolean) -> Unit = { _: Boolean -> },
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
@ -220,16 +219,10 @@ class FakeMatrixRoom(
_syncUpdateFlow.tryEmit(_syncUpdateFlow.value + 1)
}
override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> = simulateLongTask {
timelineFocusedOnEventResult(eventId)
}
override suspend fun pinnedEventsTimeline(): Result<Timeline> = simulateLongTask {
pinnedEventsTimelineResult()
}
override suspend fun mediaTimeline(eventId: EventId?): Result<Timeline> = simulateLongTask {
mediaTimelineResult(eventId)
override suspend fun createTimeline(
createTimelineParams: CreateTimelineParams,
): Result<Timeline> = simulateLongTask {
createTimelineResult(createTimelineParams)
}
override suspend fun subscribeToSync() {

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.mediaviewer.api
import android.os.Parcelable
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
@ -14,6 +15,8 @@ import io.element.android.libraries.architecture.FeatureEntryPoint
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.timeline.Timeline
import kotlinx.parcelize.Parcelize
interface MediaViewerEntryPoint : FeatureEntryPoint {
fun nodeBuilder(parentNode: Node, buildContext: BuildContext): NodeBuilder
@ -39,9 +42,14 @@ interface MediaViewerEntryPoint : FeatureEntryPoint {
val canShowInfo: Boolean,
) : NodeInputs
enum class MediaViewerMode {
SingleMedia,
TimelineImagesAndVideos,
TimelineFilesAndAudios,
sealed interface MediaViewerMode : Parcelable {
@Parcelize
data object SingleMedia : MediaViewerMode
@Parcelize
data class TimelineImagesAndVideos(val timelineMode: Timeline.Mode) : MediaViewerMode
@Parcelize
data class TimelineFilesAndAudios(val timelineMode: Timeline.Mode) : MediaViewerMode
}
}

View file

@ -18,6 +18,7 @@ interface FocusedTimelineMediaGalleryDataSourceFactory {
fun createFor(
eventId: EventId,
mediaItem: MediaItem.Event,
onlyPinnedEvents: Boolean,
): MediaGalleryDataSource
}
@ -30,6 +31,7 @@ class DefaultFocusedTimelineMediaGalleryDataSourceFactory @Inject constructor(
override fun createFor(
eventId: EventId,
mediaItem: MediaItem.Event,
onlyPinnedEvents: Boolean,
): MediaGalleryDataSource {
return TimelineMediaGalleryDataSource(
room = room,
@ -37,6 +39,7 @@ class DefaultFocusedTimelineMediaGalleryDataSourceFactory @Inject constructor(
room = room,
eventId = eventId,
initialMediaItem = mediaItem,
onlyPinnedEvents = onlyPinnedEvents,
),
timelineMediaItemsFactory = timelineMediaItemsFactory,
mediaItemsPostProcessor = mediaItemsPostProcessor,

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.matrix.api.core.EventId
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.timeline.Timeline
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 {
val currentTimeline = timeline
if (currentTimeline == null) {
room.mediaTimeline(null)
room.createTimeline(CreateTimelineParams.MediaOnly)
.onSuccess { timeline = it }
} else {
Result.success(currentTimeline)
@ -58,14 +59,22 @@ class LiveMediaTimeline @Inject constructor(
/**
* A class that will provide a media timeline that is focused on a particular event.
* Optionally, the timeline will only contain the pinned events.
*/
class FocusedMediaTimeline(
private val room: MatrixRoom,
private val eventId: EventId,
private val onlyPinnedEvents: Boolean,
initialMediaItem: MediaItem.Event,
) : MediaTimeline {
override suspend fun getTimeline(): Result<Timeline> {
return room.mediaTimeline(eventId)
return room.createTimeline(
createTimelineParams = if (onlyPinnedEvents) {
CreateTimelineParams.PinnedOnly
} else {
CreateTimelineParams.MediaOnlyFocused(eventId)
},
)
}
override val cache = persistentListOf(

View file

@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.overlay.operation.show
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.mediaviewer.api.MediaGalleryEntryPoint
import io.element.android.libraries.mediaviewer.api.MediaInfo
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
@ -96,9 +97,9 @@ class MediaGalleryRootNode @AssistedInject constructor(
val mode = when (item) {
is MediaItem.Audio,
is MediaItem.Voice,
is MediaItem.File -> MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios
is MediaItem.File -> MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(Timeline.Mode.MEDIA)
is MediaItem.Image,
is MediaItem.Video -> MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos
is MediaItem.Video -> MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(Timeline.Mode.MEDIA)
}
overlay.show(
NavTarget.MediaViewer(

View file

@ -52,8 +52,8 @@ class MediaViewerDataSource(
private val galleryMode = when (mode) {
MediaViewerMode.SingleMedia,
MediaViewerMode.TimelineImagesAndVideos -> MediaGalleryMode.Images
MediaViewerMode.TimelineFilesAndAudios -> MediaGalleryMode.Files
is MediaViewerMode.TimelineImagesAndVideos -> MediaGalleryMode.Images
is MediaViewerMode.TimelineFilesAndAudios -> MediaGalleryMode.Files
}
// Map of sourceUrl to local media state

View file

@ -22,6 +22,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory
import io.element.android.libraries.mediaviewer.impl.datasource.FocusedTimelineMediaGalleryDataSourceFactory
@ -69,16 +70,40 @@ class MediaViewerNode @AssistedInject constructor(
// Should not happen
timelineMediaGalleryDataSource
} else {
// Does timelineMediaGalleryDataSource knows the eventId?
val lastData = timelineMediaGalleryDataSource.getLastData().dataOrNull()
val isEventKnown = lastData?.hasEvent(eventId) == true
if (isEventKnown) {
timelineMediaGalleryDataSource
} else {
focusedTimelineMediaGalleryDataSourceFactory.createFor(
eventId = eventId,
mediaItem = inputs.toMediaItem(),
)
// Can we use a specific timeline?
val timelineMode = when (val mode = inputs.mode) {
is MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> mode.timelineMode
is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> mode.timelineMode
else -> null
}
when (timelineMode) {
null -> timelineMediaGalleryDataSource
Timeline.Mode.LIVE -> {
// Even if the timelineMediaGalleryDataSource does not know the eventId, the SDK will create the timeline faster
timelineMediaGalleryDataSource
}
Timeline.Mode.FOCUSED_ON_EVENT -> {
// Does timelineMediaGalleryDataSource knows the eventId?
val lastData = timelineMediaGalleryDataSource.getLastData().dataOrNull()
val isEventKnown = lastData?.hasEvent(eventId) == true
if (isEventKnown) {
timelineMediaGalleryDataSource
} else {
focusedTimelineMediaGalleryDataSourceFactory.createFor(
eventId = eventId,
mediaItem = inputs.toMediaItem(),
onlyPinnedEvents = false,
)
}
}
Timeline.Mode.PINNED_EVENTS -> {
focusedTimelineMediaGalleryDataSourceFactory.createFor(
eventId = eventId,
mediaItem = inputs.toMediaItem(),
onlyPinnedEvents = true,
)
}
Timeline.Mode.MEDIA -> timelineMediaGalleryDataSource
}
}
}

View file

@ -204,8 +204,8 @@ class MediaViewerPresenter @AssistedInject constructor(
private fun showNoMoreItemsSnackbar() {
val messageResId = when (inputs.mode) {
MediaViewerEntryPoint.MediaViewerMode.SingleMedia,
MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> R.string.screen_media_details_no_more_media_to_show
MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> R.string.screen_media_details_no_more_files_to_show
is MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> R.string.screen_media_details_no_more_media_to_show
is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> R.string.screen_media_details_no_more_files_to_show
}
val message = SnackbarMessage(messageResId)
snackbarDispatcher.post(message)

View file

@ -25,6 +25,7 @@ class DefaultFocusedTimelineMediaGalleryDataSourceFactoryTest {
val result = sut.createFor(
eventId = AN_EVENT_ID,
mediaItem = aMediaItemImage(),
onlyPinnedEvents = false,
)
assertThat(result).isInstanceOf(TimelineMediaGalleryDataSource::class.java)
}

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.mediaviewer.impl.datasource
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.timeline.Timeline
import io.element.android.libraries.matrix.test.AN_EVENT_ID
@ -75,11 +76,11 @@ class FocusedMediaTimelineTest {
@Test
fun `getTimeline returns the timeline provided by the room`() = runTest {
val mediaTimelineResult = lambdaRecorder<EventId?, Result<Timeline>> {
val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline())
}
val room = FakeMatrixRoom(
mediaTimelineResult = mediaTimelineResult,
createTimelineResult = createTimelineResult,
)
val sut = createFocusedMediaTimeline(
room = room,
@ -87,16 +88,36 @@ class FocusedMediaTimelineTest {
)
val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue()
mediaTimelineResult.assertions().isCalledOnce().with(value(AN_EVENT_ID))
createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.MediaOnlyFocused(AN_EVENT_ID)))
}
@Test
fun `getTimeline returns the timeline provided by the room for pinned Events`() = runTest {
val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline())
}
val room = FakeMatrixRoom(
createTimelineResult = createTimelineResult,
)
val sut = createFocusedMediaTimeline(
room = room,
eventId = AN_EVENT_ID,
onlyPinnedEvent = true,
)
val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue()
createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.PinnedOnly))
}
private fun createFocusedMediaTimeline(
room: MatrixRoom = FakeMatrixRoom(),
eventId: EventId = AN_EVENT_ID,
initialMediaItem: MediaItem.Event = aMediaItemImage(),
onlyPinnedEvent: Boolean = false,
) = FocusedMediaTimeline(
room = room,
eventId = eventId,
initialMediaItem = initialMediaItem,
onlyPinnedEvents = onlyPinnedEvent,
)
}

View file

@ -8,7 +8,7 @@
package io.element.android.libraries.mediaviewer.impl.datasource
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.timeline.Timeline
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@ -28,22 +28,22 @@ class LiveMediaTimelineTest {
@Test
fun `getTimeline returns the timeline provided by the room, then from cache`() = runTest {
val mediaTimelineResult = lambdaRecorder<EventId?, Result<Timeline>> {
val createTimelineResult = lambdaRecorder<CreateTimelineParams, Result<Timeline>> {
Result.success(FakeTimeline())
}
val room = FakeMatrixRoom(
mediaTimelineResult = mediaTimelineResult,
createTimelineResult = createTimelineResult,
)
val sut = createLiveMediaTimeline(
room = room,
)
val timeline = sut.getTimeline()
assertThat(timeline.isSuccess).isTrue()
mediaTimelineResult.assertions().isCalledOnce().with(value(null))
createTimelineResult.assertions().isCalledOnce().with(value(CreateTimelineParams.MediaOnly))
val timeline2 = sut.getTimeline()
assertThat(timeline2.isSuccess).isTrue()
// No called another time
mediaTimelineResult.assertions().isCalledOnce()
createTimelineResult.assertions().isCalledOnce()
}
private fun createLiveMediaTimeline(

View file

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

View file

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

View file

@ -137,7 +137,7 @@ class MediaViewerDataSourceTest {
fun `test dataFlow with data galleryMode image`() = runTest {
val galleryDataSource = FakeMediaGalleryDataSource()
val sut = createMediaViewerDataSource(
mode = MediaViewerMode.TimelineImagesAndVideos,
mode = MediaViewerMode.TimelineImagesAndVideos(timelineMode = Timeline.Mode.MEDIA),
galleryDataSource = galleryDataSource,
)
sut.dataFlow().test {
@ -159,7 +159,7 @@ class MediaViewerDataSourceTest {
fun `test dataFlow with data galleryMode files`() = runTest {
val galleryDataSource = FakeMediaGalleryDataSource()
val sut = createMediaViewerDataSource(
mode = MediaViewerMode.TimelineFilesAndAudios,
mode = MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.MEDIA),
galleryDataSource = galleryDataSource,
)
sut.dataFlow().test {
@ -265,7 +265,7 @@ class MediaViewerDataSourceTest {
}
private fun TestScope.createMediaViewerDataSource(
mode: MediaViewerMode = MediaViewerMode.TimelineImagesAndVideos,
mode: MediaViewerMode = MediaViewerMode.TimelineImagesAndVideos(timelineMode = Timeline.Mode.MEDIA),
galleryDataSource: MediaGalleryDataSource = FakeMediaGalleryDataSource(),
mediaLoader: MatrixMediaLoader = FakeMatrixMediaLoader(),
localMediaFactory: LocalMediaFactory = FakeLocalMediaFactory(mockMediaUrl),

View file

@ -519,7 +519,7 @@ class MediaViewerPresenterTest {
@Test
fun `present - snackbar displayed when there is no more items forward images and videos`() {
`present - snackbar displayed when there is no more items forward`(
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos,
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(timelineMode = Timeline.Mode.MEDIA),
expectedSnackbarResId = R.string.screen_media_details_no_more_media_to_show,
)
}
@ -527,7 +527,7 @@ class MediaViewerPresenterTest {
@Test
fun `present - snackbar displayed when there is no more items forward files and audio`() {
`present - snackbar displayed when there is no more items forward`(
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios,
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.MEDIA),
expectedSnackbarResId = R.string.screen_media_details_no_more_files_to_show,
)
}
@ -547,7 +547,7 @@ class MediaViewerPresenterTest {
awaitFirstItem()
mediaGalleryDataSource.emitGroupedMediaItems(
AsyncData.Success(
if (mode == MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
GroupedMediaItems(
imageAndVideoItems = persistentListOf(),
fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator),
@ -568,7 +568,7 @@ class MediaViewerPresenterTest {
// data source claims that there is no more items to load forward
mediaGalleryDataSource.emitGroupedMediaItems(
AsyncData.Success(
if (mode == MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
GroupedMediaItems(
imageAndVideoItems = persistentListOf(),
fileItems = persistentListOf(anImage, aBackwardLoadingIndicator),
@ -590,7 +590,7 @@ class MediaViewerPresenterTest {
@Test
fun `present - snackbar displayed when there is no more items backward images and videos`() {
`present - snackbar displayed when there is no more items backward`(
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos,
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos(timelineMode = Timeline.Mode.MEDIA),
expectedSnackbarResId = R.string.screen_media_details_no_more_media_to_show,
)
}
@ -598,7 +598,7 @@ class MediaViewerPresenterTest {
@Test
fun `present - snackbar displayed when there is no more items backward files and audio`() {
`present - snackbar displayed when there is no more items backward`(
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios,
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.MEDIA),
expectedSnackbarResId = R.string.screen_media_details_no_more_files_to_show,
)
}
@ -618,7 +618,7 @@ class MediaViewerPresenterTest {
awaitFirstItem()
mediaGalleryDataSource.emitGroupedMediaItems(
AsyncData.Success(
if (mode == MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
GroupedMediaItems(
imageAndVideoItems = persistentListOf(),
fileItems = persistentListOf(aForwardLoadingIndicator, anImage, aBackwardLoadingIndicator),
@ -640,7 +640,7 @@ class MediaViewerPresenterTest {
// data source claims that there is no more items to load backward
mediaGalleryDataSource.emitGroupedMediaItems(
AsyncData.Success(
if (mode == MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
if (mode is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios) {
GroupedMediaItems(
imageAndVideoItems = persistentListOf(),
fileItems = persistentListOf(aForwardLoadingIndicator, anImage),