Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap all management service calls in Either #6500

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import net.mullvad.mullvadvpn.compose.transitions.SlideInFromBottomTransition
import net.mullvad.mullvadvpn.compose.util.LaunchedEffectCollect
import net.mullvad.mullvadvpn.compose.util.SecureScreenWhileInView
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
import net.mullvad.mullvadvpn.lib.payment.model.PaymentStatus
Expand Down Expand Up @@ -164,6 +165,7 @@ fun AccountScreen(

val snackbarHostState = remember { SnackbarHostState() }
val copyTextString = stringResource(id = R.string.copied_mullvad_account_number)
val errorString = stringResource(id = R.string.error_occurred)
val copyToClipboard = createCopyToClipboardHandle(snackbarHostState = snackbarHostState)
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
LaunchedEffectCollect(uiSideEffect) { sideEffect ->
Expand All @@ -173,6 +175,8 @@ fun AccountScreen(
openAccountPage(sideEffect.token)
is AccountViewModel.UiSideEffect.CopyAccountNumber ->
launch { copyToClipboard(sideEffect.accountNumber, copyTextString) }
AccountViewModel.UiSideEffect.GenericError ->
snackbarHostState.showSnackbarImmediately(message = errorString)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
Expand All @@ -37,6 +38,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -73,6 +75,7 @@ import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.compose.transitions.LoginTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.accountNumberVisualTransformation
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.viewmodel.LoginUiSideEffect
Expand Down Expand Up @@ -126,6 +129,8 @@ fun Login(
}
}

val context = LocalContext.current
val snackbarHostState = remember { SnackbarHostState() }
CollectSideEffectWithLifecycle(vm.uiSideEffect) {
when (it) {
LoginUiSideEffect.NavigateToWelcome ->
Expand All @@ -147,28 +152,35 @@ fun Login(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
LoginUiSideEffect.GenericError ->
snackbarHostState.showSnackbarImmediately(
message = context.getString(R.string.error_occurred),
)
}
}
LoginScreen(
state,
vm::login,
vm::createAccount,
vm::clearAccountHistory,
vm::onAccountNumberChange,
dropUnlessResumed { navigator.navigate(SettingsDestination) }
state = state,
snackbarHostState = snackbarHostState,
onLoginClick = vm::login,
onCreateAccountClick = vm::createAccount,
onDeleteHistoryClick = vm::clearAccountHistory,
onAccountNumberChange = vm::onAccountNumberChange,
onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) }
)
}

