From c91127141ddcef2e9dd9d5eff92dfe3e118eda85 Mon Sep 17 00:00:00 2001 From: Damian Kaczmarek <76782439+damian-kaczmarek@users.noreply.github.com> Date: Thu, 21 Nov 2024 17:39:39 +0100 Subject: [PATCH] feat: connect to upgrade-personal-to-team API #WPB-11992 (#3625) Co-authored-by: ohassine --- .../com/wire/android/di/CoreLogicModule.kt | 8 ++ .../teammigration/TeamMigrationState.kt | 4 +- .../teammigration/TeamMigrationViewModel.kt | 36 ++++++++- .../TeamMigrationConfirmationStepScreen.kt | 28 ++++++- .../TeamMigrationViewModelTest.kt | 73 ++++++++++++++++++- kalium | 2 +- 6 files changed, 144 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt b/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt index 0b32e3d76f0..3bdf323e37e 100644 --- a/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt +++ b/app/src/main/kotlin/com/wire/android/di/CoreLogicModule.kt @@ -492,4 +492,12 @@ class UseCaseModule { @Provides fun provideSendFCMTokenToAPIUseCase(@KaliumCoreLogic coreLogic: CoreLogic, @CurrentAccount currentAccount: UserId) = coreLogic.getSessionScope(currentAccount).debug.sendFCMTokenToServer + + @ViewModelScoped + @Provides + fun provideMigrateFromPersonalToTeamUseCase( + @KaliumCoreLogic coreLogic: CoreLogic, + @CurrentAccount currentAccount: UserId + ) = + coreLogic.getSessionScope(currentAccount).migrateFromPersonalToTeam } 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 561ad8f57bd..0399b9c0797 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,8 +18,10 @@ package com.wire.android.ui.userprofile.teammigration import androidx.compose.foundation.text.input.TextFieldState +import com.wire.kalium.logic.CoreFailure data class TeamMigrationState( val teamNameTextState: TextFieldState = TextFieldState(), - val shouldShowMigrationLeaveDialog: Boolean = false + val shouldShowMigrationLeaveDialog: Boolean = false, + val migrationFailure: CoreFailure? = 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 89494b1f5fc..9fd2d31daba 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 @@ -17,18 +17,25 @@ */ package com.wire.android.ui.userprofile.teammigration +import androidx.compose.foundation.text.input.setTextAndSelectAll import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue 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.MigrateFromPersonalToTeamResult +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class TeamMigrationViewModel @Inject constructor( - private val anonymousAnalyticsManager: AnonymousAnalyticsManager + private val anonymousAnalyticsManager: AnonymousAnalyticsManager, + private val migrateFromPersonalToTeam: MigrateFromPersonalToTeamUseCase ) : ViewModel() { var teamMigrationState by mutableStateOf(TeamMigrationState()) @@ -81,4 +88,31 @@ class TeamMigrationViewModel @Inject constructor( ) ) } + + fun migrateFromPersonalToTeamAccount(onSuccess: () -> Unit) { + viewModelScope.launch { + migrateFromPersonalToTeam.invoke( + teamMigrationState.teamNameTextState.text.toString(), + ).let { result -> + when (result) { + is MigrateFromPersonalToTeamResult.Success -> { + teamMigrationState.teamNameTextState.setTextAndSelectAll(result.teamName) + onSuccess() + } + + is MigrateFromPersonalToTeamResult.Error -> { + onMigrationFailure(result.failure) + } + } + } + } + } + + fun failureHandled() { + teamMigrationState = teamMigrationState.copy(migrationFailure = null) + } + + private fun onMigrationFailure(failure: CoreFailure) { + 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 69fc9ca95e2..9be96dcb197 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 @@ -47,10 +47,12 @@ import com.wire.android.navigation.style.SlideNavigationAnimation import com.wire.android.ui.common.WireCheckbox 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.theme.WireTheme import com.wire.android.ui.theme.wireTypography import com.wire.android.ui.userprofile.teammigration.PersonalToTeamMigrationNavGraph +import com.wire.android.ui.userprofile.teammigration.TeamMigrationState import com.wire.android.ui.userprofile.teammigration.TeamMigrationViewModel import com.wire.android.ui.userprofile.teammigration.common.BottomLineButtons import com.wire.android.ui.userprofile.teammigration.common.BulletList @@ -66,21 +68,43 @@ fun TeamMigrationConfirmationStepScreen( navigator: DestinationsNavigator, teamMigrationViewModel: TeamMigrationViewModel ) { + val state = remember { teamMigrationViewModel.teamMigrationState } TeamMigrationConfirmationStepScreenContent( onContinueButtonClicked = { - // TODO: call the API to migrate the user to the team, if successful navigate to next screen - navigator.navigate(TeamMigrationDoneStepScreenDestination) + teamMigrationViewModel.migrateFromPersonalToTeamAccount( + onSuccess = { + navigator.navigate(TeamMigrationDoneStepScreenDestination) + }, + ) }, onBackPressed = { navigator.popBackStack() } ) + + HandleErrors(state, teamMigrationViewModel::failureHandled) + LaunchedEffect(Unit) { teamMigrationViewModel.sendPersonalTeamCreationFlowStartedEvent(3) } } +@Composable +private fun HandleErrors( + teamMigrationState: TeamMigrationState, + onFailureHandled: () -> Unit +) { + val failure = teamMigrationState.migrationFailure ?: return + // TODO handle error WPB-14281 + CoreFailureErrorDialog( + coreFailure = failure, + onDialogDismiss = { + onFailureHandled() + } + ) +} + @Composable private fun TeamMigrationConfirmationStepScreenContent( modifier: Modifier = Modifier, 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 331f4b5c004..4def3b04039 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 @@ -17,15 +17,26 @@ */ package com.wire.android.ui.userprofile.teammigration +import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd +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.MigrateFromPersonalToTeamResult +import com.wire.kalium.logic.feature.user.migration.MigrateFromPersonalToTeamUseCase import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.coVerify import io.mockk.impl.annotations.MockK +import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.test.runTest import org.amshove.kluent.internal.assertEquals +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +@ExtendWith(CoroutineTestExtension::class) class TeamMigrationViewModelTest { @Test @@ -157,17 +168,75 @@ class TeamMigrationViewModelTest { } } + @Test + fun `given team name, when migrateFromPersonalToTeamAccount return success, then call use case and onSuccess`() = + runTest { + val (arrangement, viewModel) = Arrangement() + .withMigrateFromPersonalToTeamSuccess() + .arrange() + + val onSuccess = mockk<() -> Unit>(relaxed = true) + + viewModel.migrateFromPersonalToTeamAccount(onSuccess) + + coVerify(exactly = 1) { + arrangement.migrateFromPersonalToTeam(Arrangement.TEAM_NAME) + } + verify(exactly = 1) { onSuccess() } + } + + @Test + fun `given team name, when migrateFromPersonalToTeamAccount return failure, then call use case and handle the failure`() = + runTest { + val (arrangement, viewModel) = Arrangement() + .withMigrateFromPersonalToTeamError() + .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) + } + private class Arrangement { @MockK lateinit var anonymousAnalyticsManager: AnonymousAnalyticsManager + @MockK + lateinit var migrateFromPersonalToTeam: MigrateFromPersonalToTeamUseCase + init { MockKAnnotations.init(this, relaxUnitFun = true) } fun arrange() = this to TeamMigrationViewModel( - anonymousAnalyticsManager = anonymousAnalyticsManager - ) + anonymousAnalyticsManager = anonymousAnalyticsManager, + migrateFromPersonalToTeam = migrateFromPersonalToTeam, + ).also { viewModel -> + viewModel.teamMigrationState.teamNameTextState.setTextAndPlaceCursorAtEnd(TEAM_NAME) + } + + fun withMigrateFromPersonalToTeamSuccess() = apply { + coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Success( + TEAM_NAME + ) + } + + fun withMigrateFromPersonalToTeamError() = apply { + coEvery { migrateFromPersonalToTeam(any()) } returns MigrateFromPersonalToTeamResult.Error( + NetworkFailure.NoNetworkConnection(null) + ) + } + + companion object { + const val TEAM_NAME = "teamName" + } } } diff --git a/kalium b/kalium index 9273703fe30..08c0cffe74b 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 9273703fe301924bf7ae8c17723c57bb443df41f +Subproject commit 08c0cffe74b9523c7464e3645eb79f2ca7d59d3f