From 736d665a2c8a40a4b08c5c28ed900cdb923bdde3 Mon Sep 17 00:00:00 2001 From: Damian Kaczmarek <76782439+damian-kaczmarek@users.noreply.github.com> Date: Fri, 29 Nov 2024 15:44:54 +0100 Subject: [PATCH] feat: more errors handling for personal migration #WPB-14281 (#3674) --- .../teammigration/TeamMigrationState.kt | 4 +- .../teammigration/TeamMigrationViewModel.kt | 4 +- .../TeamMigrationConfirmationStepScreen.kt | 86 +++++++++++++++++-- app/src/main/res/values/strings.xml | 6 ++ .../TeamMigrationViewModelTest.kt | 59 ++++++++++++- kalium | 2 +- 6 files changed, 143 insertions(+), 18 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationState.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationState.kt index 3a045cfd045..be2d3d9625e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationState.kt @@ -18,11 +18,11 @@ package com.wire.android.ui.userprofile.teammigration import androidx.compose.foundation.text.input.TextFieldState -import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamFailure data class TeamMigrationState( val teamNameTextState: TextFieldState = TextFieldState(), val shouldShowMigrationLeaveDialog: Boolean = false, val currentStep: Int = 0, - val migrationFailure: CoreFailure? = null + val migrationFailure: MigrateFromPersonalToTeamFailure? = null ) diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModel.kt index 9b8fefd2dc9..b4080251aa6 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModel.kt @@ -24,7 +24,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.wire.android.feature.analytics.AnonymousAnalyticsManager import com.wire.android.feature.analytics.model.AnalyticsEvent -import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamFailure import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamResult import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamUseCase import dagger.hilt.android.lifecycle.HiltViewModel @@ -114,7 +114,7 @@ class TeamMigrationViewModel @Inject constructor( teamMigrationState = teamMigrationState.copy(migrationFailure = null) } - private fun onMigrationFailure(failure: CoreFailure) { + private fun onMigrationFailure(failure: MigrateFromPersonalToTeamFailure) { teamMigrationState = teamMigrationState.copy(migrationFailure = failure) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/step3/TeamMigrationConfirmationStepScreen.kt b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/step3/TeamMigrationConfirmationStepScreen.kt index f3254d79370..996e6a2a4fc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/step3/TeamMigrationConfirmationStepScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/userprofile/teammigration/step3/TeamMigrationConfirmationStepScreen.kt @@ -41,14 +41,18 @@ import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withLink import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.ramcosta.composedestinations.navigation.popUpTo import com.wire.android.R import com.wire.android.navigation.WireDestination import com.wire.android.navigation.style.SlideNavigationAnimation import com.wire.android.ui.common.WireCheckbox +import com.wire.android.ui.common.WireDialog +import com.wire.android.ui.common.WireDialogButtonProperties +import com.wire.android.ui.common.WireDialogButtonType import com.wire.android.ui.common.colorsScheme import com.wire.android.ui.common.dimensions -import com.wire.android.ui.common.error.CoreFailureErrorDialog import com.wire.android.ui.destinations.TeamMigrationDoneStepScreenDestination +import com.wire.android.ui.destinations.TeamMigrationTeamPlanStepScreenDestination import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.wireTypography import com.wire.android.ui.userprofile.teammigration.PersonalToTeamMigrationNavGraph @@ -58,6 +62,7 @@ import com.wire.android.ui.userprofile.teammigration.common.BottomLineButtons import com.wire.android.ui.userprofile.teammigration.common.BulletList import com.wire.android.util.CustomTabsHelper import com.wire.android.util.ui.PreviewMultipleThemes +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamFailure const val TEAM_MIGRATION_CONFIRMATION_STEP = 3 @@ -70,7 +75,7 @@ fun TeamMigrationConfirmationStepScreen( navigator: DestinationsNavigator, teamMigrationViewModel: TeamMigrationViewModel ) { - val state = remember { teamMigrationViewModel.teamMigrationState } + val state = teamMigrationViewModel.teamMigrationState TeamMigrationConfirmationStepScreenContent( onContinueButtonClicked = { @@ -85,7 +90,20 @@ fun TeamMigrationConfirmationStepScreen( } ) - HandleErrors(state, teamMigrationViewModel::failureHandled) + HandleErrors( + teamMigrationState = state, + onFailureHandled = teamMigrationViewModel::failureHandled, + goBackToFirstStep = { + navigator.navigate( + direction = TeamMigrationTeamPlanStepScreenDestination, + builder = { + popUpTo(TeamMigrationTeamPlanStepScreenDestination) { + inclusive = false + } + } + ) + } + ) LaunchedEffect(Unit) { teamMigrationViewModel.sendPersonalTeamCreationFlowStartedEvent(TEAM_MIGRATION_CONFIRMATION_STEP) @@ -96,15 +114,65 @@ fun TeamMigrationConfirmationStepScreen( @Composable private fun HandleErrors( teamMigrationState: TeamMigrationState, - onFailureHandled: () -> Unit + onFailureHandled: () -> Unit, + goBackToFirstStep: () -> Unit, ) { val failure = teamMigrationState.migrationFailure ?: return - // TODO handle error WPB-14281 - CoreFailureErrorDialog( - coreFailure = failure, - onDialogDismiss = { - onFailureHandled() + + when (failure) { + is MigrateFromPersonalToTeamFailure.UserAlreadyInTeam -> { + ErrorDialog( + title = stringResource(R.string.personal_to_team_migration_error_title_already_in_team), + message = stringResource(R.string.personal_to_team_migration_error_message_already_in_team), + buttonText = stringResource(id = R.string.label_ok), + onDismiss = { + onFailureHandled() + } + ) } + + is MigrateFromPersonalToTeamFailure.NoNetwork -> { + ErrorDialog( + title = stringResource(R.string.personal_to_team_migration_error_title), + message = stringResource(R.string.personal_to_team_migration_error_message_slow_network), + buttonText = stringResource(id = R.string.label_try_again), + onDismiss = { + onFailureHandled() + goBackToFirstStep() + } + ) + } + + else -> { + ErrorDialog( + title = stringResource(R.string.personal_to_team_migration_error_title), + message = stringResource(R.string.personal_to_team_migration_error_message_unknown_error), + buttonText = stringResource(id = R.string.label_try_again), + onDismiss = { + onFailureHandled() + goBackToFirstStep() + } + ) + } + } +} + +@Composable +private fun ErrorDialog( + title: String, + message: String, + buttonText: String, + onDismiss: () -> Unit, +) { + WireDialog( + title = title, + text = message, + onDismiss = onDismiss, + dismissButtonProperties = WireDialogButtonProperties( + onClick = onDismiss, + text = buttonText, + type = WireDialogButtonType.Primary, + ) ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36c6d043c92..95fafc74212 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,6 +44,7 @@ External Service Deleted + Try Again Message could not be sent due to connectivity issues. The edited message could not be sent due to connectivity issues. Message could not be sent, as the backend of %s could not be reached. @@ -1631,5 +1632,10 @@ In group conversations, the group admin can overwrite this setting. When you leave now, you lose your progress and need to restart the team creation. Continue Team Creation Leave Without Saving + Team not created + Already part of a team + You\'ve created or joined a team with this email address on another device. + Wire could not complete your team creation due to a slow internet connection. + Wire could not complete your team creation due to an unknown error. diff --git a/app/src/test/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModelTest.kt index efec0349927..42a7a926e05 100644 --- a/app/src/test/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/userprofile/teammigration/TeamMigrationViewModelTest.kt @@ -22,6 +22,7 @@ import com.wire.android.config.CoroutineTestExtension import com.wire.android.feature.analytics.AnonymousAnalyticsManager import com.wire.android.feature.analytics.model.AnalyticsEvent import com.wire.kalium.logic.NetworkFailure +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamFailure import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamResult import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamUseCase import io.mockk.MockKAnnotations @@ -186,10 +187,48 @@ class TeamMigrationViewModelTest { } @Test - fun `given team name, when migrateFromPersonalToTeamAccount return failure, then call use case and handle the failure`() = + fun `given team name, when migrateFromPersonalToTeamAccount return unknown failure, then call use case and handle the failure`() = runTest { val (arrangement, viewModel) = Arrangement() - .withMigrateFromPersonalToTeamError() + .withMigrateFromPersonalToTeamErrorUnknown() + .arrange() + + val onSuccess = {} + + viewModel.migrateFromPersonalToTeamAccount(onSuccess) + + coVerify(exactly = 1) { + arrangement.migrateFromPersonalToTeam(Arrangement.TEAM_NAME) + } + Assertions.assertNotNull(viewModel.teamMigrationState.migrationFailure) + viewModel.failureHandled() + Assertions.assertNull(viewModel.teamMigrationState.migrationFailure) + } + + @Test + fun `given team name, when migrateFromPersonalToTeamAccount return user already in team failure, then call use case and handle the failure`() = + runTest { + val (arrangement, viewModel) = Arrangement() + .withMigrateFromPersonalToTeamErrorAlreadyInTeam() + .arrange() + + val onSuccess = {} + + viewModel.migrateFromPersonalToTeamAccount(onSuccess) + + coVerify(exactly = 1) { + arrangement.migrateFromPersonalToTeam(Arrangement.TEAM_NAME) + } + Assertions.assertNotNull(viewModel.teamMigrationState.migrationFailure) + viewModel.failureHandled() + Assertions.assertNull(viewModel.teamMigrationState.migrationFailure) + } + + @Test + fun `given team name, when migrateFromPersonalToTeamAccount return no network failure, then call use case and handle the failure`() = + runTest { + val (arrangement, viewModel) = Arrangement() + .withMigrateFromPersonalToTeamErrorNoNetwork() .arrange() val onSuccess = {} @@ -227,9 +266,21 @@ class TeamMigrationViewModelTest { coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Success } - fun withMigrateFromPersonalToTeamError() = apply { + fun withMigrateFromPersonalToTeamErrorUnknown() = apply { + coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Error( + MigrateFromPersonalToTeamFailure.UnknownError(NetworkFailure.ProxyError(null)) + ) + } + + fun withMigrateFromPersonalToTeamErrorAlreadyInTeam() = apply { + coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Error( + MigrateFromPersonalToTeamFailure.UserAlreadyInTeam() + ) + } + + fun withMigrateFromPersonalToTeamErrorNoNetwork() = apply { coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Error( - NetworkFailure.NoNetworkConnection(null) + MigrateFromPersonalToTeamFailure.NoNetwork ) } diff --git a/kalium b/kalium index bee4d00171b..4c476f7390e 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit bee4d00171b79c0a2506c4a2b6eb2f45a250c52c +Subproject commit 4c476f7390ed6a7ffe27bc9ad0ce649c532ac35b