@Composable
private fun LoginScreen(
state: LoginUiState,
snackbarHostState: SnackbarHostState = SnackbarHostState(),
onLoginClick: (String) -> Unit = {},
onCreateAccountClick: () -> Unit = {},
onDeleteHistoryClick: () -> Unit = {},
onAccountNumberChange: (String) -> Unit = {},
onSettingsClick: () -> Unit = {},
) {
ScaffoldWithTopBar(
snackbarHostState = snackbarHostState,
topBarColor = MaterialTheme.colorScheme.primary,
iconTintColor = MaterialTheme.colorScheme.onPrimary,
onSettingsClicked = onSettingsClick,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
Expand Down Expand Up @@ -49,6 +52,7 @@ import net.mullvad.mullvadvpn.compose.state.OutOfTimeUiState
import net.mullvad.mullvadvpn.compose.test.OUT_OF_TIME_SCREEN_TITLE_TEST_TAG
import net.mullvad.mullvadvpn.compose.transitions.HomeTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.lib.model.ErrorStateCause
import net.mullvad.mullvadvpn.lib.model.TunnelState
Expand Down Expand Up @@ -135,6 +139,8 @@ fun OutOfTime(
}
}

val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
CollectSideEffectWithLifecycle(vm.uiSideEffect, Lifecycle.State.RESUMED) { uiSideEffect ->
when (uiSideEffect) {
Expand All @@ -145,11 +151,16 @@ fun OutOfTime(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
OutOfTimeViewModel.UiSideEffect.GenericError ->
snackbarHostState.showSnackbarImmediately(
message = context.getString(R.string.error_occurred)
)
}
}

OutOfTimeScreen(
state = state,
snackbarHostState = snackbarHostState,
onSitePaymentClick = vm::onSitePaymentClick,
onRedeemVoucherClick = dropUnlessResumed { navigator.navigate(RedeemVoucherDestination) },
onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) },
Expand All @@ -165,6 +176,7 @@ fun OutOfTime(
@Composable
fun OutOfTimeScreen(
state: OutOfTimeUiState,
snackbarHostState: SnackbarHostState = SnackbarHostState(),
onDisconnectClick: () -> Unit = {},
onSitePaymentClick: () -> Unit = {},
onRedeemVoucherClick: () -> Unit = {},
Expand All @@ -176,6 +188,7 @@ fun OutOfTimeScreen(

val scrollState = rememberScrollState()
ScaffoldWithTopBarAndDeviceName(
snackbarHostState = snackbarHostState,
topBarColor =
if (state.tunnelState.isSecured()) {
MaterialTheme.colorScheme.tertiary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -57,6 +58,7 @@ import net.mullvad.mullvadvpn.compose.state.WelcomeUiState
import net.mullvad.mullvadvpn.compose.transitions.HomeTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.createCopyToClipboardHandle
import net.mullvad.mullvadvpn.compose.util.showSnackbarImmediately
import net.mullvad.mullvadvpn.lib.common.util.groupWithSpaces
import net.mullvad.mullvadvpn.lib.model.AccountNumber
import net.mullvad.mullvadvpn.lib.payment.model.PaymentProduct
Expand Down Expand Up @@ -131,6 +133,8 @@ fun Welcome(
}
}

val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
val openAccountPage = LocalUriHandler.current.createOpenAccountPageHook()
CollectSideEffectWithLifecycle(sideEffect = vm.uiSideEffect, Lifecycle.State.RESUMED) {
uiSideEffect ->
Expand All @@ -141,11 +145,16 @@ fun Welcome(
launchSingleTop = true
popUpTo(NavGraphs.root) { inclusive = true }
}
WelcomeViewModel.UiSideEffect.GenericError ->
snackbarHostState.showSnackbarImmediately(
message = context.getString(R.string.error_occurred)
)
}
}

WelcomeScreen(
state = state,
snackbarHostState = snackbarHostState,
onSitePaymentClick = dropUnlessResumed { vm.onSitePaymentClick() },
onRedeemVoucherClick = dropUnlessResumed { navigator.navigate(RedeemVoucherDestination) },
onSettingsClick = dropUnlessResumed { navigator.navigate(SettingsDestination) },
Expand All @@ -163,6 +172,7 @@ fun Welcome(
@Composable
fun WelcomeScreen(
state: WelcomeUiState,
snackbarHostState: SnackbarHostState = SnackbarHostState(),
onSitePaymentClick: () -> Unit,
onRedeemVoucherClick: () -> Unit,
onSettingsClick: () -> Unit,
Expand All @@ -173,7 +183,6 @@ fun WelcomeScreen(
navigateToVerificationPendingDialog: () -> Unit
) {
val scrollState = rememberScrollState()
val snackbarHostState = remember { SnackbarHostState() }

ScaffoldWithTopBar(
topBarColor = MaterialTheme.colorScheme.primary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ class AccountViewModel(

fun onLogoutClick() {
viewModelScope.launch {
accountRepository.logout()
_uiSideEffect.send(UiSideEffect.NavigateToLogin)
accountRepository
.logout()
.fold(
{ _uiSideEffect.send(UiSideEffect.GenericError) },
{ _uiSideEffect.send(UiSideEffect.NavigateToLogin) }
)
}
}

Expand Down Expand Up @@ -127,6 +131,8 @@ class AccountViewModel(
UiSideEffect()

data class CopyAccountNumber(val accountNumber: String) : UiSideEffect()

data object GenericError : UiSideEffect()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,19 @@ class ConnectViewModel(
}

fun onDisconnectClick() {
viewModelScope.launch { connectionProxy.disconnect() }
viewModelScope.launch {
connectionProxy.disconnect().onLeft {
_uiSideEffect.send(UiSideEffect.ConnectError.Generic)
}
}
}

fun onReconnectClick() {
viewModelScope.launch { connectionProxy.reconnect() }
viewModelScope.launch {
connectionProxy.reconnect().onLeft {
_uiSideEffect.send(UiSideEffect.ConnectError.Generic)
}
}
}

fun onConnectClick() {
Expand Down Expand Up @@ -156,7 +164,11 @@ class ConnectViewModel(
}

fun onCancelClick() {
viewModelScope.launch { connectionProxy.disconnect() }
viewModelScope.launch {
connectionProxy.disconnect().onLeft {
_uiSideEffect.send(UiSideEffect.ConnectError.Generic)
}
}
}

fun onManageAccountClick() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ sealed interface LoginUiSideEffect {
data object NavigateToOutOfTime : LoginUiSideEffect

data class TooManyDevices(val accountNumber: AccountNumber) : LoginUiSideEffect

data object GenericError : LoginUiSideEffect
}

class LoginViewModel(
Expand Down Expand Up @@ -78,9 +80,15 @@ class LoginViewModel(

fun clearAccountHistory() =
viewModelScope.launch {
accountRepository.clearAccountHistory()
_mutableAccountHistory.update { null }
_mutableAccountHistory.update { accountRepository.fetchAccountHistory() }
accountRepository
.clearAccountHistory()
.fold(
{ _uiSideEffect.send(LoginUiSideEffect.GenericError) },
{
_mutableAccountHistory.update { null }
_mutableAccountHistory.update { accountRepository.fetchAccountHistory() }
}
)
}

fun createAccount() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import net.mullvad.mullvadvpn.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.util.isSuccess
import net.mullvad.mullvadvpn.util.toPaymentState
import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel.UiSideEffect

class OutOfTimeViewModel(
private val accountRepository: AccountRepository,
Expand Down Expand Up @@ -71,7 +72,9 @@ class OutOfTimeViewModel(
}

fun onDisconnectClick() {
viewModelScope.launch { connectionProxy.disconnect() }
viewModelScope.launch {
connectionProxy.disconnect().onLeft { _uiSideEffect.send(UiSideEffect.GenericError) }
}
}

private fun verifyPurchases() {
Expand Down Expand Up @@ -118,5 +121,7 @@ class OutOfTimeViewModel(
data class OpenAccountView(val token: WebsiteAuthToken?) : UiSideEffect

data object OpenConnectScreen : UiSideEffect

data object GenericError : UiSideEffect
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ class WelcomeViewModel(
}

fun onDisconnectClick() {
viewModelScope.launch { connectionProxy.disconnect() }
viewModelScope.launch {
connectionProxy.disconnect().onLeft { _uiSideEffect.send(UiSideEffect.GenericError) }
}
}

private fun verifyPurchases() {
Expand Down Expand Up @@ -118,6 +120,8 @@ class WelcomeViewModel(
data class OpenAccountView(val token: WebsiteAuthToken?) : UiSideEffect

data object OpenConnectScreen : UiSideEffect

data object GenericError : UiSideEffect
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.viewmodel

import android.app.Activity
import app.cash.turbine.test
import arrow.core.right
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
Expand Down Expand Up @@ -93,6 +94,9 @@ class AccountViewModelTest {

@Test
fun `onLogoutClick should invoke logout on AccountRepository`() {
// Arrange
coEvery { mockAccountRepository.logout() } returns Unit.right()

// Act
viewModel.onLogoutClick()

Expand Down
Loading
Loading