From 36eceb75e822c97b2297ce27342862fc9709d700 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 10 Dec 2024 13:34:44 +0100 Subject: [PATCH] Move bottom state to the View state --- .../impl/viewer/MediaViewerEvents.kt | 3 ++ .../impl/viewer/MediaViewerPresenter.kt | 43 +++++++++++++------ .../impl/viewer/MediaViewerState.kt | 3 +- .../impl/viewer/MediaViewerStateProvider.kt | 12 +++++- .../impl/viewer/MediaViewerView.kt | 22 +++------- .../impl/viewer/MediaViewerPresenterTest.kt | 13 +++--- 6 files changed, 58 insertions(+), 38 deletions(-) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt index 5b85cd2b9f..6d9a31a816 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerEvents.kt @@ -16,5 +16,8 @@ sealed interface MediaViewerEvents { data object RetryLoading : MediaViewerEvents data object ClearLoadingError : MediaViewerEvents data class ViewInTimeline(val eventId: EventId) : MediaViewerEvents + data object OpenInfo : MediaViewerEvents + data class ConfirmDelete(val eventId: EventId) : MediaViewerEvents + data object CloseBottomSheet : MediaViewerEvents data class Delete(val eventId: EventId) : MediaViewerEvents } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt index a2201aec65..a480d1ba7c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenter.kt @@ -11,11 +11,9 @@ import android.content.ActivityNotFoundException import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue @@ -37,6 +35,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTran import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.LocalMediaFactory +import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.impl.local.LocalMediaActions import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope @@ -78,15 +77,7 @@ class MediaViewerPresenter @AssistedInject constructor( mediaFile.value?.close() } } - - val syncUpdateFlow = room.syncUpdateFlow.collectAsState() - val canDelete by produceState(false, syncUpdateFlow.value) { - value = when (inputs.mediaInfo.senderId) { - null -> false - room.sessionId -> room.canRedactOwn().getOrElse { false } && inputs.eventId != null - else -> room.canRedactOther().getOrElse { false } && inputs.eventId != null - } - } + var mediaBottomSheetState by remember { mutableStateOf(MediaBottomSheetState.Hidden) } fun handleEvents(mediaViewerEvents: MediaViewerEvents) { when (mediaViewerEvents) { @@ -95,10 +86,36 @@ class MediaViewerPresenter @AssistedInject constructor( MediaViewerEvents.SaveOnDisk -> coroutineScope.saveOnDisk(localMedia.value) MediaViewerEvents.Share -> coroutineScope.share(localMedia.value) MediaViewerEvents.OpenWith -> coroutineScope.open(localMedia.value) - is MediaViewerEvents.Delete -> coroutineScope.delete(mediaViewerEvents.eventId) + is MediaViewerEvents.Delete -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden + coroutineScope.delete(mediaViewerEvents.eventId) + } is MediaViewerEvents.ViewInTimeline -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden navigator.onViewInTimelineClick(mediaViewerEvents.eventId) } + MediaViewerEvents.OpenInfo -> coroutineScope.launch { + mediaBottomSheetState = MediaBottomSheetState.MediaDetailsBottomSheetState( + eventId = inputs.eventId, + canDelete = when (inputs.mediaInfo.senderId) { + null -> false + room.sessionId -> room.canRedactOwn().getOrElse { false } && inputs.eventId != null + else -> room.canRedactOther().getOrElse { false } && inputs.eventId != null + }, + mediaInfo = inputs.mediaInfo, + thumbnailSource = inputs.thumbnailSource, + ) + } + is MediaViewerEvents.ConfirmDelete -> { + mediaBottomSheetState = MediaBottomSheetState.MediaDeleteConfirmationState( + eventId = mediaViewerEvents.eventId, + mediaInfo = inputs.mediaInfo, + thumbnailSource = inputs.thumbnailSource ?: inputs.mediaSource, + ) + } + MediaViewerEvents.CloseBottomSheet -> { + mediaBottomSheetState = MediaBottomSheetState.Hidden + } } } @@ -111,7 +128,7 @@ class MediaViewerPresenter @AssistedInject constructor( canShowInfo = inputs.canShowInfo, canDownload = inputs.canDownload, canShare = inputs.canShare, - canDelete = canDelete, + mediaBottomSheetState = mediaBottomSheetState, eventSink = ::handleEvents ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt index bdd59c5426..6ae8554b06 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerState.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState data class MediaViewerState( val eventId: EventId?, @@ -23,6 +24,6 @@ data class MediaViewerState( val canShowInfo: Boolean, val canDownload: Boolean, val canShare: Boolean, - val canDelete: Boolean, + val mediaBottomSheetState: MediaBottomSheetState, val eventSink: (MediaViewerEvents) -> Unit, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt index 003e079b99..8bed5dfc49 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerStateProvider.kt @@ -17,6 +17,9 @@ import io.element.android.libraries.mediaviewer.api.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.anAudioMediaInfo import io.element.android.libraries.mediaviewer.api.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState +import io.element.android.libraries.mediaviewer.impl.details.aMediaDeleteConfirmationState +import io.element.android.libraries.mediaviewer.impl.details.aMediaDetailsBottomSheetState open class MediaViewerStateProvider : PreviewParameterProvider { override val values: Sequence @@ -91,6 +94,12 @@ open class MediaViewerStateProvider : PreviewParameterProvider canShare = false, ) }, + aMediaViewerState( + mediaBottomSheetState = aMediaDetailsBottomSheetState(), + ), + aMediaViewerState( + mediaBottomSheetState = aMediaDeleteConfirmationState(), + ), ) } @@ -100,6 +109,7 @@ fun aMediaViewerState( canShowInfo: Boolean = true, canDownload: Boolean = true, canShare: Boolean = true, + mediaBottomSheetState: MediaBottomSheetState = MediaBottomSheetState.Hidden, eventSink: (MediaViewerEvents) -> Unit = {}, ) = MediaViewerState( eventId = null, @@ -110,6 +120,6 @@ fun aMediaViewerState( canShowInfo = canShowInfo, canDownload = canDownload, canShare = canShare, - canDelete = true, + mediaBottomSheetState = mediaBottomSheetState, eventSink = eventSink, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index f5fae4d3bb..6ca9ebec9c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -95,7 +95,6 @@ fun MediaViewerView( val defaultBottomPaddingInPixels = if (LocalInspectionMode.current) 303 else 0 var bottomPaddingInPixels by remember { mutableIntStateOf(defaultBottomPaddingInPixels) } BackHandler { onBackClick() } - var mediaBottomSheetState by remember { mutableStateOf(MediaBottomSheetState.Hidden) } Scaffold( modifier, containerColor = Color.Transparent, @@ -128,12 +127,7 @@ fun MediaViewerView( canShowInfo = state.canShowInfo, onBackClick = onBackClick, onInfoClick = { - mediaBottomSheetState = MediaBottomSheetState.MediaDetailsBottomSheetState( - eventId = state.eventId, - canDelete = state.canDelete, - mediaInfo = state.mediaInfo, - thumbnailSource = state.thumbnailSource, - ) + state.eventSink(MediaViewerEvents.OpenInfo) }, eventSink = state.eventSink ) @@ -146,24 +140,19 @@ fun MediaViewerView( } } } - when (val bottomSheetState = mediaBottomSheetState) { + when (val bottomSheetState = state.mediaBottomSheetState) { MediaBottomSheetState.Hidden -> Unit is MediaBottomSheetState.MediaDetailsBottomSheetState -> { MediaDetailsBottomSheet( state = bottomSheetState, onViewInTimeline = { - mediaBottomSheetState = MediaBottomSheetState.Hidden state.eventSink(MediaViewerEvents.ViewInTimeline(it)) }, onDelete = { eventId -> - mediaBottomSheetState = MediaBottomSheetState.MediaDeleteConfirmationState( - eventId = eventId, - mediaInfo = state.mediaInfo, - thumbnailSource = state.thumbnailSource, - ) + state.eventSink(MediaViewerEvents.ConfirmDelete(eventId)) }, onDismiss = { - mediaBottomSheetState = MediaBottomSheetState.Hidden + state.eventSink(MediaViewerEvents.CloseBottomSheet) }, ) } @@ -171,11 +160,10 @@ fun MediaViewerView( MediaDeleteConfirmationBottomSheet( state = bottomSheetState, onDelete = { - mediaBottomSheetState = MediaBottomSheetState.Hidden state.eventSink(MediaViewerEvents.Delete(it)) }, onDismiss = { - mediaBottomSheetState = MediaBottomSheetState.Hidden + state.eventSink(MediaViewerEvents.CloseBottomSheet) }, ) } diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt index 2b9e56820d..43835e71e5 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerPresenterTest.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.mediaviewer.api.anApkMediaInfo +import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState import io.element.android.libraries.mediaviewer.test.FakeLocalMediaActions import io.element.android.libraries.mediaviewer.test.FakeLocalMediaFactory import io.element.android.tests.testutils.WarmUpRule @@ -67,7 +68,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.canDownload).isTrue() assertThat(initialState.canShare).isTrue() - assertThat(initialState.canDelete).isFalse() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -87,7 +88,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isFalse() assertThat(initialState.canDownload).isTrue() assertThat(initialState.canShare).isTrue() - assertThat(initialState.canDelete).isFalse() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -107,7 +108,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.canDownload).isTrue() assertThat(initialState.canShare).isFalse() - assertThat(initialState.canDelete).isFalse() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -127,7 +128,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.canDownload).isFalse() assertThat(initialState.canShare).isTrue() - assertThat(initialState.canDelete).isFalse() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -147,7 +148,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.canDownload).isTrue() assertThat(initialState.canShare).isTrue() - assertThat(initialState.canDelete).isTrue() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } } @@ -168,7 +169,7 @@ class MediaViewerPresenterTest { assertThat(initialState.canShowInfo).isTrue() assertThat(initialState.canDownload).isTrue() assertThat(initialState.canShare).isTrue() - assertThat(initialState.canDelete).isFalse() + assertThat(initialState.mediaBottomSheetState).isEqualTo(MediaBottomSheetState.Hidden) } }