BIT-1670: Add the initial autofill dialog (#922)

This commit is contained in:
Oleg Semenenko 2024-01-31 21:50:58 -06:00 committed by Álison Fernandes
parent 77ac4b1956
commit f380e21600
4 changed files with 105 additions and 2 deletions

View file

@ -23,6 +23,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.components.BasicDialogState
import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
@ -136,6 +137,9 @@ fun VaultAddEditScreen(
onDismissRequest = remember(viewModel) {
{ viewModel.trySendAction(VaultAddEditAction.Common.DismissDialog) }
},
onAutofillDismissRequest = remember(viewModel) {
{ viewModel.trySendAction(VaultAddEditAction.Common.InitialAutofillDialogDismissed) }
},
)
if (pendingDeleteCipher) {
@ -267,6 +271,7 @@ fun VaultAddEditScreen(
private fun VaultAddEditItemDialogs(
dialogState: VaultAddEditState.DialogState?,
onDismissRequest: () -> Unit,
onAutofillDismissRequest: () -> Unit,
) {
when (dialogState) {
is VaultAddEditState.DialogState.Loading -> {
@ -285,6 +290,16 @@ private fun VaultAddEditItemDialogs(
)
}
is VaultAddEditState.DialogState.InitialAutofillPrompt -> {
BitwardenBasicDialog(
visibilityState = BasicDialogState.Shown(
title = R.string.bitwarden_autofill_service.asText(),
message = R.string.bitwarden_autofill_service_alert2.asText(),
),
onDismissRequest = onAutofillDismissRequest,
)
}
null -> Unit
}
}

View file

@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
@ -75,6 +76,7 @@ class VaultAddEditViewModel @Inject constructor(
private val clipboardManager: BitwardenClipboardManager,
private val vaultRepository: VaultRepository,
private val generatorRepository: GeneratorRepository,
private val settingsRepository: SettingsRepository,
private val specialCircumstanceManager: SpecialCircumstanceManager,
private val resourceManager: ResourceManager,
) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>(
@ -90,6 +92,17 @@ class VaultAddEditViewModel @Inject constructor(
val autofillSelectionData = specialCircumstanceManager
.specialCircumstance
?.toAutofillSelectionDataOrNull()
val dialogState =
if (!settingsRepository.initialAutofillDialogShown &&
vaultAddEditType is VaultAddEditType.AddItem &&
autofillSelectionData == null
) {
VaultAddEditState.DialogState.InitialAutofillPrompt
} else {
null
}
val defaultAddTypeContent = autofillSelectionData
?.toDefaultAddTypeContent()
?: autofillSaveItem
@ -106,7 +119,7 @@ class VaultAddEditViewModel @Inject constructor(
is VaultAddEditType.EditItem -> VaultAddEditState.ViewState.Loading
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
},
dialog = null,
dialog = dialogState,
// Set special conditions for autofill save
shouldShowCloseButton = autofillSaveItem == null,
shouldExitOnSave = autofillSaveItem != null,
@ -195,6 +208,9 @@ class VaultAddEditViewModel @Inject constructor(
)
is VaultAddEditAction.Common.CollectionSelect -> handleCollectionSelect(action)
is VaultAddEditAction.Common.InitialAutofillDialogDismissed -> {
handleInitialAutofillDialogDismissed()
}
}
}
@ -362,6 +378,13 @@ class VaultAddEditViewModel @Inject constructor(
}
}
private fun handleInitialAutofillDialogDismissed() {
settingsRepository.initialAutofillDialogShown = true
mutableStateFlow.update {
it.copy(dialog = null)
}
}
private fun handleAddNewCustomFieldClick(
action: VaultAddEditAction.Common.AddNewCustomFieldClick,
) {
@ -1716,6 +1739,12 @@ data class VaultAddEditState(
*/
@Parcelize
data class Loading(val label: Text) : DialogState()
/**
* Displays the initial autofill dialog to the user.
*/
@Parcelize
data object InitialAutofillPrompt : DialogState()
}
}
@ -1815,6 +1844,11 @@ sealed class VaultAddEditAction {
*/
data object AttachmentsClick : Common()
/**
* The user has dismissed the initial autofill dialog.
*/
data object InitialAutofillDialogDismissed : Common()
/**
* The user has clicked the move to organization overflow option.
*/

View file

@ -220,6 +220,45 @@ class VaultAddEditScreenTest : BaseComposeTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `clicking dismiss dialog button on InitialAutofillPrompt should send InitialAutofillDialogDismissed action`() {
mutableStateFlow.value = DEFAULT_STATE_LOGIN_DIALOG.copy(
dialog = VaultAddEditState.DialogState.InitialAutofillPrompt,
)
composeTestRule
.onAllNodesWithText("Ok")
.filterToOne(hasAnyAncestor(isDialog()))
.performClick()
verify {
viewModel.trySendAction(
VaultAddEditAction.Common.InitialAutofillDialogDismissed,
)
}
}
@Test
fun `InitialAutofillPrompt is shown according to state`() {
mutableStateFlow.value = DEFAULT_STATE_LOGIN
composeTestRule.assertNoDialogExists()
mutableStateFlow.value = DEFAULT_STATE_LOGIN_DIALOG.copy(
dialog = VaultAddEditState.DialogState.InitialAutofillPrompt,
)
composeTestRule
.onAllNodesWithText("Bitwarden Auto-fill Service")
.filterToOne(hasAnyAncestor(isDialog()))
.assertIsDisplayed()
mutableStateFlow.value = DEFAULT_STATE_LOGIN_DIALOG.copy(
dialog = VaultAddEditState.DialogState.Loading("Loading".asText()),
)
}
@Test
fun `clicking dismiss dialog button should send DismissDialog action`() {
mutableStateFlow.value = DEFAULT_STATE_LOGIN_DIALOG

View file

@ -20,6 +20,7 @@ import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
@ -70,6 +71,10 @@ import java.util.UUID
@Suppress("LargeClass")
class VaultAddEditViewModelTest : BaseViewModelTest() {
private val settingsRepository: SettingsRepository = mockk {
every { initialAutofillDialogShown = any() } just runs
every { initialAutofillDialogShown } returns true
}
private val mutableUserStateFlow = MutableStateFlow<UserState?>(createUserState())
private val authRepository: AuthRepository = mockk {
every { userStateFlow } returns mutableUserStateFlow
@ -1769,6 +1774,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
specialCircumstanceManager = specialCircumstanceManager,
resourceManager = resourceManager,
authRepository = authRepository,
settingsRepository = settingsRepository,
)
}
@ -2195,8 +2201,16 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
)
}
}
}
@Test
fun `InitialAutofillDialogDismissed should update the settings value to true`() {
viewModel.trySendAction(VaultAddEditAction.Common.InitialAutofillDialogDismissed)
verify {
settingsRepository.initialAutofillDialogShown = true
}
}
}
//region Helper functions
@Suppress("MaxLineLength")
@ -2301,6 +2315,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
specialCircumstanceManager = specialCircumstanceManager,
resourceManager = bitwardenResourceManager,
authRepository = authRepository,
settingsRepository = settingsRepository,
)
private fun createVaultData(