mirror of
https://github.com/bitwarden/android.git
synced 2026-03-22 12:32:53 -05:00
[PM-16695] Learn more new device verification (#4527)
Co-authored-by: André Bispo <abispo@bitwarden.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -24,19 +25,24 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.LearnMoreClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText
|
||||
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
|
||||
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
|
||||
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
|
||||
/**
|
||||
@@ -47,12 +53,19 @@ fun NewDeviceNoticeEmailAccessScreen(
|
||||
onNavigateBackToVault: () -> Unit,
|
||||
onNavigateToTwoFactorOptions: () -> Unit,
|
||||
viewModel: NewDeviceNoticeEmailAccessViewModel = hiltViewModel(),
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
NavigateToTwoFactorOptions -> onNavigateToTwoFactorOptions()
|
||||
NewDeviceNoticeEmailAccessEvent.NavigateBackToVault -> onNavigateBackToVault()
|
||||
is NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore -> {
|
||||
intentManager.launchUri(
|
||||
"https://bitwarden.com/help/new-device-verification/"
|
||||
.toUri(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +78,12 @@ fun NewDeviceNoticeEmailAccessScreen(
|
||||
viewModel.trySendAction(EmailAccessToggle(isEnabled = newState))
|
||||
}
|
||||
},
|
||||
onContinueClick = { viewModel.trySendAction(ContinueClick) },
|
||||
onContinueClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(ContinueClick) }
|
||||
},
|
||||
onLearnMoreClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(LearnMoreClick) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -76,6 +94,7 @@ private fun NewDeviceNoticeEmailAccessContent(
|
||||
isEmailAccessEnabled: Boolean,
|
||||
onEmailAccessToggleChanged: (Boolean) -> Unit,
|
||||
onContinueClick: () -> Unit,
|
||||
onLearnMoreClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
@@ -86,7 +105,7 @@ private fun NewDeviceNoticeEmailAccessContent(
|
||||
.verticalScroll(state = rememberScrollState()),
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(104.dp))
|
||||
HeaderContent()
|
||||
HeaderContent(onLearnMoreClick = onLearnMoreClick)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
MainContent(
|
||||
email = email,
|
||||
@@ -110,7 +129,9 @@ private fun NewDeviceNoticeEmailAccessContent(
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
@Composable
|
||||
private fun ColumnScope.HeaderContent() {
|
||||
private fun ColumnScope.HeaderContent(
|
||||
onLearnMoreClick: () -> Unit,
|
||||
) {
|
||||
Image(
|
||||
painter = rememberVectorPainter(id = R.drawable.warning),
|
||||
contentDescription = null,
|
||||
@@ -132,6 +153,13 @@ private fun ColumnScope.HeaderContent() {
|
||||
color = BitwardenTheme.colorScheme.text.primary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
BitwardenClickableText(
|
||||
label = stringResource(id = R.string.learn_more),
|
||||
onClick = onLearnMoreClick,
|
||||
style = BitwardenTheme.typography.labelLarge,
|
||||
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
|
||||
modifier = Modifier.testTag("LearnMoreLabel"),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,6 +211,7 @@ private fun NewDeviceNoticeEmailAccessScreen_preview() {
|
||||
isEmailAccessEnabled = true,
|
||||
onEmailAccessToggleChanged = {},
|
||||
onContinueClick = {},
|
||||
onLearnMoreClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.LearnMoreClick
|
||||
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -42,6 +43,7 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
|
||||
when (action) {
|
||||
ContinueClick -> handleContinueClick()
|
||||
is EmailAccessToggle -> handleEmailAccessToggle(action)
|
||||
LearnMoreClick -> handleLearnMoreClick()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +73,10 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
|
||||
it.copy(isEmailAccessEnabled = action.isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLearnMoreClick() {
|
||||
sendEvent(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +101,11 @@ sealed class NewDeviceNoticeEmailAccessEvent {
|
||||
* Navigates back.
|
||||
*/
|
||||
data object NavigateBackToVault : NewDeviceNoticeEmailAccessEvent()
|
||||
|
||||
/**
|
||||
* Navigates to learn more about New Device Login Protection
|
||||
*/
|
||||
data object NavigateToLearnMore : NewDeviceNoticeEmailAccessEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,4 +121,9 @@ sealed class NewDeviceNoticeEmailAccessAction {
|
||||
* User tapped the email access toggle.
|
||||
*/
|
||||
data class EmailAccessToggle(val isEnabled: Boolean) : NewDeviceNoticeEmailAccessAction()
|
||||
|
||||
/**
|
||||
* User tapped the learn more button.
|
||||
*/
|
||||
data object LearnMoreClick : NewDeviceNoticeEmailAccessAction()
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.core.net.toUri
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -27,6 +31,10 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
}
|
||||
|
||||
private val intentManager: IntentManager = mockk {
|
||||
every { launchUri(any()) } just runs
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
composeTestRule.setContent {
|
||||
@@ -34,6 +42,7 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
|
||||
onNavigateBackToVault = { onNavigateBackToVaultCalled = true },
|
||||
onNavigateToTwoFactorOptions = { onNavigateToTwoFactorOptionsCalled = true },
|
||||
viewModel = viewModel,
|
||||
intentManager = intentManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -91,6 +100,14 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
|
||||
mutableEventFlow.tryEmit(NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions)
|
||||
assertTrue(onNavigateToTwoFactorOptionsCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToLearnMore should call launchUri on IntentManager`() {
|
||||
mutableEventFlow.tryEmit(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore)
|
||||
verify {
|
||||
intentManager.launchUri("https://bitwarden.com/help/new-device-verification/".toUri())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val EMAIL = "active@bitwarden.com"
|
||||
|
||||
@@ -110,6 +110,16 @@ class NewDeviceNoticeEmailAccessViewModelTest : BaseViewModelTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LearnMoreClick should emit NavigateToLearnMore`() =
|
||||
runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(NewDeviceNoticeEmailAccessAction.LearnMoreClick)
|
||||
assertEquals(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
savedStateHandle: SavedStateHandle = SavedStateHandle().also {
|
||||
it["email_address"] = EMAIL
|
||||
|
||||
Reference in New Issue
Block a user