diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/field/BitwardenTextField.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/field/BitwardenTextField.kt index 33e405f6fb..6d65ba4fa0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/field/BitwardenTextField.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/field/BitwardenTextField.kt @@ -80,6 +80,7 @@ import kotlinx.collections.immutable.toImmutableList * @param value current next on the text field. * @param modifier modifier for the composable. * @param onValueChange callback that is triggered when the input of the text field changes. + * @param tooltip the optional tooltip to be displayed in the label. * @param placeholder the optional placeholder to be displayed when the text field is in focus and * the [value] is empty. * @param leadingIconResource the optional resource for the leading icon on the text field. @@ -109,6 +110,7 @@ fun BitwardenTextField( onValueChange: (String) -> Unit, cardStyle: CardStyle, modifier: Modifier = Modifier, + tooltip: TooltipData? = null, placeholder: String? = null, leadingIconResource: IconResource? = null, supportingText: String? = null, @@ -133,6 +135,7 @@ fun BitwardenTextField( label = label, value = value, onValueChange = onValueChange, + tooltip = tooltip, placeholder = placeholder, leadingIconResource = leadingIconResource, supportingContent = supportingText?.let { @@ -171,6 +174,7 @@ fun BitwardenTextField( * @param label label for the text field. * @param value current next on the text field. * @param modifier modifier for the composable. + * @param tooltip the optional tooltip to be displayed in the label. * @param onValueChange callback that is triggered when the input of the text field changes. * @param supportingContent An optional supporting content composable that will appear below the * text input. diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt index a5d99f9050..3293b30bdc 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemLoginContent.kt @@ -25,6 +25,7 @@ import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText import com.x8bit.bitwarden.ui.platform.components.indicator.BitwardenCircularCountdownIndicator import com.x8bit.bitwarden.ui.platform.components.model.CardStyle +import com.x8bit.bitwarden.ui.platform.components.model.TooltipData import com.x8bit.bitwarden.ui.platform.components.text.BitwardenClickableText import com.x8bit.bitwarden.ui.platform.components.text.BitwardenHyperTextLink import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme @@ -141,6 +142,8 @@ fun VaultItemLoginContent( totpCodeItemData = totpCodeItemData, enabled = loginItemState.canViewTotpCode, onCopyTotpClick = vaultLoginItemTypeHandlers.onCopyTotpCodeClick, + onAuthenticatorHelpToolTipClick = vaultLoginItemTypeHandlers + .onAuthenticatorHelpToolTipClick, modifier = Modifier .fillMaxWidth() .standardHorizontalMargin(), @@ -399,6 +402,7 @@ private fun TotpField( totpCodeItemData: TotpCodeItemData, enabled: Boolean, onCopyTotpClick: () -> Unit, + onAuthenticatorHelpToolTipClick: () -> Unit, modifier: Modifier = Modifier, ) { if (enabled) { @@ -411,6 +415,10 @@ private fun TotpField( textStyle = BitwardenTheme.typography.sensitiveInfoSmall, readOnly = true, singleLine = true, + tooltip = TooltipData( + onClick = onAuthenticatorHelpToolTipClick, + contentDescription = stringResource(id = R.string.authenticator_key_help), + ), actions = { BitwardenCircularCountdownIndicator( timeLeftSeconds = totpCodeItemData.timeLeftSeconds, @@ -431,6 +439,10 @@ private fun TotpField( BitwardenTextField( label = stringResource(id = R.string.authenticator_key), value = "", + tooltip = TooltipData( + onClick = onAuthenticatorHelpToolTipClick, + contentDescription = stringResource(id = R.string.authenticator_key_help), + ), supportingText = stringResource(id = R.string.premium_subscription_required), enabled = false, singleLine = false, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt index 7c9a2a02b1..b065d66605 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModel.kt @@ -530,6 +530,10 @@ class VaultItemViewModel @Inject constructor( private fun handleLoginTypeActions(action: VaultItemAction.ItemType.Login) { when (action) { + is VaultItemAction.ItemType.Login.AuthenticatorHelpToolTipClick -> { + handleAuthenticatorHelpToolTipClick() + } + is VaultItemAction.ItemType.Login.CheckForBreachClick -> { handleCheckForBreachClick() } @@ -564,6 +568,14 @@ class VaultItemViewModel @Inject constructor( } } + private fun handleAuthenticatorHelpToolTipClick() { + sendEvent( + event = VaultItemEvent.NavigateToUri( + uri = "https://bitwarden.com/help/integrated-authenticator", + ), + ) + } + private fun handleCheckForBreachClick() { onLoginContent { _, login -> val password = requireNotNull(login.passwordData?.password) @@ -1991,6 +2003,11 @@ sealed class VaultItemAction { * Represents actions specific to the Login type. */ sealed class Login : ItemType() { + /** + * The user has clicked the call to action on the authenticator help tooltip. + */ + data object AuthenticatorHelpToolTipClick : Login() + /** * The user has clicked the check for breach button. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultLoginItemTypeHandlers.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultLoginItemTypeHandlers.kt index c8b69e51a2..10b5e28aee 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultLoginItemTypeHandlers.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/item/handlers/VaultLoginItemTypeHandlers.kt @@ -12,6 +12,7 @@ data class VaultLoginItemTypeHandlers( val onCheckForBreachClick: () -> Unit, val onCopyPasswordClick: () -> Unit, val onCopyTotpCodeClick: () -> Unit, + val onAuthenticatorHelpToolTipClick: () -> Unit, val onCopyUriClick: (String) -> Unit, val onCopyUsernameClick: () -> Unit, val onLaunchUriClick: (String) -> Unit, @@ -37,6 +38,11 @@ data class VaultLoginItemTypeHandlers( onCopyTotpCodeClick = { viewModel.trySendAction(VaultItemAction.ItemType.Login.CopyTotpClick) }, + onAuthenticatorHelpToolTipClick = { + viewModel.trySendAction( + VaultItemAction.ItemType.Login.AuthenticatorHelpToolTipClick, + ) + }, onCopyUriClick = { viewModel.trySendAction(VaultItemAction.ItemType.Login.CopyUriClick(it)) }, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt index afa2c93d4c..f174581161 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt @@ -1727,6 +1727,21 @@ class VaultItemScreenTest : BaseComposeTest() { } } + @Test + fun `in login state, on totp help tooltip click should send AuthenticatorHelpToolTipClick`() { + mutableStateFlow.update { currentState -> + currentState.copy(viewState = DEFAULT_LOGIN_VIEW_STATE) + } + + composeTestRule + .onNodeWithContentDescriptionAfterScroll("Authenticator key help") + .performClick() + + verify { + viewModel.trySendAction(VaultItemAction.ItemType.Login.AuthenticatorHelpToolTipClick) + } + } + @Test fun `in login state, launch uri button should be displayed according to state`() { val uriData = VaultItemState.ViewState.Content.ItemType.Login.UriData( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt index d69e1780f1..9a74efd6db 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemViewModelTest.kt @@ -2001,6 +2001,21 @@ class VaultItemViewModelTest : BaseViewModelTest() { } } + @Test + fun `on AuthenticatorHelpToolTipClick should emit NavigateToUri`() = runTest { + viewModel.eventFlow.test { + viewModel.trySendAction( + action = VaultItemAction.ItemType.Login.AuthenticatorHelpToolTipClick, + ) + assertEquals( + VaultItemEvent.NavigateToUri( + "https://bitwarden.com/help/integrated-authenticator", + ), + awaitItem(), + ) + } + } + @Test fun `on PasswordHistoryClick should show password dialog when re-prompt is required`() = runTest {