mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
PM-14009 complete fix importlogins card show logic (#4175)
This commit is contained in:
parent
14a11680b1
commit
296ee17211
14 changed files with 205 additions and 36 deletions
|
@ -298,4 +298,20 @@ interface SettingsDiskSource {
|
||||||
* Emits updates that track [getShowUnlockSettingBadge] for the given [userId].
|
* Emits updates that track [getShowUnlockSettingBadge] for the given [userId].
|
||||||
*/
|
*/
|
||||||
fun getShowUnlockSettingBadgeFlow(userId: String): Flow<Boolean?>
|
fun getShowUnlockSettingBadgeFlow(userId: String): Flow<Boolean?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not the given [userId] has signalled they want to import logins later.
|
||||||
|
*/
|
||||||
|
fun getShowImportLoginsSettingBadge(userId: String): Boolean?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the given value for whether or not the given [userId] has signalled they want to
|
||||||
|
* set import logins later, during first time usage.
|
||||||
|
*/
|
||||||
|
fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits updates that track [getShowImportLoginsSettingBadge] for the given [userId].
|
||||||
|
*/
|
||||||
|
fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?>
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ private const val INITIAL_AUTOFILL_DIALOG_SHOWN = "addSitePromptShown"
|
||||||
private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount"
|
private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount"
|
||||||
private const val SHOW_AUTOFILL_SETTING_BADGE = "showAutofillSettingBadge"
|
private const val SHOW_AUTOFILL_SETTING_BADGE = "showAutofillSettingBadge"
|
||||||
private const val SHOW_UNLOCK_SETTING_BADGE = "showUnlockSettingBadge"
|
private const val SHOW_UNLOCK_SETTING_BADGE = "showUnlockSettingBadge"
|
||||||
|
private const val SHOW_IMPORT_LOGINS_SETTING_BADGE = "showImportLoginsSettingBadge"
|
||||||
private const val LAST_SCHEME_CHANGE_INSTANT = "lastDatabaseSchemeChangeInstant"
|
private const val LAST_SCHEME_CHANGE_INSTANT = "lastDatabaseSchemeChangeInstant"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +66,9 @@ class SettingsDiskSourceImpl(
|
||||||
private val mutableShowUnlockSettingBadgeFlowMap =
|
private val mutableShowUnlockSettingBadgeFlowMap =
|
||||||
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
||||||
|
|
||||||
|
private val mutableShowImportLoginsSettingBadgeFlowMap =
|
||||||
|
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
||||||
|
|
||||||
private val mutableIsIconLoadingDisabledFlow = bufferedMutableSharedFlow<Boolean?>()
|
private val mutableIsIconLoadingDisabledFlow = bufferedMutableSharedFlow<Boolean?>()
|
||||||
|
|
||||||
private val mutableIsCrashLoggingEnabledFlow = bufferedMutableSharedFlow<Boolean?>()
|
private val mutableIsCrashLoggingEnabledFlow = bufferedMutableSharedFlow<Boolean?>()
|
||||||
|
@ -412,6 +416,24 @@ class SettingsDiskSourceImpl(
|
||||||
getMutableShowUnlockSettingBadgeFlow(userId = userId)
|
getMutableShowUnlockSettingBadgeFlow(userId = userId)
|
||||||
.onSubscription { emit(getShowUnlockSettingBadge(userId)) }
|
.onSubscription { emit(getShowUnlockSettingBadge(userId)) }
|
||||||
|
|
||||||
|
override fun getShowImportLoginsSettingBadge(userId: String): Boolean? {
|
||||||
|
return getBoolean(
|
||||||
|
key = SHOW_IMPORT_LOGINS_SETTING_BADGE.appendIdentifier(userId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?) {
|
||||||
|
putBoolean(
|
||||||
|
key = SHOW_IMPORT_LOGINS_SETTING_BADGE.appendIdentifier(userId),
|
||||||
|
showBadge,
|
||||||
|
)
|
||||||
|
getMutableShowImportLoginsSettingBadgeFlow(userId).tryEmit(showBadge)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?> =
|
||||||
|
getMutableShowImportLoginsSettingBadgeFlow(userId)
|
||||||
|
.onSubscription { emit(getShowImportLoginsSettingBadge(userId)) }
|
||||||
|
|
||||||
private fun getMutableLastSyncFlow(
|
private fun getMutableLastSyncFlow(
|
||||||
userId: String,
|
userId: String,
|
||||||
): MutableSharedFlow<Instant?> =
|
): MutableSharedFlow<Instant?> =
|
||||||
|
@ -455,4 +477,11 @@ class SettingsDiskSourceImpl(
|
||||||
mutableShowUnlockSettingBadgeFlowMap.getOrPut(userId) {
|
mutableShowUnlockSettingBadgeFlowMap.getOrPut(userId) {
|
||||||
bufferedMutableSharedFlow(replay = 1)
|
bufferedMutableSharedFlow(replay = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getMutableShowImportLoginsSettingBadgeFlow(
|
||||||
|
userId: String,
|
||||||
|
): MutableSharedFlow<Boolean?> =
|
||||||
|
mutableShowImportLoginsSettingBadgeFlowMap.getOrPut(userId) {
|
||||||
|
bufferedMutableSharedFlow(replay = 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,4 +61,9 @@ interface FirstTimeActionManager {
|
||||||
* Update the value of the showImportLogins status for the active user.
|
* Update the value of the showImportLogins status for the active user.
|
||||||
*/
|
*/
|
||||||
fun storeShowImportLogins(showImportLogins: Boolean)
|
fun storeShowImportLogins(showImportLogins: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the value of the showImportLoginsSettingsBadge status for the active user.
|
||||||
|
*/
|
||||||
|
fun storeShowImportLoginsSettingsBadge(showBadge: Boolean)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,11 +99,11 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.flatMapLatest {
|
.flatMapLatest {
|
||||||
combine(
|
combine(
|
||||||
getShowImportLoginsFlowInternal(userId = it),
|
getShowImportLoginsSettingBadgeFlowInternal(userId = it),
|
||||||
featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow),
|
featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow),
|
||||||
) { showImportLogins, importLoginsEnabled ->
|
) { showImportLogins, importLoginsEnabled ->
|
||||||
val shouldShowImportLogins = showImportLogins && importLoginsEnabled
|
val shouldShowImportLoginsSettings = showImportLogins && importLoginsEnabled
|
||||||
listOf(shouldShowImportLogins)
|
listOf(shouldShowImportLoginsSettings)
|
||||||
}
|
}
|
||||||
.map { list ->
|
.map { list ->
|
||||||
list.count { showImportLogins -> showImportLogins }
|
list.count { showImportLogins -> showImportLogins }
|
||||||
|
@ -129,12 +129,14 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||||
getShowImportLoginsFlowInternal(userId = activeUserId),
|
getShowImportLoginsFlowInternal(userId = activeUserId),
|
||||||
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = activeUserId),
|
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = activeUserId),
|
||||||
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = activeUserId),
|
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = activeUserId),
|
||||||
|
getShowImportLoginsSettingBadgeFlowInternal(userId = activeUserId),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
FirstTimeState(
|
FirstTimeState(
|
||||||
showImportLoginsCard = it[0],
|
showImportLoginsCard = it[0],
|
||||||
showSetupUnlockCard = it[1],
|
showSetupUnlockCard = it[1],
|
||||||
showSetupAutofillCard = it[2],
|
showSetupAutofillCard = it[2],
|
||||||
|
showImportLoginsCardInSettings = it[3],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,24 +146,12 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||||
showImportLoginsCard = null,
|
showImportLoginsCard = null,
|
||||||
showSetupUnlockCard = null,
|
showSetupUnlockCard = null,
|
||||||
showSetupAutofillCard = null,
|
showSetupAutofillCard = null,
|
||||||
|
showImportLoginsCardInSettings = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal implementation to get a flow of the showImportLogins value which takes
|
|
||||||
* into account if the vault is empty.
|
|
||||||
*/
|
|
||||||
private fun getShowImportLoginsFlowInternal(userId: String): Flow<Boolean> {
|
|
||||||
return authDiskSource.getShowImportLoginsFlow(userId)
|
|
||||||
.combine(
|
|
||||||
vaultDiskSource.getCiphers(userId),
|
|
||||||
) { showImportLogins, ciphers ->
|
|
||||||
showImportLogins ?: true && ciphers.isEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current [FirstTimeState] of the active user if available, otherwise return
|
* Get the current [FirstTimeState] of the active user if available, otherwise return
|
||||||
* a default configuration.
|
* a default configuration.
|
||||||
|
@ -176,12 +166,15 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||||
showImportLoginsCard = authDiskSource.getShowImportLogins(it),
|
showImportLoginsCard = authDiskSource.getShowImportLogins(it),
|
||||||
showSetupUnlockCard = settingsDiskSource.getShowUnlockSettingBadge(it),
|
showSetupUnlockCard = settingsDiskSource.getShowUnlockSettingBadge(it),
|
||||||
showSetupAutofillCard = settingsDiskSource.getShowAutoFillSettingBadge(it),
|
showSetupAutofillCard = settingsDiskSource.getShowAutoFillSettingBadge(it),
|
||||||
|
showImportLoginsCardInSettings = settingsDiskSource
|
||||||
|
.getShowImportLoginsSettingBadge(it),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
?: FirstTimeState(
|
?: FirstTimeState(
|
||||||
showImportLoginsCard = null,
|
showImportLoginsCard = null,
|
||||||
showSetupUnlockCard = null,
|
showSetupUnlockCard = null,
|
||||||
showSetupAutofillCard = null,
|
showSetupAutofillCard = null,
|
||||||
|
showImportLoginsCardInSettings = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun storeShowUnlockSettingBadge(showBadge: Boolean) {
|
override fun storeShowUnlockSettingBadge(showBadge: Boolean) {
|
||||||
|
@ -207,4 +200,40 @@ class FirstTimeActionManagerImpl @Inject constructor(
|
||||||
showImportLogins = showImportLogins,
|
showImportLogins = showImportLogins,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun storeShowImportLoginsSettingsBadge(showBadge: Boolean) {
|
||||||
|
val activeUserId = authDiskSource.userState?.activeUserId ?: return
|
||||||
|
settingsDiskSource.storeShowImportLoginsSettingBadge(
|
||||||
|
userId = activeUserId,
|
||||||
|
showBadge = showBadge,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation to get a flow of the showImportLogins value which takes
|
||||||
|
* into account if the vault is empty.
|
||||||
|
*/
|
||||||
|
private fun getShowImportLoginsFlowInternal(userId: String): Flow<Boolean> {
|
||||||
|
return authDiskSource
|
||||||
|
.getShowImportLoginsFlow(userId)
|
||||||
|
.combine(
|
||||||
|
vaultDiskSource.getCiphers(userId),
|
||||||
|
) { showImportLogins, ciphers ->
|
||||||
|
showImportLogins ?: true && ciphers.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation to get a flow of the showImportLogins value which takes
|
||||||
|
* into account if the vault is empty.
|
||||||
|
*/
|
||||||
|
private fun getShowImportLoginsSettingBadgeFlowInternal(userId: String): Flow<Boolean> {
|
||||||
|
return settingsDiskSource
|
||||||
|
.getShowImportLoginsSettingBadgeFlow(userId)
|
||||||
|
.combine(
|
||||||
|
vaultDiskSource.getCiphers(userId),
|
||||||
|
) { showImportLogins, ciphers ->
|
||||||
|
showImportLogins ?: false && ciphers.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ package com.x8bit.bitwarden.data.platform.manager.model
|
||||||
*/
|
*/
|
||||||
data class FirstTimeState(
|
data class FirstTimeState(
|
||||||
val showImportLoginsCard: Boolean,
|
val showImportLoginsCard: Boolean,
|
||||||
|
val showImportLoginsCardInSettings: Boolean,
|
||||||
val showSetupUnlockCard: Boolean,
|
val showSetupUnlockCard: Boolean,
|
||||||
val showSetupAutofillCard: Boolean,
|
val showSetupAutofillCard: Boolean,
|
||||||
) {
|
) {
|
||||||
|
@ -16,9 +17,11 @@ data class FirstTimeState(
|
||||||
showImportLoginsCard: Boolean? = null,
|
showImportLoginsCard: Boolean? = null,
|
||||||
showSetupUnlockCard: Boolean? = null,
|
showSetupUnlockCard: Boolean? = null,
|
||||||
showSetupAutofillCard: Boolean? = null,
|
showSetupAutofillCard: Boolean? = null,
|
||||||
|
showImportLoginsCardInSettings: Boolean? = null,
|
||||||
) : this(
|
) : this(
|
||||||
showImportLoginsCard = showImportLoginsCard ?: true,
|
showImportLoginsCard = showImportLoginsCard ?: true,
|
||||||
showSetupUnlockCard = showSetupUnlockCard ?: false,
|
showSetupUnlockCard = showSetupUnlockCard ?: false,
|
||||||
showSetupAutofillCard = showSetupAutofillCard ?: false,
|
showSetupAutofillCard = showSetupAutofillCard ?: false,
|
||||||
|
showImportLoginsCardInSettings = showImportLoginsCardInSettings ?: false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class VaultSettingsViewModel @Inject constructor(
|
||||||
.toBaseWebVaultImportUrl,
|
.toBaseWebVaultImportUrl,
|
||||||
isNewImportLoginsFlowEnabled = featureFlagManager
|
isNewImportLoginsFlowEnabled = featureFlagManager
|
||||||
.getFeatureFlag(FlagKey.ImportLoginsFlow),
|
.getFeatureFlag(FlagKey.ImportLoginsFlow),
|
||||||
showImportActionCard = firstTimeState.showImportLoginsCard,
|
showImportActionCard = firstTimeState.showImportLoginsCardInSettings,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
@ -48,7 +48,7 @@ class VaultSettingsViewModel @Inject constructor(
|
||||||
.firstTimeStateFlow
|
.firstTimeStateFlow
|
||||||
.map {
|
.map {
|
||||||
VaultSettingsAction.Internal.UserFirstTimeStateChanged(
|
VaultSettingsAction.Internal.UserFirstTimeStateChanged(
|
||||||
showImportLoginsCard = it.showImportLoginsCard,
|
showImportLoginsCard = it.showImportLoginsCardInSettings,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.onEach(::sendAction)
|
.onEach(::sendAction)
|
||||||
|
@ -79,7 +79,7 @@ class VaultSettingsViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun handleImportLoginsCardDismissClicked() {
|
private fun handleImportLoginsCardDismissClicked() {
|
||||||
if (!state.shouldShowImportCard) return
|
if (!state.shouldShowImportCard) return
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleImportLoginsCardClicked() {
|
private fun handleImportLoginsCardClicked() {
|
||||||
|
|
|
@ -97,6 +97,7 @@ class ImportLoginsViewModel @Inject constructor(
|
||||||
is SyncVaultDataResult.Success -> {
|
is SyncVaultDataResult.Success -> {
|
||||||
if (result.itemsAvailable) {
|
if (result.itemsAvailable) {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
showBottomSheet = true,
|
showBottomSheet = true,
|
||||||
|
@ -160,6 +161,8 @@ class ImportLoginsViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun handleConfirmImportLater() {
|
private fun handleConfirmImportLater() {
|
||||||
dismissDialog()
|
dismissDialog()
|
||||||
|
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = true)
|
||||||
sendEvent(ImportLoginsEvent.NavigateBack)
|
sendEvent(ImportLoginsEvent.NavigateBack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,6 +186,7 @@ class VaultViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDismissImportActionCard() {
|
private fun handleDismissImportActionCard() {
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
|
||||||
if (!state.showImportActionCard) return
|
if (!state.showImportActionCard) return
|
||||||
firstTimeActionManager.storeShowImportLogins(false)
|
firstTimeActionManager.storeShowImportLogins(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1062,10 +1062,10 @@ class SettingsDiskSourceTest {
|
||||||
@Test
|
@Test
|
||||||
fun `storeShowAutoFillSettingBadge should update the flow value`() = runTest {
|
fun `storeShowAutoFillSettingBadge should update the flow value`() = runTest {
|
||||||
val mockUserId = "mockUserId"
|
val mockUserId = "mockUserId"
|
||||||
settingsDiskSource.storeShowAutoFillSettingBadge(mockUserId, true)
|
|
||||||
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = mockUserId).test {
|
settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = mockUserId).test {
|
||||||
// The initial values of the Flow are in sync
|
// The initial values of the Flow are in sync
|
||||||
assertTrue(awaitItem() ?: false)
|
assertFalse(awaitItem() ?: false)
|
||||||
|
settingsDiskSource.storeShowAutoFillSettingBadge(mockUserId, true)
|
||||||
assertTrue(awaitItem() ?: false)
|
assertTrue(awaitItem() ?: false)
|
||||||
|
|
||||||
// update the value to false
|
// update the value to false
|
||||||
|
@ -1103,10 +1103,10 @@ class SettingsDiskSourceTest {
|
||||||
@Test
|
@Test
|
||||||
fun `storeShowUnlockSettingsBadge should update the flow value`() = runTest {
|
fun `storeShowUnlockSettingsBadge should update the flow value`() = runTest {
|
||||||
val mockUserId = "mockUserId"
|
val mockUserId = "mockUserId"
|
||||||
settingsDiskSource.storeShowUnlockSettingBadge(mockUserId, true)
|
|
||||||
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = mockUserId).test {
|
settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = mockUserId).test {
|
||||||
// The initial values of the Flow are in sync
|
// The initial values of the Flow are in sync
|
||||||
assertTrue(awaitItem() ?: false)
|
assertFalse(awaitItem() ?: false)
|
||||||
|
settingsDiskSource.storeShowUnlockSettingBadge(mockUserId, true)
|
||||||
assertTrue(awaitItem() ?: false)
|
assertTrue(awaitItem() ?: false)
|
||||||
|
|
||||||
// update the value to false
|
// update the value to false
|
||||||
|
@ -1165,4 +1165,47 @@ class SettingsDiskSourceTest {
|
||||||
actual,
|
actual,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getShowImportLoginsSettingBadge should pull from shared preferences`() {
|
||||||
|
val mockUserId = "mockUserId"
|
||||||
|
val showImportLoginsSettingBadgeKey =
|
||||||
|
"bwPreferencesStorage:showImportLoginsSettingBadge_$mockUserId"
|
||||||
|
fakeSharedPreferences.edit {
|
||||||
|
putBoolean(showImportLoginsSettingBadgeKey, true)
|
||||||
|
}
|
||||||
|
assertTrue(
|
||||||
|
settingsDiskSource.getShowImportLoginsSettingBadge(userId = mockUserId)!!,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `storeShowImportLoginsSettingBadge should update SharedPreferences`() {
|
||||||
|
val mockUserId = "mockUserId"
|
||||||
|
val showImportLoginsSettingBadgeKey =
|
||||||
|
"bwPreferencesStorage:showImportLoginsSettingBadge_$mockUserId"
|
||||||
|
settingsDiskSource.storeShowImportLoginsSettingBadge(mockUserId, true)
|
||||||
|
assertTrue(
|
||||||
|
fakeSharedPreferences.getBoolean(showImportLoginsSettingBadgeKey, false),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `storeShowImportLoginsSettingBadge should update the flow value`() = runTest {
|
||||||
|
val mockUserId = "mockUserId"
|
||||||
|
settingsDiskSource.getShowImportLoginsSettingBadgeFlow(mockUserId).test {
|
||||||
|
// The initial values of the Flow are in sync
|
||||||
|
assertFalse(awaitItem() ?: false)
|
||||||
|
settingsDiskSource.storeShowImportLoginsSettingBadge(
|
||||||
|
userId = mockUserId,
|
||||||
|
showBadge = true,
|
||||||
|
)
|
||||||
|
assertTrue(awaitItem() ?: false)
|
||||||
|
// update the value to false
|
||||||
|
settingsDiskSource.storeShowImportLoginsSettingBadge(
|
||||||
|
userId = mockUserId, false,
|
||||||
|
)
|
||||||
|
assertFalse(awaitItem() ?: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,7 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
||||||
private val userSignIns = mutableMapOf<String, Boolean>()
|
private val userSignIns = mutableMapOf<String, Boolean>()
|
||||||
private val userShowAutoFillBadge = mutableMapOf<String, Boolean?>()
|
private val userShowAutoFillBadge = mutableMapOf<String, Boolean?>()
|
||||||
private val userShowUnlockBadge = mutableMapOf<String, Boolean?>()
|
private val userShowUnlockBadge = mutableMapOf<String, Boolean?>()
|
||||||
|
private val userShowImportLoginsBadge = mutableMapOf<String, Boolean?>()
|
||||||
private var storedLastDatabaseSchemeChangeInstant: Instant? = null
|
private var storedLastDatabaseSchemeChangeInstant: Instant? = null
|
||||||
|
|
||||||
private val mutableShowAutoFillSettingBadgeFlowMap =
|
private val mutableShowAutoFillSettingBadgeFlowMap =
|
||||||
|
@ -71,6 +72,9 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
||||||
private val mutableShowUnlockSettingBadgeFlowMap =
|
private val mutableShowUnlockSettingBadgeFlowMap =
|
||||||
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
||||||
|
|
||||||
|
private val mutableShowImportLoginsSettingBadgeFlowMap =
|
||||||
|
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
|
||||||
|
|
||||||
override var appLanguage: AppLanguage? = null
|
override var appLanguage: AppLanguage? = null
|
||||||
|
|
||||||
override var appTheme: AppTheme
|
override var appTheme: AppTheme
|
||||||
|
@ -323,6 +327,19 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
||||||
emit(getShowUnlockSettingBadge(userId = userId))
|
emit(getShowUnlockSettingBadge(userId = userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getShowImportLoginsSettingBadge(userId: String): Boolean? =
|
||||||
|
userShowImportLoginsBadge[userId]
|
||||||
|
|
||||||
|
override fun storeShowImportLoginsSettingBadge(userId: String, showBadge: Boolean?) {
|
||||||
|
userShowImportLoginsBadge[userId] = showBadge
|
||||||
|
getMutableShowImportLoginsSettingBadgeFlow(userId).tryEmit(showBadge)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getShowImportLoginsSettingBadgeFlow(userId: String): Flow<Boolean?> =
|
||||||
|
getMutableShowImportLoginsSettingBadgeFlow(userId = userId).onSubscription {
|
||||||
|
emit(getShowImportLoginsSettingBadge(userId = userId))
|
||||||
|
}
|
||||||
|
|
||||||
//region Private helper functions
|
//region Private helper functions
|
||||||
private fun getMutableScreenCaptureAllowedFlow(userId: String): MutableSharedFlow<Boolean?> {
|
private fun getMutableScreenCaptureAllowedFlow(userId: String): MutableSharedFlow<Boolean?> {
|
||||||
return mutableScreenCaptureAllowedFlowMap.getOrPut(userId) {
|
return mutableScreenCaptureAllowedFlowMap.getOrPut(userId) {
|
||||||
|
@ -369,5 +386,11 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
||||||
bufferedMutableSharedFlow(replay = 1)
|
bufferedMutableSharedFlow(replay = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getMutableShowImportLoginsSettingBadgeFlow(
|
||||||
|
userId: String,
|
||||||
|
): MutableSharedFlow<Boolean?> = mutableShowImportLoginsSettingBadgeFlowMap.getOrPut(userId) {
|
||||||
|
bufferedMutableSharedFlow(replay = 1)
|
||||||
|
}
|
||||||
|
|
||||||
//endregion Private helper functions
|
//endregion Private helper functions
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ class FirstTimeActionManagerTest {
|
||||||
// for the import logins count it is dependent on the feature flag state and
|
// for the import logins count it is dependent on the feature flag state and
|
||||||
// cipher list being empty
|
// cipher list being empty
|
||||||
mutableImportLoginsFlow.update { true }
|
mutableImportLoginsFlow.update { true }
|
||||||
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true)
|
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||||
assertEquals(2, awaitItem())
|
assertEquals(2, awaitItem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,15 +127,15 @@ class FirstTimeActionManagerTest {
|
||||||
firstTimeActionManager.allVaultSettingsBadgeCountFlow.test {
|
firstTimeActionManager.allVaultSettingsBadgeCountFlow.test {
|
||||||
assertEquals(0, awaitItem())
|
assertEquals(0, awaitItem())
|
||||||
mutableImportLoginsFlow.update { true }
|
mutableImportLoginsFlow.update { true }
|
||||||
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true)
|
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||||
assertEquals(1, awaitItem())
|
assertEquals(1, awaitItem())
|
||||||
mutableImportLoginsFlow.update { false }
|
mutableImportLoginsFlow.update { false }
|
||||||
assertEquals(0, awaitItem())
|
assertEquals(0, awaitItem())
|
||||||
mutableImportLoginsFlow.update { true }
|
mutableImportLoginsFlow.update { true }
|
||||||
assertEquals(1, awaitItem())
|
assertEquals(1, awaitItem())
|
||||||
fakeAuthDiskSource.storeShowImportLogins(USER_ID, false)
|
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, false)
|
||||||
assertEquals(0, awaitItem())
|
assertEquals(0, awaitItem())
|
||||||
fakeAuthDiskSource.storeShowImportLogins(USER_ID, true)
|
fakeSettingsDiskSource.storeShowImportLoginsSettingBadge(USER_ID, true)
|
||||||
assertEquals(1, awaitItem())
|
assertEquals(1, awaitItem())
|
||||||
mutableCiphersListFlow.update {
|
mutableCiphersListFlow.update {
|
||||||
listOf(mockCipher)
|
listOf(mockCipher)
|
||||||
|
@ -156,7 +156,7 @@ class FirstTimeActionManagerTest {
|
||||||
),
|
),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
fakeAuthDiskSource.storeShowImportLogins(USER_ID, false)
|
firstTimeActionManager.storeShowImportLogins(false)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
FirstTimeState(
|
FirstTimeState(
|
||||||
showImportLoginsCard = false,
|
showImportLoginsCard = false,
|
||||||
|
@ -233,6 +233,13 @@ class FirstTimeActionManagerTest {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = true)
|
firstTimeActionManager.storeShowImportLogins(showImportLogins = true)
|
||||||
assertTrue(fakeAuthDiskSource.getShowImportLogins(userId = USER_ID)!!)
|
assertTrue(fakeAuthDiskSource.getShowImportLogins(userId = USER_ID)!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `storeShowImportLoginsSettingsBadge should store value of true to disk`() {
|
||||||
|
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
|
assertFalse(fakeSettingsDiskSource.getShowImportLoginsSettingBadge(userId = USER_ID)!!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val USER_ID: String = "userId"
|
private const val USER_ID: String = "userId"
|
||||||
|
|
|
@ -31,7 +31,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
private val firstTimeActionManager = mockk<FirstTimeActionManager> {
|
private val firstTimeActionManager = mockk<FirstTimeActionManager> {
|
||||||
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
|
every { currentOrDefaultUserFirstTimeState } returns DEFAULT_FIRST_TIME_STATE
|
||||||
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
|
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
|
||||||
every { storeShowImportLogins(any()) } just runs
|
every { storeShowImportLoginsSettingsBadge(any()) } just runs
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -84,7 +84,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
assertTrue(viewModel.stateFlow.value.shouldShowImportCard)
|
assertTrue(viewModel.stateFlow.value.shouldShowImportCard)
|
||||||
mutableFirstTimeStateFlow.update {
|
mutableFirstTimeStateFlow.update {
|
||||||
it.copy(showImportLoginsCard = false)
|
it.copy(showImportLoginsCardInSettings = false)
|
||||||
}
|
}
|
||||||
assertFalse(viewModel.stateFlow.value.shouldShowImportCard)
|
assertFalse(viewModel.stateFlow.value.shouldShowImportCard)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
viewModel.trySendAction(VaultSettingsAction.ImportLoginsCardDismissClick)
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,4 +143,4 @@ class VaultSettingsViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(showImportLoginsCard = true)
|
private val DEFAULT_FIRST_TIME_STATE = FirstTimeState(showImportLoginsCardInSettings = true)
|
||||||
|
|
|
@ -28,6 +28,7 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
|
||||||
|
|
||||||
private val firstTimeActionManager: FirstTimeActionManager = mockk {
|
private val firstTimeActionManager: FirstTimeActionManager = mockk {
|
||||||
every { storeShowImportLogins(any()) } just runs
|
every { storeShowImportLogins(any()) } just runs
|
||||||
|
every { storeShowImportLoginsSettingsBadge(any()) } just runs
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -100,8 +101,9 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `ConfirmImportLater sets dialog state to null and sends NavigateBack event`() = runTest {
|
fun `ConfirmImportLater sets dialog state to null, sends NavigateBack event, and stores first time values`() = runTest {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
viewModel.stateEventFlow(backgroundScope) { stateFlow, eventFlow ->
|
viewModel.stateEventFlow(backgroundScope) { stateFlow, eventFlow ->
|
||||||
// Initial state
|
// Initial state
|
||||||
|
@ -133,6 +135,10 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
|
||||||
eventFlow.awaitItem(),
|
eventFlow.awaitItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
verify(exactly = 1) {
|
||||||
|
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -320,6 +326,7 @@ class ImportLoginsViewModelTest : BaseViewModelTest() {
|
||||||
)
|
)
|
||||||
verify {
|
verify {
|
||||||
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
firstTimeActionManager.storeShowImportLogins(showImportLogins = false)
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(showBadge = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||||
private val firstTimeActionManager: FirstTimeActionManager = mockk {
|
private val firstTimeActionManager: FirstTimeActionManager = mockk {
|
||||||
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
|
every { firstTimeStateFlow } returns mutableFirstTimeStateFlow
|
||||||
every { storeShowImportLogins(any()) } just runs
|
every { storeShowImportLogins(any()) } just runs
|
||||||
|
every { storeShowImportLoginsSettingsBadge(any()) } just runs
|
||||||
}
|
}
|
||||||
|
|
||||||
private val authRepository: AuthRepository =
|
private val authRepository: AuthRepository =
|
||||||
|
@ -1555,12 +1556,14 @@ class VaultViewModelTest : BaseViewModelTest() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `when DismissImportActionCard is sent, repository called to set value to false`() {
|
fun `when DismissImportActionCard is sent, repository called to showImportLogins to false and storeShowImportLoginsBadge to true`() {
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
viewModel.trySendAction(VaultAction.DismissImportActionCard)
|
viewModel.trySendAction(VaultAction.DismissImportActionCard)
|
||||||
verify(exactly = 1) {
|
verify(exactly = 1) {
|
||||||
firstTimeActionManager.storeShowImportLogins(false)
|
firstTimeActionManager.storeShowImportLogins(false)
|
||||||
|
firstTimeActionManager.storeShowImportLoginsSettingsBadge(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue