Skip to content

Commit b5d73c9

Browse files
[PM-16695] Learn more new device verification (#4527)
Co-authored-by: André Bispo <abispo@bitwarden.com>
1 parent 6b6e95a commit b5d73c9

File tree

4 files changed

+75
-3
lines changed

4 files changed

+75
-3
lines changed

app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreen.kt

+32-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.auth.feature.newdevicenotice
33
import androidx.compose.foundation.Image
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.ColumnScope
6+
import androidx.compose.foundation.layout.PaddingValues
67
import androidx.compose.foundation.layout.Spacer
78
import androidx.compose.foundation.layout.fillMaxSize
89
import androidx.compose.foundation.layout.height
@@ -24,19 +25,24 @@ import androidx.compose.ui.text.font.FontWeight
2425
import androidx.compose.ui.text.style.TextAlign
2526
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
2627
import androidx.compose.ui.unit.dp
28+
import androidx.core.net.toUri
2729
import androidx.hilt.navigation.compose.hiltViewModel
2830
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2931
import com.x8bit.bitwarden.R
3032
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
3133
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
34+
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.LearnMoreClick
3235
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
3336
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
3437
import com.x8bit.bitwarden.ui.platform.base.util.standardHorizontalMargin
3538
import com.x8bit.bitwarden.ui.platform.base.util.toAnnotatedString
3639
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenFilledButton
3740
import com.x8bit.bitwarden.ui.platform.components.scaffold.BitwardenScaffold
41+
import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText
3842
import com.x8bit.bitwarden.ui.platform.components.toggle.BitwardenSwitch
3943
import com.x8bit.bitwarden.ui.platform.components.util.rememberVectorPainter
44+
import com.x8bit.bitwarden.ui.platform.composition.LocalIntentManager
45+
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
4046
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
4147

4248
/**
@@ -47,12 +53,19 @@ fun NewDeviceNoticeEmailAccessScreen(
4753
onNavigateBackToVault: () -> Unit,
4854
onNavigateToTwoFactorOptions: () -> Unit,
4955
viewModel: NewDeviceNoticeEmailAccessViewModel = hiltViewModel(),
56+
intentManager: IntentManager = LocalIntentManager.current,
5057
) {
5158
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
5259
EventsEffect(viewModel = viewModel) { event ->
5360
when (event) {
5461
NavigateToTwoFactorOptions -> onNavigateToTwoFactorOptions()
5562
NewDeviceNoticeEmailAccessEvent.NavigateBackToVault -> onNavigateBackToVault()
63+
is NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore -> {
64+
intentManager.launchUri(
65+
"https://bitwarden.com/help/new-device-verification/"
66+
.toUri(),
67+
)
68+
}
5669
}
5770
}
5871

@@ -65,7 +78,12 @@ fun NewDeviceNoticeEmailAccessScreen(
6578
viewModel.trySendAction(EmailAccessToggle(isEnabled = newState))
6679
}
6780
},
68-
onContinueClick = { viewModel.trySendAction(ContinueClick) },
81+
onContinueClick = remember(viewModel) {
82+
{ viewModel.trySendAction(ContinueClick) }
83+
},
84+
onLearnMoreClick = remember(viewModel) {
85+
{ viewModel.trySendAction(LearnMoreClick) }
86+
},
6987
)
7088
}
7189
}
@@ -76,6 +94,7 @@ private fun NewDeviceNoticeEmailAccessContent(
7694
isEmailAccessEnabled: Boolean,
7795
onEmailAccessToggleChanged: (Boolean) -> Unit,
7896
onContinueClick: () -> Unit,
97+
onLearnMoreClick: () -> Unit,
7998
modifier: Modifier = Modifier,
8099
) {
81100
Column(
@@ -86,7 +105,7 @@ private fun NewDeviceNoticeEmailAccessContent(
86105
.verticalScroll(state = rememberScrollState()),
87106
) {
88107
Spacer(modifier = Modifier.height(104.dp))
89-
HeaderContent()
108+
HeaderContent(onLearnMoreClick = onLearnMoreClick)
90109
Spacer(modifier = Modifier.height(24.dp))
91110
MainContent(
92111
email = email,
@@ -110,7 +129,9 @@ private fun NewDeviceNoticeEmailAccessContent(
110129
*/
111130
@Suppress("MaxLineLength")
112131
@Composable
113-
private fun ColumnScope.HeaderContent() {
132+
private fun ColumnScope.HeaderContent(
133+
onLearnMoreClick: () -> Unit,
134+
) {
114135
Image(
115136
painter = rememberVectorPainter(id = R.drawable.warning),
116137
contentDescription = null,
@@ -132,6 +153,13 @@ private fun ColumnScope.HeaderContent() {
132153
color = BitwardenTheme.colorScheme.text.primary,
133154
textAlign = TextAlign.Center,
134155
)
156+
BitwardenClickableText(
157+
label = stringResource(id = R.string.learn_more),
158+
onClick = onLearnMoreClick,
159+
style = BitwardenTheme.typography.labelLarge,
160+
innerPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp),
161+
modifier = Modifier.testTag("LearnMoreLabel"),
162+
)
135163
}
136164

137165
/**
@@ -183,6 +211,7 @@ private fun NewDeviceNoticeEmailAccessScreen_preview() {
183211
isEmailAccessEnabled = true,
184212
onEmailAccessToggleChanged = {},
185213
onContinueClick = {},
214+
onLearnMoreClick = {},
186215
)
187216
}
188217
}

app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessViewModel.kt

+16
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
1010
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
1111
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.ContinueClick
1212
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.EmailAccessToggle
13+
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessAction.LearnMoreClick
1314
import com.x8bit.bitwarden.ui.auth.feature.newdevicenotice.NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions
1415
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
1516
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -42,6 +43,7 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
4243
when (action) {
4344
ContinueClick -> handleContinueClick()
4445
is EmailAccessToggle -> handleEmailAccessToggle(action)
46+
LearnMoreClick -> handleLearnMoreClick()
4547
}
4648
}
4749

@@ -71,6 +73,10 @@ class NewDeviceNoticeEmailAccessViewModel @Inject constructor(
7173
it.copy(isEmailAccessEnabled = action.isEnabled)
7274
}
7375
}
76+
77+
private fun handleLearnMoreClick() {
78+
sendEvent(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore)
79+
}
7480
}
7581

7682
/**
@@ -95,6 +101,11 @@ sealed class NewDeviceNoticeEmailAccessEvent {
95101
* Navigates back.
96102
*/
97103
data object NavigateBackToVault : NewDeviceNoticeEmailAccessEvent()
104+
105+
/**
106+
* Navigates to learn more about New Device Login Protection
107+
*/
108+
data object NavigateToLearnMore : NewDeviceNoticeEmailAccessEvent()
98109
}
99110

100111
/**
@@ -110,4 +121,9 @@ sealed class NewDeviceNoticeEmailAccessAction {
110121
* User tapped the email access toggle.
111122
*/
112123
data class EmailAccessToggle(val isEnabled: Boolean) : NewDeviceNoticeEmailAccessAction()
124+
125+
/**
126+
* User tapped the learn more button.
127+
*/
128+
data object LearnMoreClick : NewDeviceNoticeEmailAccessAction()
113129
}

app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessScreenTest.kt

+17
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ import androidx.compose.ui.test.assertIsOn
55
import androidx.compose.ui.test.onNodeWithText
66
import androidx.compose.ui.test.performClick
77
import androidx.compose.ui.test.performScrollTo
8+
import androidx.core.net.toUri
89
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
910
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
11+
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
1012
import io.mockk.every
13+
import io.mockk.just
1114
import io.mockk.mockk
15+
import io.mockk.runs
1216
import io.mockk.verify
1317
import junit.framework.TestCase.assertTrue
1418
import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,13 +31,18 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
2731
every { eventFlow } returns mutableEventFlow
2832
}
2933

34+
private val intentManager: IntentManager = mockk {
35+
every { launchUri(any()) } just runs
36+
}
37+
3038
@Before
3139
fun setUp() {
3240
composeTestRule.setContent {
3341
NewDeviceNoticeEmailAccessScreen(
3442
onNavigateBackToVault = { onNavigateBackToVaultCalled = true },
3543
onNavigateToTwoFactorOptions = { onNavigateToTwoFactorOptionsCalled = true },
3644
viewModel = viewModel,
45+
intentManager = intentManager,
3746
)
3847
}
3948
}
@@ -91,6 +100,14 @@ class NewDeviceNoticeEmailAccessScreenTest : BaseComposeTest() {
91100
mutableEventFlow.tryEmit(NewDeviceNoticeEmailAccessEvent.NavigateToTwoFactorOptions)
92101
assertTrue(onNavigateToTwoFactorOptionsCalled)
93102
}
103+
104+
@Test
105+
fun `on NavigateToLearnMore should call launchUri on IntentManager`() {
106+
mutableEventFlow.tryEmit(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore)
107+
verify {
108+
intentManager.launchUri("https://bitwarden.com/help/new-device-verification/".toUri())
109+
}
110+
}
94111
}
95112

96113
private const val EMAIL = "active@bitwarden.com"

app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/newdevicenotice/NewDeviceNoticeEmailAccessViewModelTest.kt

+10
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ class NewDeviceNoticeEmailAccessViewModelTest : BaseViewModelTest() {
110110
}
111111
}
112112

113+
@Test
114+
fun `LearnMoreClick should emit NavigateToLearnMore`() =
115+
runTest {
116+
val viewModel = createViewModel()
117+
viewModel.eventFlow.test {
118+
viewModel.trySendAction(NewDeviceNoticeEmailAccessAction.LearnMoreClick)
119+
assertEquals(NewDeviceNoticeEmailAccessEvent.NavigateToLearnMore, awaitItem())
120+
}
121+
}
122+
113123
private fun createViewModel(
114124
savedStateHandle: SavedStateHandle = SavedStateHandle().also {
115125
it["email_address"] = EMAIL

0 commit comments

Comments
 (0)