Simplify ForwardMessagesState by using AsyncAction.

This commit is contained in:
Benoit Marty
2024-05-30 12:22:20 +02:00
committed by Benoit Marty
parent e6fab7eee2
commit 130bce178a
6 changed files with 42 additions and 83 deletions

View File

@@ -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<RoomId>) {
private fun onForwardSuccess(roomIds: List<RoomId>) {
navigateUp()
if (roomIds.size == 1) {
val targetRoomId = roomIds.first()

View File

@@ -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<ForwardMessagesState> {
private val eventId: EventId = EventId(eventId)
@@ -48,28 +46,22 @@ class ForwardMessagesPresenter @AssistedInject constructor(
fun create(eventId: String): ForwardMessagesPresenter
}
private val forwardingActionState: MutableState<AsyncData<ImmutableList<RoomId>>> = mutableStateOf(AsyncData.Uninitialized)
private val forwardingActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
fun onRoomSelected(roomIds: List<RoomId>) {
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<RoomId>,
isForwardMessagesState: MutableState<AsyncData<ImmutableList<RoomId>>>,
isForwardMessagesState: MutableState<AsyncAction<List<RoomId>>>,
) = 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)
}
}

View File

@@ -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<RoomId>?,
val forwardAction: AsyncAction<List<RoomId>>,
val eventSink: (ForwardMessagesEvents) -> Unit
)

View File

@@ -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<ForwardMessagesState> {
override val values: Sequence<ForwardMessagesState>
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<RoomId>? = null,
forwardAction: AsyncAction<List<RoomId>> = AsyncAction.Uninitialized,
) = ForwardMessagesState(
isForwarding = isForwarding,
error = error,
forwardingSucceeded = forwardingSucceeded,
forwardAction = forwardAction,
eventSink = {}
)

View File

@@ -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<RoomId>) -> Unit,
modifier: Modifier = Modifier,
onForwardSuccess: (List<RoomId>) -> 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)
},
)
}

View File

@@ -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,
)
}