Skip to content

Commit

Permalink
Merge branch 'ensure-all-potential-grpc-errors-are-wrapped-droid-1170'
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-mullvad committed Aug 23, 2024
2 parents 67486d3 + 4c7a6fa commit 97ee66d
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 53 deletions.
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

0 comments on commit 97ee66d

Please sign in to comment.