Skip to content

Commit

Permalink
[PM-16695] Learn more new device verification (#4527)
Browse files Browse the repository at this point in the history
Co-authored-by: André Bispo <[email protected]>
  • Loading branch information
aj-rosado and andrebispo5 authored Jan 9, 2025
1 parent 6b6e95a commit b5d73c9
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

/**
Expand All @@ -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(),
)
}
}
}

Expand All @@ -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) }
},
)
}
}
Expand All @@ -76,6 +94,7 @@ private fun NewDeviceNoticeEmailAccessContent(
isEmailAccessEnabled: Boolean,
onEmailAccessToggleChanged: (Boolean) -> Unit,
onContinueClick: () -> Unit,
onLearnMoreClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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"),
)
}

/**
Expand Down Expand Up @@ -183,6 +211,7 @@ private fun NewDeviceNoticeEmailAccessScreen_preview() {
isEmailAccessEnabled = true,
onEmailAccessToggleChanged = {},
onContinueClick = {},
onLearnMoreClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -42,6 +43,7 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
when (action) {
ContinueClick -> handleContinueClick()
is EmailAccessToggle -> handleEmailAccessToggle(action)
LearnMoreClick -> handleLearnMoreClick()
}
}

Expand Down Expand Up @@ -71,6 +73,10 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
it.copy(isEmailAccessEnabled = action.isEnabled)
}
}

private fun handleLearnMoreClick() {
sendEvent(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore)
}
}

/**
Expand All @@ -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()
}

/**
Expand All @@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,13 +31,18 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
every { eventFlow } returns mutableEventFlow
}

private val intentManager: IntentManager = mockk {
every { launchUri(any()) } just runs
}

@Before
fun setUp() {
composeTestRule.setContent {
NewDeviceNoticeEmailAccessScreen(
onNavigateBackToVault = { onNavigateBackToVaultCalled = true },
onNavigateToTwoFactorOptions = { onNavigateToTwoFactorOptionsCalled = true },
viewModel = viewModel,
intentManager = intentManager,
)
}
}
Expand Down Expand Up @@ -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 = "[email protected]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b5d73c9

Please sign in to comment.