diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt index 04d562abde..37aabfd4b8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesNode.kt @@ -36,7 +36,6 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.roomselect.api.RoomSelectEntryPoint import io.element.android.libraries.roomselect.api.RoomSelectMode -import kotlinx.collections.immutable.ImmutableList import kotlinx.parcelize.Parcelize @ContributesNode(RoomScope::class) @@ -99,7 +98,7 @@ class ForwardMessagesNode @AssistedInject constructor( } } - private fun onForwardSuccess(roomIds: ImmutableList) { + private fun onForwardSuccess(roomIds: List) { navigateUp() if (roomIds.size == 1) { val targetRoomId = roomIds.first() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt index 9cfdbcd14d..3b311c7ced 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenter.kt @@ -18,15 +18,13 @@ package io.element.android.features.messages.impl.forward import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.timeline.TimelineProvider @@ -38,7 +36,7 @@ import kotlinx.coroutines.launch class ForwardMessagesPresenter @AssistedInject constructor( @Assisted eventId: String, - private val matrixCoroutineScope: CoroutineScope, + private val appCoroutineScope: CoroutineScope, private val timelineProvider: TimelineProvider, ) : Presenter { private val eventId: EventId = EventId(eventId) @@ -48,28 +46,22 @@ class ForwardMessagesPresenter @AssistedInject constructor( fun create(eventId: String): ForwardMessagesPresenter } - private val forwardingActionState: MutableState>> = mutableStateOf(AsyncData.Uninitialized) + private val forwardingActionState: MutableState>> = mutableStateOf(AsyncAction.Uninitialized) fun onRoomSelected(roomIds: List) { - matrixCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState) + appCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState) } @Composable override fun present(): ForwardMessagesState { - val forwardingSucceeded by remember { - derivedStateOf { forwardingActionState.value.dataOrNull() } - } - fun handleEvents(event: ForwardMessagesEvents) { when (event) { - ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncData.Uninitialized + ForwardMessagesEvents.ClearError -> forwardingActionState.value = AsyncAction.Uninitialized } } return ForwardMessagesState( - isForwarding = forwardingActionState.value.isLoading(), - error = (forwardingActionState.value as? AsyncData.Failure)?.error, - forwardingSucceeded = forwardingSucceeded, + forwardAction = forwardingActionState.value, eventSink = { handleEvents(it) } ) } @@ -77,12 +69,11 @@ class ForwardMessagesPresenter @AssistedInject constructor( private fun CoroutineScope.forwardEvent( eventId: EventId, roomIds: ImmutableList, - isForwardMessagesState: MutableState>>, + isForwardMessagesState: MutableState>>, ) = launch { - isForwardMessagesState.value = AsyncData.Loading() - timelineProvider.getActiveTimeline().forwardEvent(eventId, roomIds).fold( - { isForwardMessagesState.value = AsyncData.Success(roomIds) }, - { isForwardMessagesState.value = AsyncData.Failure(it) } - ) + suspend { + timelineProvider.getActiveTimeline().forwardEvent(eventId, roomIds).getOrThrow() + roomIds + }.runCatchingUpdatingState(isForwardMessagesState) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt index d64c4dd7e9..166ab7b65d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesState.kt @@ -16,13 +16,10 @@ package io.element.android.features.messages.impl.forward +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import kotlinx.collections.immutable.ImmutableList data class ForwardMessagesState( - // TODO Migrate to an Async - val isForwarding: Boolean, - val error: Throwable?, - val forwardingSucceeded: ImmutableList?, + val forwardAction: AsyncAction>, val eventSink: (ForwardMessagesEvents) -> Unit ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt index aa33b1cbd9..e66741c3bf 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesStateProvider.kt @@ -17,34 +17,30 @@ package io.element.android.features.messages.impl.forward import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf open class ForwardMessagesStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aForwardMessagesState(), aForwardMessagesState( - isForwarding = true, + forwardAction = AsyncAction.Loading, ), aForwardMessagesState( - forwardingSucceeded = persistentListOf(RoomId("!room2:domain")), + forwardAction = AsyncAction.Success( + listOf(RoomId("!room2:domain")), + ) ), aForwardMessagesState( - error = Throwable("error"), + forwardAction = AsyncAction.Failure(Throwable("error")), ), - // Add other states here ) } fun aForwardMessagesState( - isForwarding: Boolean = false, - error: Throwable? = null, - forwardingSucceeded: ImmutableList? = null, + forwardAction: AsyncAction> = AsyncAction.Uninitialized, ) = ForwardMessagesState( - isForwarding = isForwarding, - error = error, - forwardingSucceeded = forwardingSucceeded, + forwardAction = forwardAction, eventSink = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt index 030b137c04..310e0c85fe 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesView.kt @@ -17,45 +17,25 @@ package io.element.android.features.messages.impl.forward import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter -import io.element.android.libraries.designsystem.components.ProgressDialog -import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog -import io.element.android.libraries.designsystem.components.dialogs.ErrorDialogDefaults +import io.element.android.libraries.designsystem.components.async.AsyncActionView import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.matrix.api.core.RoomId -import kotlinx.collections.immutable.ImmutableList @Composable fun ForwardMessagesView( state: ForwardMessagesState, - onForwardSuccess: (ImmutableList) -> Unit, - modifier: Modifier = Modifier, + onForwardSuccess: (List) -> Unit, ) { - if (state.forwardingSucceeded != null) { - onForwardSuccess(state.forwardingSucceeded) - return - } - - if (state.isForwarding) { - ProgressDialog(modifier) - } - - if (state.error != null) { - ForwardingErrorDialog( - modifier = modifier, - onDismiss = { state.eventSink(ForwardMessagesEvents.ClearError) }, - ) - } -} - -@Composable -private fun ForwardingErrorDialog(onDismiss: () -> Unit, modifier: Modifier = Modifier) { - ErrorDialog( - content = ErrorDialogDefaults.title, - onDismiss = onDismiss, - modifier = modifier, + AsyncActionView( + async = state.forwardAction, + onSuccess = { + onForwardSuccess(it) + }, + onErrorDismiss = { + state.eventSink(ForwardMessagesEvents.ClearError) + }, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTest.kt index 99734de4ee..e30011999f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/forward/ForwardMessagesPresenterTest.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -28,13 +29,11 @@ import io.element.android.libraries.matrix.test.room.aRoomSummaryDetails import io.element.android.libraries.matrix.test.timeline.FakeTimeline import io.element.android.libraries.matrix.test.timeline.LiveTimelineProvider import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import java.lang.IllegalStateException class ForwardMessagesPresenterTest { @get:Rule @@ -47,9 +46,7 @@ class ForwardMessagesPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.isForwarding).isFalse() - assertThat(initialState.error).isNull() - assertThat(initialState.forwardingSucceeded).isNull() + assertThat(initialState.forwardAction.isUninitialized()).isTrue() } } @@ -70,11 +67,10 @@ class ForwardMessagesPresenterTest { val summary = aRoomSummaryDetails() presenter.onRoomSelected(listOf(summary.roomId)) val forwardingState = awaitItem() - assertThat(forwardingState.isForwarding).isTrue() + assertThat(forwardingState.forwardAction.isLoading()).isTrue() val successfulForwardState = awaitItem() - assertThat(successfulForwardState.isForwarding).isFalse() - assertThat(successfulForwardState.forwardingSucceeded).isNotNull() - assert(forwardEventLambda).isCalledOnce() + assertThat(successfulForwardState.forwardAction).isEqualTo(AsyncAction.Success(listOf(summary.roomId))) + forwardEventLambda.assertions().isCalledOnce() } } @@ -96,11 +92,11 @@ class ForwardMessagesPresenterTest { presenter.onRoomSelected(listOf(summary.roomId)) skipItems(1) val failedForwardState = awaitItem() - assertThat(failedForwardState.error).isNotNull() + assertThat(failedForwardState.forwardAction.isFailure()).isTrue() // Then clear error failedForwardState.eventSink(ForwardMessagesEvents.ClearError) - assertThat(awaitItem().error).isNull() - assert(forwardEventLambda).isCalledOnce() + assertThat(awaitItem().forwardAction.isUninitialized()).isTrue() + forwardEventLambda.assertions().isCalledOnce() } } @@ -111,6 +107,6 @@ class ForwardMessagesPresenterTest { ) = ForwardMessagesPresenter( eventId = eventId.value, timelineProvider = LiveTimelineProvider(fakeMatrixRoom), - matrixCoroutineScope = coroutineScope, + appCoroutineScope = coroutineScope, ) }