mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 19:28:44 +03:00
Settings boilerplate (#170)
This commit is contained in:
parent
d0e0362771
commit
0af6e7f826
35 changed files with 1205 additions and 35 deletions
|
@ -5,6 +5,18 @@ import androidx.navigation.NavGraphBuilder
|
|||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.navigation
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.about.aboutDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.about.navigateToAbout
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.accountSecurityDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity.navigateToAccountSecurity
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.appearanceDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.navigateToAppearance
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.autoFillDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.autofill.navigateToAutoFill
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.other.navigateToOther
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.other.otherDestination
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.vault.navigateToVault
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.vault.vaultDestination
|
||||
|
||||
const val SETTINGS_GRAPH_ROUTE: String = "settings_graph"
|
||||
private const val SETTINGS_ROUTE: String = "settings"
|
||||
|
@ -21,10 +33,20 @@ fun NavGraphBuilder.settingsGraph(
|
|||
) {
|
||||
composable(SETTINGS_ROUTE) {
|
||||
SettingsScreen(
|
||||
onNavigateToAbout = { navController.navigateToAbout() },
|
||||
onNavigateToAccountSecurity = { navController.navigateToAccountSecurity() },
|
||||
onNavigateToAppearance = { navController.navigateToAppearance() },
|
||||
onNavigateToAutoFill = { navController.navigateToAutoFill() },
|
||||
onNavigateToOther = { navController.navigateToOther() },
|
||||
onNavigateToVault = { navController.navigateToVault() },
|
||||
)
|
||||
}
|
||||
aboutDestination(onNavigateBack = { navController.popBackStack() })
|
||||
accountSecurityDestination(onNavigateBack = { navController.popBackStack() })
|
||||
appearanceDestination(onNavigateBack = { navController.popBackStack() })
|
||||
autoFillDestination(onNavigateBack = { navController.popBackStack() })
|
||||
otherDestination(onNavigateBack = { navController.popBackStack() })
|
||||
vaultDestination(onNavigateBack = { navController.popBackStack() })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,12 +44,22 @@ import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
onNavigateToAbout: () -> Unit,
|
||||
onNavigateToAccountSecurity: () -> Unit,
|
||||
onNavigateToAppearance: () -> Unit,
|
||||
onNavigateToAutoFill: () -> Unit,
|
||||
onNavigateToOther: () -> Unit,
|
||||
onNavigateToVault: () -> Unit,
|
||||
viewModel: SettingsViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
SettingsEvent.NavigateAbout -> onNavigateToAbout()
|
||||
SettingsEvent.NavigateAccountSecurity -> onNavigateToAccountSecurity.invoke()
|
||||
SettingsEvent.NavigateAppearance -> onNavigateToAppearance()
|
||||
SettingsEvent.NavigateAutoFill -> onNavigateToAutoFill()
|
||||
SettingsEvent.NavigateOther -> onNavigateToOther()
|
||||
SettingsEvent.NavigateVault -> onNavigateToVault()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,23 +26,23 @@ class SettingsViewModel @Inject constructor() : BaseViewModel<Unit, SettingsEven
|
|||
}
|
||||
|
||||
Settings.AUTO_FILL -> {
|
||||
// TODO: BIT-927 Launch auto-fill UI
|
||||
sendEvent(SettingsEvent.NavigateAutoFill)
|
||||
}
|
||||
|
||||
Settings.VAULT -> {
|
||||
// TODO: BIT-928 Launch vault UI
|
||||
sendEvent(SettingsEvent.NavigateVault)
|
||||
}
|
||||
|
||||
Settings.APPEARANCE -> {
|
||||
// TODO: BIT-929 Launch appearance UI
|
||||
sendEvent(SettingsEvent.NavigateAppearance)
|
||||
}
|
||||
|
||||
Settings.OTHER -> {
|
||||
// TODO: BIT-930 Launch other UI
|
||||
sendEvent(SettingsEvent.NavigateOther)
|
||||
}
|
||||
|
||||
Settings.ABOUT -> {
|
||||
// TODO: BIT-931 Launch about UI
|
||||
sendEvent(SettingsEvent.NavigateAbout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,10 +52,35 @@ class SettingsViewModel @Inject constructor() : BaseViewModel<Unit, SettingsEven
|
|||
* Models events for the settings screen.
|
||||
*/
|
||||
sealed class SettingsEvent {
|
||||
/**
|
||||
* Navigate to the about screen.
|
||||
*/
|
||||
data object NavigateAbout : SettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the account security screen.
|
||||
*/
|
||||
data object NavigateAccountSecurity : SettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the appearance screen.
|
||||
*/
|
||||
data object NavigateAppearance : SettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the auto-fill screen.
|
||||
*/
|
||||
data object NavigateAutoFill : SettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the other screen.
|
||||
*/
|
||||
data object NavigateOther : SettingsEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the vault screen.
|
||||
*/
|
||||
data object NavigateVault : SettingsEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,12 +100,11 @@ sealed class SettingsAction {
|
|||
*
|
||||
* @property text The [Text] of the string that represents the label of each setting.
|
||||
*/
|
||||
// TODO: BIT-944 Missing correct resources for "Account Security", "Vault", and "Appearance".
|
||||
enum class Settings(val text: Text) {
|
||||
ACCOUNT_SECURITY(R.string.security.asText()),
|
||||
ACCOUNT_SECURITY(R.string.account_security.asText()),
|
||||
AUTO_FILL(R.string.autofill.asText()),
|
||||
VAULT(R.string.vaults.asText()),
|
||||
APPEARANCE(R.string.language.asText()),
|
||||
VAULT(R.string.vault.asText()),
|
||||
APPEARANCE(R.string.appearance.asText()),
|
||||
OTHER(R.string.other.asText()),
|
||||
ABOUT(R.string.about.asText()),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val ABOUT_ROUTE = "settings_about"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.aboutDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composable(ABOUT_ROUTE) {
|
||||
AboutScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the about screen.
|
||||
*/
|
||||
fun NavController.navigateToAbout(navOptions: NavOptions? = null) {
|
||||
navigate(ABOUT_ROUTE, navOptions)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the about screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AboutScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: AboutViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
AboutEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.about),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AboutAction.BackClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-931 Display About UI
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the about screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class AboutViewModel @Inject constructor() : BaseViewModel<Unit, AboutEvent, AboutAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: AboutAction): Unit = when (action) {
|
||||
AboutAction.BackClick -> sendEvent(AboutEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the about screen.
|
||||
*/
|
||||
sealed class AboutEvent {
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : AboutEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the about screen.
|
||||
*/
|
||||
sealed class AboutAction {
|
||||
/**
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : AboutAction()
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val ACCOUNT_SECURITY_ROUTE = "account_security"
|
||||
private const val ACCOUNT_SECURITY_ROUTE = "settings_account_security"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
@ -30,7 +30,6 @@ import com.x8bit.bitwarden.R
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
|
@ -54,16 +53,13 @@ fun AccountSecurityScreen(
|
|||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.account),
|
||||
title = stringResource(id = R.string.account_security),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AccountSecurityAction.BackClick) }
|
||||
},
|
||||
actions = {
|
||||
BitwardenOverflowActionItem()
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val APPEARANCE_ROUTE = "settings_appearance"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.appearanceDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composable(APPEARANCE_ROUTE) {
|
||||
AppearanceScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the appearance screen.
|
||||
*/
|
||||
fun NavController.navigateToAppearance(navOptions: NavOptions? = null) {
|
||||
navigate(APPEARANCE_ROUTE, navOptions)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the appearance screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AppearanceScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: AppearanceViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
AppearanceEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.appearance),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AppearanceAction.BackClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-929 Display Appearance UI
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the appearance screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class AppearanceViewModel @Inject constructor() :
|
||||
BaseViewModel<Unit, AppearanceEvent, AppearanceAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: AppearanceAction): Unit = when (action) {
|
||||
AppearanceAction.BackClick -> sendEvent(AppearanceEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the appearance screen.
|
||||
*/
|
||||
sealed class AppearanceEvent {
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : AppearanceEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the appearance screen.
|
||||
*/
|
||||
sealed class AppearanceAction {
|
||||
/**
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : AppearanceAction()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val AUTO_FILL_ROUTE = "settings_auto_fill"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.autoFillDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composable(AUTO_FILL_ROUTE) {
|
||||
AutoFillScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the auto-fill screen.
|
||||
*/
|
||||
fun NavController.navigateToAutoFill(navOptions: NavOptions? = null) {
|
||||
navigate(AUTO_FILL_ROUTE, navOptions)
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the auto-fill screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AutoFillScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: AutoFillViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
AutoFillEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.autofill),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(AutoFillAction.BackClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-927 Display auto-fill UI
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the auto-fill screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class AutoFillViewModel @Inject constructor() : BaseViewModel<Unit, AutoFillEvent, AutoFillAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: AutoFillAction): Unit = when (action) {
|
||||
AutoFillAction.BackClick -> sendEvent(AutoFillEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the auto-fill screen.
|
||||
*/
|
||||
sealed class AutoFillEvent {
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : AutoFillEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the auto-fill screen.
|
||||
*/
|
||||
sealed class AutoFillAction {
|
||||
/**
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : AutoFillAction()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val OTHER_ROUTE = "settings_other"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.otherDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composable(OTHER_ROUTE) {
|
||||
OtherScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the about screen.
|
||||
*/
|
||||
fun NavController.navigateToOther(navOptions: NavOptions? = null) {
|
||||
navigate(OTHER_ROUTE, navOptions)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the other screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun OtherScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: OtherViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
OtherEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.other),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(OtherAction.BackClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-930 Display Other UI
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the other screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class OtherViewModel @Inject constructor() : BaseViewModel<Unit, OtherEvent, OtherAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: OtherAction): Unit = when (action) {
|
||||
OtherAction.BackClick -> sendEvent(OtherEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the other screen.
|
||||
*/
|
||||
sealed class OtherEvent {
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : OtherEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the other screen.
|
||||
*/
|
||||
sealed class OtherAction {
|
||||
/**
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : OtherAction()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.composable
|
||||
|
||||
private const val VAULT_ROUTE = "settings_vault"
|
||||
|
||||
/**
|
||||
* Add settings destinations to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.vaultDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composable(VAULT_ROUTE) {
|
||||
VaultScreen(onNavigateBack = onNavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the vault screen.
|
||||
*/
|
||||
fun NavController.navigateToVault(navOptions: NavOptions? = null) {
|
||||
navigate(VAULT_ROUTE, navOptions)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
|
||||
/**
|
||||
* Displays the vault screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun VaultScreen(
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: VaultViewModel = hiltViewModel(),
|
||||
) {
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
VaultEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.vault),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultAction.BackClick) }
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(innerPadding)
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// TODO: BIT-928 Display Vault UI
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View model for the vault screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class VaultViewModel @Inject constructor() : BaseViewModel<Unit, VaultEvent, VaultAction>(
|
||||
initialState = Unit,
|
||||
) {
|
||||
override fun handleAction(action: VaultAction): Unit = when (action) {
|
||||
VaultAction.BackClick -> sendEvent(VaultEvent.NavigateBack)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Models events for the vault screen.
|
||||
*/
|
||||
sealed class VaultEvent {
|
||||
/**
|
||||
* Navigate back.
|
||||
*/
|
||||
data object NavigateBack : VaultEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Models actions for the vault screen.
|
||||
*/
|
||||
sealed class VaultAction {
|
||||
/**
|
||||
* User clicked back button.
|
||||
*/
|
||||
data object BackClick : VaultAction()
|
||||
}
|
|
@ -8,8 +8,8 @@ import io.mockk.mockk
|
|||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
|
||||
class SettingsScreenTest : BaseComposeTest() {
|
||||
|
||||
|
@ -22,7 +22,12 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("About").performClick()
|
||||
|
@ -40,10 +45,15 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Security").performClick()
|
||||
composeTestRule.onNodeWithText("Account security").performClick()
|
||||
verify { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.ACCOUNT_SECURITY)) }
|
||||
}
|
||||
|
||||
|
@ -56,10 +66,15 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Language").performClick()
|
||||
composeTestRule.onNodeWithText("Appearance").performClick()
|
||||
verify { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.APPEARANCE)) }
|
||||
}
|
||||
|
||||
|
@ -72,7 +87,12 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Auto-fill").performClick()
|
||||
|
@ -88,7 +108,12 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Other").performClick()
|
||||
|
@ -104,13 +129,40 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithText("Vaults").performClick()
|
||||
composeTestRule.onNodeWithText("Vault").performClick()
|
||||
verify { viewModel.trySendAction(SettingsAction.SettingsClick(Settings.VAULT)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToAbout`() {
|
||||
var haveCalledNavigateToAbout = false
|
||||
val viewModel = mockk<SettingsViewModel> {
|
||||
every { eventFlow } returns flowOf(SettingsEvent.NavigateAbout)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = {
|
||||
haveCalledNavigateToAbout = true
|
||||
},
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToAbout)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAccountSecurity should call onNavigateToAccountSecurity`() {
|
||||
var haveCalledNavigateToAccountSecurity = false
|
||||
|
@ -120,11 +172,102 @@ class SettingsScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = {
|
||||
haveCalledNavigateToAccountSecurity = true
|
||||
},
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToAccountSecurity)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAccountSecurity should call NavigateAppearance`() {
|
||||
var haveCalledNavigateToAppearance = false
|
||||
val viewModel = mockk<SettingsViewModel> {
|
||||
every { eventFlow } returns flowOf(SettingsEvent.NavigateAppearance)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { haveCalledNavigateToAppearance = true },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToAppearance)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAccountSecurity should call onNavigateToAutoFill`() {
|
||||
var haveCalledNavigateToAutoFill = false
|
||||
val viewModel = mockk<SettingsViewModel> {
|
||||
every { eventFlow } returns flowOf(SettingsEvent.NavigateAutoFill)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = {
|
||||
haveCalledNavigateToAutoFill = true
|
||||
},
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToAutoFill)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAccountSecurity should call onNavigateToOther`() {
|
||||
var haveCalledNavigateToOther = false
|
||||
val viewModel = mockk<SettingsViewModel> {
|
||||
every { eventFlow } returns flowOf(SettingsEvent.NavigateOther)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = {
|
||||
haveCalledNavigateToOther = true
|
||||
},
|
||||
onNavigateToVault = { },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToOther)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAccountSecurity should call NavigateVault`() {
|
||||
var haveCalledNavigateToVault = false
|
||||
val viewModel = mockk<SettingsViewModel> {
|
||||
every { eventFlow } returns flowOf(SettingsEvent.NavigateVault)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
SettingsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateToAbout = { },
|
||||
onNavigateToAccountSecurity = { },
|
||||
onNavigateToAppearance = { },
|
||||
onNavigateToAutoFill = { },
|
||||
onNavigateToOther = { },
|
||||
onNavigateToVault = {
|
||||
haveCalledNavigateToVault = true
|
||||
},
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateToVault)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import org.junit.jupiter.api.Test
|
|||
class SettingsViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on SettingsClick with ABOUT should emit nothing`() = runTest {
|
||||
fun `on SettingsClick with ABOUT should emit NavigateAbout`() = runTest {
|
||||
val viewModel = SettingsViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SettingsAction.SettingsClick(Settings.ABOUT))
|
||||
expectNoEvents()
|
||||
assertEquals(SettingsEvent.NavigateAbout, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,38 +27,38 @@ class SettingsViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `on SettingsClick with APPEARANCE should emit nothing`() = runTest {
|
||||
fun `on SettingsClick with APPEARANCE should emit NavigateAppearance`() = runTest {
|
||||
val viewModel = SettingsViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SettingsAction.SettingsClick(Settings.APPEARANCE))
|
||||
expectNoEvents()
|
||||
assertEquals(SettingsEvent.NavigateAppearance, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SettingsClick with AUTO_FILL should emit nothing`() = runTest {
|
||||
fun `on SettingsClick with AUTO_FILL should emit NavigateAutoFill`() = runTest {
|
||||
val viewModel = SettingsViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SettingsAction.SettingsClick(Settings.AUTO_FILL))
|
||||
expectNoEvents()
|
||||
assertEquals(SettingsEvent.NavigateAutoFill, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SettingsClick with OTHER should emit nothing`() = runTest {
|
||||
fun `on SettingsClick with OTHER should emit NavigateOther`() = runTest {
|
||||
val viewModel = SettingsViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SettingsAction.SettingsClick(Settings.OTHER))
|
||||
expectNoEvents()
|
||||
assertEquals(SettingsEvent.NavigateOther, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on SettingsClick with VAULT should emit nothing`() = runTest {
|
||||
fun `on SettingsClick with VAULT should emit NavigateVault`() = runTest {
|
||||
val viewModel = SettingsViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(SettingsAction.SettingsClick(Settings.VAULT))
|
||||
expectNoEvents()
|
||||
assertEquals(SettingsEvent.NavigateVault, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AboutScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: AboutViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AboutAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(AboutAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToAbout`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<AboutViewModel> {
|
||||
every { eventFlow } returns flowOf(AboutEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AboutScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.about
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AboutViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = AboutViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AboutAction.BackClick)
|
||||
assertEquals(AboutEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
|
@ -9,8 +9,8 @@ import io.mockk.mockk
|
|||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
|
||||
class AccountSecurityScreenTest : BaseComposeTest() {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings
|
||||
package com.x8bit.bitwarden.ui.platform.feature.settings.accountsecurity
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
|
@ -0,0 +1,46 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AppearanceScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: AppearanceViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AppearanceAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AppearanceScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(AppearanceAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToAbout`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<AppearanceViewModel> {
|
||||
every { eventFlow } returns flowOf(AppearanceEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AppearanceScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.appearance
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AppearanceViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = AppearanceViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AppearanceAction.BackClick)
|
||||
assertEquals(AppearanceEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class AutoFillScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: AutoFillViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(AutoFillAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AutoFillScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(AutoFillAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToAutoFill`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<AutoFillViewModel> {
|
||||
every { eventFlow } returns flowOf(AutoFillEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
AutoFillScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.autofill
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AutoFillViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = AutoFillViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AutoFillAction.BackClick)
|
||||
assertEquals(AutoFillEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class OtherScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: OtherViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(OtherAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
OtherScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(OtherAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateOther should call onNavigateToOther`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<OtherViewModel> {
|
||||
every { eventFlow } returns flowOf(OtherEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
OtherScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.other
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class OtherViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = OtherViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(OtherAction.BackClick)
|
||||
assertEquals(OtherEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.performClick
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class VaultScreenTest : BaseComposeTest() {
|
||||
|
||||
@Test
|
||||
fun `on back click should send BackClick`() {
|
||||
val viewModel: VaultViewModel = mockk {
|
||||
every { eventFlow } returns emptyFlow()
|
||||
every { trySendAction(VaultAction.BackClick) } returns Unit
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
VaultScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { },
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithContentDescription("Back").performClick()
|
||||
verify { viewModel.trySendAction(VaultAction.BackClick) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateAbout should call onNavigateToVault`() {
|
||||
var haveCalledNavigateBack = false
|
||||
val viewModel = mockk<VaultViewModel> {
|
||||
every { eventFlow } returns flowOf(VaultEvent.NavigateBack)
|
||||
}
|
||||
composeTestRule.setContent {
|
||||
VaultScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { haveCalledNavigateBack = true },
|
||||
)
|
||||
}
|
||||
assertTrue(haveCalledNavigateBack)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.feature.settings.vault
|
||||
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `on BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = VaultViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(VaultAction.BackClick)
|
||||
assertEquals(VaultEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue