mirror of
https://github.com/bitwarden/android.git
synced 2024-11-26 19:36:18 +03:00
BIT-1670: Add the initial autofill dialog (#922)
This commit is contained in:
parent
77ac4b1956
commit
f380e21600
4 changed files with 105 additions and 2 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in a new issue