diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt index e0e369e481..7734b3bb29 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteState.kt @@ -9,10 +9,8 @@ package io.element.android.features.invite.api.response import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import java.util.Optional data class AcceptDeclineInviteState( - val invite: Optional, val acceptAction: AsyncAction, val declineAction: AsyncAction, val eventSink: (AcceptDeclineInviteEvents) -> Unit, diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt index 4c377590e6..a4f3695369 100644 --- a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt @@ -10,23 +10,20 @@ package io.element.android.features.invite.api.response import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId -import java.util.Optional open class AcceptDeclineInviteStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAcceptDeclineInviteState(), anAcceptDeclineInviteState( - invite = Optional.of( - InviteData(RoomId("!room:matrix.org"), isDm = true, roomName = "Alice"), + declineAction = ConfirmingDeclineInvite( + InviteData(RoomId("!room:matrix.org"), isDm = true, roomName = "Alice") ), - declineAction = AsyncAction.ConfirmingNoParams, ), anAcceptDeclineInviteState( - invite = Optional.of( - InviteData(RoomId("!room:matrix.org"), isDm = false, roomName = "Some room"), + declineAction = ConfirmingDeclineInvite( + InviteData(RoomId("!room:matrix.org"), isDm = false, roomName = "Some room") ), - declineAction = AsyncAction.ConfirmingNoParams, ), anAcceptDeclineInviteState( acceptAction = AsyncAction.Failure(Throwable("Whoops")), @@ -38,12 +35,10 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider = Optional.empty(), acceptAction: AsyncAction = AsyncAction.Uninitialized, declineAction: AsyncAction = AsyncAction.Uninitialized, eventSink: (AcceptDeclineInviteEvents) -> Unit = {} ) = AcceptDeclineInviteState( - invite = invite, acceptAction = acceptAction, declineAction = declineAction, eventSink = eventSink, diff --git a/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt new file mode 100644 index 0000000000..c2231019c8 --- /dev/null +++ b/features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/ConfirmingDeclineInvite.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2024 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only + * Please see LICENSE in the repository root for full details. + */ + +package io.element.android.features.invite.api.response + +import io.element.android.libraries.architecture.AsyncAction + +data class ConfirmingDeclineInvite( + val inviteData: InviteData, +) : AsyncAction.Confirming diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt index 5ebf3cbb76..082120c6f3 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt @@ -9,15 +9,13 @@ package io.element.android.features.invite.impl.response import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents import io.element.android.features.invite.api.response.AcceptDeclineInviteState -import io.element.android.features.invite.api.response.InviteData +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runCatchingUpdatingState @@ -29,9 +27,7 @@ import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -import java.util.Optional import javax.inject.Inject -import kotlin.jvm.optionals.getOrNull class AcceptDeclineInvitePresenter @Inject constructor( private val client: MatrixClient, @@ -43,35 +39,22 @@ class AcceptDeclineInvitePresenter @Inject constructor( val localCoroutineScope = rememberCoroutineScope() val acceptedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val declinedAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } - var currentInvite by remember { - mutableStateOf>(Optional.empty()) - } fun handleEvents(event: AcceptDeclineInviteEvents) { when (event) { is AcceptDeclineInviteEvents.AcceptInvite -> { - // currentInvite is used to render the decline confirmation dialog - // and to reuse the roomId when the user confirm the rejection of the invitation. - // Just set it to empty here. - currentInvite = Optional.empty() localCoroutineScope.acceptInvite(event.invite.roomId, acceptedAction) } is AcceptDeclineInviteEvents.DeclineInvite -> { - currentInvite = Optional.of(event.invite) - declinedAction.value = AsyncAction.ConfirmingNoParams + declinedAction.value = ConfirmingDeclineInvite(event.invite) } is InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite -> { - declinedAction.value = AsyncAction.Uninitialized - currentInvite.getOrNull()?.let { - localCoroutineScope.declineInvite(it.roomId, declinedAction) - } - currentInvite = Optional.empty() + localCoroutineScope.declineInvite(event.roomId, declinedAction) } is InternalAcceptDeclineInviteEvents.CancelDeclineInvite -> { - currentInvite = Optional.empty() declinedAction.value = AsyncAction.Uninitialized } @@ -86,7 +69,6 @@ class AcceptDeclineInvitePresenter @Inject constructor( } return AcceptDeclineInviteState( - invite = currentInvite, acceptAction = acceptedAction.value, declineAction = declinedAction.value, eventSink = ::handleEvents diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt index 94593d0764..ecbc601352 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.invite.api.response.AcceptDeclineInviteState import io.element.android.features.invite.api.response.AcceptDeclineInviteStateProvider +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.features.invite.api.response.InviteData import io.element.android.features.invite.impl.R import io.element.android.libraries.designsystem.components.async.AsyncActionView @@ -22,7 +23,6 @@ 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 io.element.android.libraries.ui.strings.CommonStrings -import kotlin.jvm.optionals.getOrNull @Composable fun AcceptDeclineInviteView( @@ -45,13 +45,13 @@ fun AcceptDeclineInviteView( onErrorDismiss = { state.eventSink(InternalAcceptDeclineInviteEvents.DismissDeclineError) }, - confirmationDialog = { - val invite = state.invite.getOrNull() - if (invite != null) { + confirmationDialog = { confirming -> + // Note: confirming will always be of type ConfirmingDeclineInvite. + if (confirming is ConfirmingDeclineInvite) { DeclineConfirmationDialog( - invite = invite, + invite = confirming.inviteData, onConfirmClick = { - state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite) + state.eventSink(InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(confirming.inviteData.roomId)) }, onDismissClick = { state.eventSink(InternalAcceptDeclineInviteEvents.CancelDeclineInvite) diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt index 779106e1c3..60b96805e6 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/InternalAcceptDeclineInviteEvents.kt @@ -8,9 +8,10 @@ package io.element.android.features.invite.impl.response import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents +import io.element.android.libraries.matrix.api.core.RoomId sealed interface InternalAcceptDeclineInviteEvents : AcceptDeclineInviteEvents { - data object ConfirmDeclineInvite : InternalAcceptDeclineInviteEvents + data class ConfirmDeclineInvite(val roomId: RoomId) : InternalAcceptDeclineInviteEvents data object CancelDeclineInvite : InternalAcceptDeclineInviteEvents data object DismissAcceptError : InternalAcceptDeclineInviteEvents data object DismissDeclineError : InternalAcceptDeclineInviteEvents diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index f194404f8e..6c1057638f 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -10,6 +10,7 @@ package io.element.android.features.invite.impl.response import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.features.invite.api.response.AcceptDeclineInviteEvents +import io.element.android.features.invite.api.response.ConfirmingDeclineInvite import io.element.android.features.invite.api.response.InviteData import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.MatrixClient @@ -33,7 +34,6 @@ import io.element.android.tests.testutils.test import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import java.util.Optional class AcceptDeclineInvitePresenterTest { @get:Rule @@ -46,7 +46,6 @@ class AcceptDeclineInvitePresenterTest { awaitItem().also { state -> assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) - assertThat(state.invite).isEqualTo(Optional.empty()) } } } @@ -61,17 +60,13 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.of(inviteData)) - assertThat(state.declineAction).isInstanceOf(AsyncAction.Confirming::class.java) + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( InternalAcceptDeclineInviteEvents.CancelDeclineInvite ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } } @@ -93,22 +88,20 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( - InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite + InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId) ) } - skipItems(2) + assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Failure::class.java) state.eventSink( InternalAcceptDeclineInviteEvents.DismissDeclineError ) } - skipItems(1) awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.declineAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } cancelAndConsumeRemainingEvents() @@ -141,13 +134,13 @@ class AcceptDeclineInvitePresenterTest { AcceptDeclineInviteEvents.DeclineInvite(inviteData) ) } - skipItems(1) awaitItem().also { state -> + assertThat(state.declineAction).isEqualTo(ConfirmingDeclineInvite(inviteData)) state.eventSink( - InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite + InternalAcceptDeclineInviteEvents.ConfirmDeclineInvite(inviteData.roomId) ) } - skipItems(2) + assertThat(awaitItem().declineAction.isLoading()).isTrue() awaitItem().also { state -> assertThat(state.declineAction).isInstanceOf(AsyncAction.Success::class.java) } @@ -173,7 +166,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading) } awaitItem().also { state -> @@ -183,7 +175,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isInstanceOf(AsyncAction.Uninitialized::class.java) } cancelAndConsumeRemainingEvents() @@ -220,7 +211,6 @@ class AcceptDeclineInvitePresenterTest { ) } awaitItem().also { state -> - assertThat(state.invite).isEqualTo(Optional.empty()) assertThat(state.acceptAction).isEqualTo(AsyncAction.Loading) } awaitItem().also { state -> diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt index 3224d1fd2f..06416e2c80 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt @@ -13,14 +13,15 @@ import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.tests.testutils.lambda.lambdaError +import io.element.android.tests.testutils.simulateLongTask class FakeInvitedRoom( override val sessionId: SessionId = A_SESSION_ID, override val roomId: RoomId = A_ROOM_ID, private val declineInviteResult: () -> Result = { lambdaError() } ) : InvitedRoom { - override suspend fun declineInvite(): Result { - return declineInviteResult() + override suspend fun declineInvite(): Result = simulateLongTask { + declineInviteResult() } override fun close() = Unit