Skip to content

Commit

Permalink
feat: more errors handling for personal migration #WPB-14281 (#3674)
Browse files Browse the repository at this point in the history
  • Loading branch information
damian-kaczmarek authored Nov 29, 2024
1 parent aa35a0b commit 736d665
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -70,7 +75,7 @@ fun TeamMigrationConfirmationStepScreen(
navigator: DestinationsNavigator,
teamMigrationViewModel: TeamMigrationViewModel
) {
val state = remember { teamMigrationViewModel.teamMigrationState }
val state = teamMigrationViewModel.teamMigrationState

TeamMigrationConfirmationStepScreenContent(
onContinueButtonClicked = {
Expand All @@ -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)
Expand All @@ -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,
)
)
}

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<string name="label_membership_external">External</string>
<string name="label_membership_service">Service</string>
<string name="label_user_deleted">Deleted</string>
<string name="label_try_again">Try Again</string>
<string name="label_message_sent_failure">Message could not be sent due to connectivity issues.</string>
<string name="label_message_edit_sent_failure">The edited message could not be sent due to connectivity issues.</string>
<string name="label_message_sent_remotely_failure">Message could not be sent, as the backend of %s could not be reached.</string>
Expand Down Expand Up @@ -1631,5 +1632,10 @@ In group conversations, the group admin can overwrite this setting.</string>
<string name="personal_to_team_migration_confirm_leave_dialog_description">When you leave now, you lose your progress and need to restart the team creation.</string>
<string name="personal_to_team_migration_confirm_leave_dialog_continue_button">Continue Team Creation</string>
<string name="personal_to_team_migration_confirm_leave_dialog_leave_button">Leave Without Saving</string>
<string name="personal_to_team_migration_error_title">Team not created</string>
<string name="personal_to_team_migration_error_title_already_in_team">Already part of a team</string>
<string name="personal_to_team_migration_error_message_already_in_team">You\'ve created or joined a team with this email address on another device.</string>
<string name="personal_to_team_migration_error_message_slow_network">Wire could not complete your team creation due to a slow internet connection.</string>
<string name="personal_to_team_migration_error_message_unknown_error">Wire could not complete your team creation due to an unknown error.</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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
)
}

Expand Down

0 comments on commit 736d665

Please sign in to comment.