Merge pull request #5669 from element-hq/fix/forward-events-from-pinned-media-timeline

Fix forward events from media viewer from pinned media timeline
This commit is contained in:
Benoit Marty 2025-11-04 09:52:01 +01:00 committed by GitHub
commit 32b1856dbd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 148 additions and 71 deletions

View file

@ -23,6 +23,6 @@ interface MediaGalleryEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun onBackClick()
fun viewInTimeline(eventId: EventId)
fun forward(eventId: EventId)
fun forward(eventId: EventId, fromPinnedEvents: Boolean)
}
}

View file

@ -31,7 +31,7 @@ interface MediaViewerEntryPoint : FeatureEntryPoint {
interface Callback : Plugin {
fun onDone()
fun viewInTimeline(eventId: EventId)
fun forwardEvent(eventId: EventId)
fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean)
}
data class Params(

View file

@ -85,7 +85,7 @@ class MediaGalleryFlowNode(
}
override fun forward(eventId: EventId) {
callback.forward(eventId)
callback.forward(eventId, fromPinnedEvents = false)
}
override fun showItem(item: MediaItem.Event) {
@ -119,9 +119,9 @@ class MediaGalleryFlowNode(
callback.viewInTimeline(eventId)
}
override fun forwardEvent(eventId: EventId) {
override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) {
// Need to go to the parent because of the overlay
callback.forward(eventId)
callback.forward(eventId, fromPinnedEvents)
}
}
mediaViewerEntryPoint.createNode(

View file

@ -11,6 +11,6 @@ import io.element.android.libraries.matrix.api.core.EventId
interface MediaViewerNavigator {
fun onViewInTimelineClick(eventId: EventId)
fun onForwardClick(eventId: EventId)
fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean)
fun onItemDeleted()
}

View file

@ -64,8 +64,8 @@ class MediaViewerNode(
callback.viewInTimeline(eventId)
}
override fun onForwardClick(eventId: EventId) {
callback.forwardEvent(eventId)
override fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean) {
callback.forwardEvent(eventId, fromPinnedEvents)
}
override fun onItemDeleted() {
@ -81,11 +81,7 @@ class MediaViewerNode(
timelineMediaGalleryDataSource
} else {
// 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
}
val timelineMode = inputs.mode.getTimelineMode()
when (timelineMode) {
null -> timelineMediaGalleryDataSource
Timeline.Mode.Live,
@ -149,3 +145,11 @@ class MediaViewerNode(
}
}
}
internal fun MediaViewerEntryPoint.MediaViewerMode.getTimelineMode(): Timeline.Mode? {
return when (this) {
is MediaViewerEntryPoint.MediaViewerMode.TimelineImagesAndVideos -> timelineMode
is MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios -> timelineMode
else -> null
}
}

View file

@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
@ -119,7 +120,10 @@ class MediaViewerPresenter(
}
is MediaViewerEvents.Forward -> {
mediaBottomSheetState = MediaBottomSheetState.Hidden
navigator.onForwardClick(event.eventId)
navigator.onForwardClick(
eventId = event.eventId,
fromPinnedEvents = inputs.mode.getTimelineMode() == Timeline.Mode.PinnedEvents,
)
}
is MediaViewerEvents.OpenInfo -> coroutineScope.launch {
mediaBottomSheetState = MediaBottomSheetState.MediaDetailsBottomSheetState(

View file

@ -27,6 +27,7 @@ class SingleMediaGalleryDataSource(
override fun start() = Unit
override fun groupedMediaItemsFlow() = flowOf(AsyncData.Success(data))
override fun getLastData(): AsyncData<GroupedMediaItems> = AsyncData.Success(data)
override suspend fun loadMore(direction: Timeline.PaginationDirection) = Unit
override suspend fun deleteItem(eventId: EventId) = Unit

View file

@ -40,7 +40,7 @@ class DefaultMediaGalleryEntryPointTest {
val callback = object : MediaGalleryEntryPoint.Callback {
override fun onBackClick() = lambdaError()
override fun viewInTimeline(eventId: EventId) = lambdaError()
override fun forward(eventId: EventId) = lambdaError()
override fun forward(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError()
}
val result = entryPoint.createNode(
parentNode = parentNode,

View file

@ -72,7 +72,7 @@ class DefaultMediaViewerEntryPointTest {
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun viewInTimeline(eventId: EventId) = lambdaError()
override fun forwardEvent(eventId: EventId) = lambdaError()
override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError()
}
val params = createMediaViewerEntryPointParams()
val result = entryPoint.createNode(
@ -118,7 +118,7 @@ class DefaultMediaViewerEntryPointTest {
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() = lambdaError()
override fun viewInTimeline(eventId: EventId) = lambdaError()
override fun forwardEvent(eventId: EventId) = lambdaError()
override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) = lambdaError()
}
val params = entryPoint.createParamsForAvatar(
filename = "fn",

View file

@ -12,15 +12,15 @@ import io.element.android.tests.testutils.lambda.lambdaError
class FakeMediaViewerNavigator(
private val onViewInTimelineClickLambda: (EventId) -> Unit = { lambdaError() },
private val onForwardClickLambda: (EventId) -> Unit = { lambdaError() },
private val onForwardClickLambda: (EventId, Boolean) -> Unit = { _, _ -> lambdaError() },
private val onItemDeletedLambda: () -> Unit = { lambdaError() },
) : MediaViewerNavigator {
override fun onViewInTimelineClick(eventId: EventId) {
onViewInTimelineClickLambda(eventId)
}
override fun onForwardClick(eventId: EventId) {
onForwardClickLambda(eventId)
override fun onForwardClick(eventId: EventId, fromPinnedEvents: Boolean) {
onForwardClickLambda(eventId, fromPinnedEvents)
}
override fun onItemDeleted() {

View file

@ -785,7 +785,7 @@ class MediaViewerPresenterTest {
@Test
fun `present - forward hides the bottom sheet and invokes the navigator`() = runTest {
val onForwardClickLambda = lambdaRecorder<EventId, Unit> { }
val onForwardClickLambda = lambdaRecorder<EventId, Boolean, Unit> { _, _ -> }
val navigator = FakeMediaViewerNavigator(
onForwardClickLambda = onForwardClickLambda,
)
@ -804,7 +804,35 @@ class MediaViewerPresenterTest {
initialState.eventSink(MediaViewerEvents.Forward(AN_EVENT_ID))
val finalState = awaitItem()
assertThat(finalState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden)
onForwardClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID))
onForwardClickLambda.assertions().isCalledOnce()
.with(value(AN_EVENT_ID), value(false))
}
}
@Test
fun `present - forward from pinned events hides the bottom sheet and invokes the navigator`() = runTest {
val onForwardClickLambda = lambdaRecorder<EventId, Boolean, Unit> { _, _ -> }
val navigator = FakeMediaViewerNavigator(
onForwardClickLambda = onForwardClickLambda,
)
val presenter = createMediaViewerPresenter(
mode = MediaViewerEntryPoint.MediaViewerMode.TimelineFilesAndAudios(timelineMode = Timeline.Mode.PinnedEvents),
localMediaFactory = localMediaFactory,
mediaViewerNavigator = navigator,
room = FakeJoinedRoom(
baseRoom = FakeBaseRoom(canRedactOwnResult = { Result.success(true) }),
),
)
presenter.test {
val initialState = awaitItem()
initialState.eventSink(MediaViewerEvents.OpenInfo(aMediaViewerPageData()))
val withBottomSheetState = awaitItem()
assertThat(withBottomSheetState.mediaBottomSheetState).isInstanceOf(MediaBottomSheetState.MediaDetailsBottomSheetState::class.java)
initialState.eventSink(MediaViewerEvents.Forward(AN_EVENT_ID))
val finalState = awaitItem()
assertThat(finalState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden)
onForwardClickLambda.assertions().isCalledOnce()
.with(value(AN_EVENT_ID), value(true))
}
}