From 56ad1ef05b50542d152dff0b64573d5988275280 Mon Sep 17 00:00:00 2001 From: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:30:13 -0400 Subject: [PATCH] PM-13464 and PM-13627 support (#4107) --- .../auth/repository/AuthRepositoryImpl.kt | 11 +- .../repository/di/AuthRepositoryModule.kt | 3 + .../data/auth/repository/model/UserState.kt | 18 +- .../util/AuthDiskSourceExtensions.kt | 40 --- .../util/UserStateJsonExtensions.kt | 3 +- .../manager/FirstTimeActionManager.kt | 47 ++++ .../manager/FirstTimeActionManagerImpl.kt | 186 ++++++++++++++ .../manager/di/PlatformManagerModule.kt | 19 ++ .../platform/manager/model/FirstTimeState.kt | 24 ++ .../platform/repository/SettingsRepository.kt | 18 -- .../repository/SettingsRepositoryImpl.kt | 61 ----- .../feature/settings/SettingsViewModel.kt | 12 +- .../VaultUnlockedNavBarViewModel.kt | 8 +- .../com/x8bit/bitwarden/MainViewModelTest.kt | 3 +- .../auth/repository/AuthRepositoryTest.kt | 11 +- .../util/AuthDiskSourceExtensionsTest.kt | 43 ---- .../util/UserStateJsonExtensionsTest.kt | 41 +-- .../processor/Fido2ProviderProcessorTest.kt | 3 +- .../manager/FirstTimeActionManagerTest.kt | 234 ++++++++++++++++++ .../repository/SettingsRepositoryTest.kt | 65 ----- .../accountsetup/SetupUnlockViewModelTest.kt | 3 +- .../feature/landing/LandingViewModelTest.kt | 13 +- .../auth/feature/login/LoginViewModelTest.kt | 3 +- .../RemovePasswordViewModelTest.kt | 3 +- .../TrustedDeviceViewModelTest.kt | 3 +- .../vaultunlock/VaultUnlockViewModelTest.kt | 7 +- .../feature/rootnav/RootNavViewModelTest.kt | 59 ++--- .../feature/search/SearchViewModelTest.kt | 3 +- .../feature/settings/SettingsViewModelTest.kt | 6 +- .../AccountSecurityViewModelTest.kt | 3 +- .../DeleteAccountViewModelTest.kt | 3 +- .../LoginApprovalViewModelTest.kt | 3 +- .../exportvault/ExportVaultViewModelTest.kt | 3 +- .../VaultUnlockedNavBarViewModelTest.kt | 6 +- .../generator/GeneratorViewModelTest.kt | 3 +- .../send/addsend/AddSendViewModelTest.kt | 3 +- .../addedit/VaultAddEditViewModelTest.kt | 3 +- .../addedit/util/CipherViewExtensionsTest.kt | 3 +- .../attachments/AttachmentsViewModelTest.kt | 3 +- .../feature/item/VaultItemViewModelTest.kt | 3 +- .../VaultItemListingViewModelTest.kt | 3 +- .../VaultMoveToOrganizationViewModelTest.kt | 3 +- .../VaultMoveToOrganizationExtensionsTest.kt | 3 +- .../vault/feature/vault/VaultViewModelTest.kt | 3 +- .../vault/util/UserStateExtensionsTest.kt | 21 +- 45 files changed, 662 insertions(+), 357 deletions(-) create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManager.kt create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerImpl.kt create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FirstTimeState.kt create mode 100644 app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 5a978ad26..0b369b6d3 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -45,6 +45,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthState import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult import com.x8bit.bitwarden.data.auth.repository.model.LoginResult import com.x8bit.bitwarden.data.auth.repository.model.NewSsoUserResult @@ -76,8 +77,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.SsoCallbackResult import com.x8bit.bitwarden.data.auth.repository.util.WebAuthResult import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow import com.x8bit.bitwarden.data.auth.repository.util.currentOnboardingStatus -import com.x8bit.bitwarden.data.auth.repository.util.currentOrDefaultUserFirstTimeState -import com.x8bit.bitwarden.data.auth.repository.util.firstTimeStateFlow import com.x8bit.bitwarden.data.auth.repository.util.onboardingStatusChangesFlow import com.x8bit.bitwarden.data.auth.repository.util.policyInformation import com.x8bit.bitwarden.data.auth.repository.util.toRemovedPasswordUserStateJson @@ -96,6 +95,7 @@ import com.x8bit.bitwarden.data.auth.util.YubiKeyResult import com.x8bit.bitwarden.data.auth.util.toSdkParams import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PushManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager @@ -164,6 +164,7 @@ class AuthRepositoryImpl( private val userLogoutManager: UserLogoutManager, private val policyManager: PolicyManager, private val featureFlagManager: FeatureFlagManager, + private val firstTimeActionManager: FirstTimeActionManager, pushManager: PushManager, dispatcherManager: DispatcherManager, ) : AuthRepository, @@ -258,7 +259,7 @@ class AuthRepositoryImpl( authDiskSource.userOrganizationsListFlow, authDiskSource.userKeyConnectorStateFlow, authDiskSource.onboardingStatusChangesFlow, - authDiskSource.firstTimeStateFlow, + firstTimeActionManager.firstTimeStateFlow, vaultRepository.vaultUnlockDataStateFlow, mutableHasPendingAccountAdditionStateFlow, // Ignore the data in the merge, but trigger an update when they emit. @@ -272,7 +273,7 @@ class AuthRepositoryImpl( val userOrganizationsList = array[2] as List val userIsUsingKeyConnectorList = array[3] as List val onboardingStatus = array[4] as OnboardingStatus? - val firstTimeState = array[5] as UserState.FirstTimeState + val firstTimeState = array[5] as FirstTimeState val vaultState = array[6] as List val hasPendingAccountAddition = array[7] as Boolean userStateJson?.toUserState( @@ -305,7 +306,7 @@ class AuthRepositoryImpl( isBiometricsEnabledProvider = ::isBiometricsEnabled, vaultUnlockTypeProvider = ::getVaultUnlockType, isDeviceTrustedProvider = ::isDeviceTrusted, - firstTimeState = authDiskSource.currentOrDefaultUserFirstTimeState, + firstTimeState = firstTimeActionManager.currentOrDefaultUserFirstTimeState, ), ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt index c7a67fc48..4e07ab2e3 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt @@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PushManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager @@ -58,6 +59,7 @@ object AuthRepositoryModule { pushManager: PushManager, policyManager: PolicyManager, featureFlagManager: FeatureFlagManager, + firstTimeActionManager: FirstTimeActionManager, ): AuthRepository = AuthRepositoryImpl( accountsService = accountsService, devicesService = devicesService, @@ -79,5 +81,6 @@ object AuthRepositoryModule { pushManager = pushManager, policyManager = policyManager, featureFlagManager = featureFlagManager, + firstTimeActionManager = firstTimeActionManager, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt index 5d51209e2..ab3f3c5ed 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserState.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository.model import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.model.UserState.Account +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.repository.model.Environment /** @@ -95,21 +96,4 @@ data class UserState( val hasLoginApprovingDevice: Boolean, val hasResetPasswordPermission: Boolean, ) - - /** - * Model to encapsulate different states for a user's first time experience. - */ - data class FirstTimeState( - val showImportLoginsCard: Boolean, - ) { - /** - * Constructs a [FirstTimeState] accepting nullable values. If a value is null, the default - * is used. - */ - constructor( - showImportLoginsCoachMarker: Boolean?, - ) : this( - showImportLoginsCard = showImportLoginsCoachMarker ?: true, - ) - } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt index 8b08770db..ece16144c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt @@ -5,7 +5,6 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations -import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -192,42 +191,3 @@ val AuthDiskSource.currentOnboardingStatus: OnboardingStatus? .userState ?.activeUserId ?.let { this.getOnboardingStatus(userId = it) } - -/** - * Returns a [Flow] that emits every time the active user's first time state is changed. - */ -@OptIn(ExperimentalCoroutinesApi::class) -val AuthDiskSource.firstTimeStateFlow: Flow - get() = activeUserIdChangesFlow - .flatMapLatest { activeUserId -> - combine( - listOf( - activeUserId - ?.let { - getShowImportLoginsFlow(it) - } - ?: flowOf(null), - ), - ) { - UserState.FirstTimeState( - showImportLoginsCoachMarker = it[0], - ) - } - } - .distinctUntilChanged() - -/** - * Get the current [UserState.FirstTimeState] of the active user if available, otherwise return - * a default configuration. - */ -val AuthDiskSource.currentOrDefaultUserFirstTimeState - get() = userState - ?.activeUserId - ?.let { - UserState.FirstTimeState( - showImportLoginsCoachMarker = getShowImportLogins(it), - ) - } - ?: UserState.FirstTimeState( - showImportLoginsCoachMarker = true, - ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt index 9fb962217..8edcf440d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt @@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.auth.repository.util import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations @@ -111,7 +112,7 @@ fun UserStateJson.toUserState( userIsUsingKeyConnectorList: List, hasPendingAccountAddition: Boolean, onboardingStatus: OnboardingStatus?, - firstTimeState: UserState.FirstTimeState, + firstTimeState: FirstTimeState, isBiometricsEnabledProvider: (userId: String) -> Boolean, vaultUnlockTypeProvider: (userId: String) -> VaultUnlockType, isDeviceTrustedProvider: (userId: String) -> Boolean, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManager.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManager.kt new file mode 100644 index 000000000..bd0356485 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManager.kt @@ -0,0 +1,47 @@ +package com.x8bit.bitwarden.data.platform.manager + +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +/** + * Manager for compiling the state of all first time actions and related information such + * as counts of notifications to show, etc. + */ +interface FirstTimeActionManager { + + /** + * Returns an observable count of the number of settings items that have a badge to display + * for the current active user. + */ + val allSettingsBadgeCountFlow: StateFlow + + /** + * Returns an observable count of the number of security settings items that have a badge to + * display for the current active user. + */ + val allSecuritySettingsBadgeCountFlow: StateFlow + + /** + * Returns an observable count of the number of autofill settings items that have a badge to + * display for the current active user. + */ + val allAutofillSettingsBadgeCountFlow: StateFlow + + /** + * Returns an observable count of the number of vault settings items that have a badge to + * display for the current active user. + */ + val allVaultSettingsBadgeCountFlow: StateFlow + + /** + * Returns a [Flow] that emits every time the active user's first time state is changed. + */ + val firstTimeStateFlow: Flow + + /** + * Get the current [FirstTimeState] of the active user if available, otherwise return + * a default configuration. + */ + val currentOrDefaultUserFirstTimeState: FirstTimeState +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerImpl.kt new file mode 100644 index 000000000..1dfca6378 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerImpl.kt @@ -0,0 +1,186 @@ +package com.x8bit.bitwarden.data.platform.manager + +import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource +import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow +import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource +import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState +import com.x8bit.bitwarden.data.platform.manager.model.FlagKey +import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +/** + * Implementation of [FirstTimeActionManager] + */ +class FirstTimeActionManagerImpl @Inject constructor( + dispatcherManager: DispatcherManager, + private val authDiskSource: AuthDiskSource, + private val settingsDiskSource: SettingsDiskSource, + private val vaultDiskSource: VaultDiskSource, + private val featureFlagManager: FeatureFlagManager, +) : FirstTimeActionManager { + + private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined) + + override val allSettingsBadgeCountFlow: StateFlow + get() = combine( + listOf( + allSecuritySettingsBadgeCountFlow, + allAutofillSettingsBadgeCountFlow, + allVaultSettingsBadgeCountFlow, + ), + ) { + it.sum() + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Lazily, + initialValue = 0, + ) + + @OptIn(ExperimentalCoroutinesApi::class) + override val allSecuritySettingsBadgeCountFlow: StateFlow + get() = authDiskSource + .activeUserIdChangesFlow + .filterNotNull() + .flatMapLatest { + // can be expanded to support multiple security settings + settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = it) + .map { showUnlockBadge -> + listOfNotNull(showUnlockBadge) + } + .map { list -> + list.count { badgeOnValue -> badgeOnValue } + } + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Lazily, + initialValue = 0, + ) + + @OptIn(ExperimentalCoroutinesApi::class) + override val allAutofillSettingsBadgeCountFlow: StateFlow + get() = authDiskSource + .activeUserIdChangesFlow + .filterNotNull() + .flatMapLatest { + // Can be expanded to support multiple autofill settings + settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = it) + .map { showAutofillBadge -> + listOfNotNull(showAutofillBadge) + } + .map { list -> + list.count { showBadge -> showBadge } + } + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Lazily, + initialValue = 0, + ) + + @OptIn(ExperimentalCoroutinesApi::class) + override val allVaultSettingsBadgeCountFlow: StateFlow + get() = authDiskSource + .activeUserIdChangesFlow + .filterNotNull() + .flatMapLatest { + combine( + getShowImportLoginsFlowInternal(userId = it), + featureFlagManager.getFeatureFlagFlow(FlagKey.ImportLoginsFlow), + ) { showImportLogins, importLoginsEnabled -> + val shouldShowImportLogins = showImportLogins && importLoginsEnabled + listOf(shouldShowImportLogins) + } + .map { list -> + list.count { showImportLogins -> showImportLogins } + } + } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Lazily, + initialValue = 0, + ) + + /** + * Returns a [Flow] that emits every time the active user's first time state is changed. + */ + @OptIn(ExperimentalCoroutinesApi::class) + override val firstTimeStateFlow: Flow + get() = authDiskSource + .activeUserIdChangesFlow + .filterNotNull() + .flatMapLatest { activeUserId -> + combine( + listOf( + getShowImportLoginsFlowInternal(userId = activeUserId), + settingsDiskSource.getShowUnlockSettingBadgeFlow(userId = activeUserId), + settingsDiskSource.getShowAutoFillSettingBadgeFlow(userId = activeUserId), + ), + ) { + FirstTimeState( + showImportLoginsCard = it[0], + showSetupUnlockCard = it[1], + showSetupAutofillCard = it[2], + ) + } + } + .onStart { + emit( + FirstTimeState( + showImportLoginsCard = null, + showSetupUnlockCard = null, + showSetupAutofillCard = null, + ), + ) + } + .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 { + 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 + * a default configuration. + */ + override val currentOrDefaultUserFirstTimeState: FirstTimeState + get() = + authDiskSource + .userState + ?.activeUserId + ?.let { + FirstTimeState( + showImportLoginsCard = authDiskSource.getShowImportLogins(it), + showSetupUnlockCard = settingsDiskSource.getShowUnlockSettingBadge(it), + showSetupAutofillCard = settingsDiskSource.getShowAutoFillSettingBadge(it), + ) + } + ?: FirstTimeState( + showImportLoginsCard = null, + showSetupUnlockCard = null, + showSetupAutofillCard = null, + ) +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt index 34e9e12c7..4bb8ee4b0 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/di/PlatformManagerModule.kt @@ -24,6 +24,8 @@ import com.x8bit.bitwarden.data.platform.manager.CrashLogsManagerImpl import com.x8bit.bitwarden.data.platform.manager.DebugMenuFeatureFlagManagerImpl import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManagerImpl +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManagerImpl import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManagerImpl import com.x8bit.bitwarden.data.platform.manager.NetworkConnectionManager @@ -55,6 +57,7 @@ import com.x8bit.bitwarden.data.platform.repository.DebugMenuRepository import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.ServerConfigRepository import com.x8bit.bitwarden.data.platform.repository.SettingsRepository +import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository import dagger.Module import dagger.Provides @@ -273,4 +276,20 @@ object PlatformManagerModule { fun provideResourceCacheManager( @ApplicationContext context: Context, ): ResourceCacheManager = ResourceCacheManagerImpl(context = context) + + @Provides + @Singleton + fun provideFirstTimeActionManager( + authDiskSource: AuthDiskSource, + settingsDiskSource: SettingsDiskSource, + vaultDiskSource: VaultDiskSource, + dispatcherManager: DispatcherManager, + featureFlagManager: FeatureFlagManager, + ): FirstTimeActionManager = FirstTimeActionManagerImpl( + authDiskSource = authDiskSource, + settingsDiskSource = settingsDiskSource, + vaultDiskSource = vaultDiskSource, + dispatcherManager = dispatcherManager, + featureFlagManager = featureFlagManager, + ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FirstTimeState.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FirstTimeState.kt new file mode 100644 index 000000000..b9a41dff4 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/FirstTimeState.kt @@ -0,0 +1,24 @@ +package com.x8bit.bitwarden.data.platform.manager.model + +/** + * Model to encapsulate different states for a user's first time experience. + */ +data class FirstTimeState( + val showImportLoginsCard: Boolean, + val showSetupUnlockCard: Boolean, + val showSetupAutofillCard: Boolean, +) { + /** + * Constructs a [FirstTimeState] accepting nullable values. If a value is null, the default + * is used. + */ + constructor( + showImportLoginsCard: Boolean? = null, + showSetupUnlockCard: Boolean? = null, + showSetupAutofillCard: Boolean? = null, + ) : this( + showImportLoginsCard = showImportLoginsCard ?: true, + showSetupUnlockCard = showSetupUnlockCard ?: false, + showSetupAutofillCard = showSetupAutofillCard ?: false, + ) +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepository.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepository.kt index b096dc072..f838cb646 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepository.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepository.kt @@ -160,24 +160,6 @@ interface SettingsRepository { */ val isScreenCaptureAllowedStateFlow: StateFlow - /** - * Returns an observable count of the number of settings items that have a badge to display - * for the current active user. - */ - val allSettingsBadgeCountFlow: StateFlow - - /** - * Returns an observable count of the number of security settings items that have a badge to - * display for the current active user. - */ - val allSecuritySettingsBadgeCountFlow: StateFlow - - /** - * Returns an observable count of the number of autofill settings items that have a badge to - * display for the current active user. - */ - val allAutofillSettingsBadgeCountFlow: StateFlow - /** * Disables autofill if it is currently enabled. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt index 2f40a567a..be03b157d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt @@ -6,7 +6,6 @@ import com.x8bit.bitwarden.BuildConfig import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult -import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow import com.x8bit.bitwarden.data.auth.repository.util.policyInformation import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityEnabledManager import com.x8bit.bitwarden.data.autofill.manager.AutofillEnabledManager @@ -30,8 +29,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn @@ -340,60 +337,6 @@ class SettingsRepositoryImpl( ?: DEFAULT_IS_SCREEN_CAPTURE_ALLOWED, ) - override val allSettingsBadgeCountFlow: StateFlow - get() = combine( - allSecuritySettingsBadgeCountFlow, - allAutofillSettingsBadgeCountFlow, - transform = ::sumSettingsBadgeCount, - ) - .stateIn( - scope = unconfinedScope, - started = SharingStarted.Lazily, - initialValue = 0, - ) - - @OptIn(ExperimentalCoroutinesApi::class) - override val allSecuritySettingsBadgeCountFlow: StateFlow - get() = authDiskSource - .activeUserIdChangesFlow - .filterNotNull() - .flatMapLatest { - // can be expanded to support multiple security settings - getShowUnlockBadgeFlow(userId = it) - .map { showUnlockBadge -> - listOf(showUnlockBadge) - } - .map { list -> - list.count { badgeOnValue -> badgeOnValue } - } - } - .stateIn( - scope = unconfinedScope, - started = SharingStarted.Lazily, - initialValue = 0, - ) - - @OptIn(ExperimentalCoroutinesApi::class) - override val allAutofillSettingsBadgeCountFlow: StateFlow - get() = authDiskSource - .activeUserIdChangesFlow - .filterNotNull() - .flatMapLatest { - // Can be expanded to support multiple autofill settings - getShowAutofillBadgeFlow(userId = it) - .map { showAutofillBadge -> - listOf(showAutofillBadge) - } - .map { list -> - list.count { showBadge -> showBadge } - } - } - .stateIn( - scope = unconfinedScope, - started = SharingStarted.Lazily, - initialValue = 0, - ) - init { policyManager .getActivePoliciesFlow(type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT) @@ -660,10 +603,6 @@ class SettingsRepositoryImpl( } } } - - // helper function to sum badge counts from different settings sub-menus. - private fun sumSettingsBadgeCount(autoFillBadgeCount: Int, securityBadgeCount: Int) = - autoFillBadgeCount + securityBadgeCount } /** diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt index c3af8d010..fbbed1dcf 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModel.kt @@ -3,9 +3,9 @@ package com.x8bit.bitwarden.ui.platform.feature.settings import androidx.compose.material3.Text import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.R +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance -import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.util.BackgroundEvent import com.x8bit.bitwarden.ui.platform.base.util.Text @@ -22,19 +22,19 @@ import javax.inject.Inject */ @HiltViewModel class SettingsViewModel @Inject constructor( - settingsRepository: SettingsRepository, specialCircumstanceManager: SpecialCircumstanceManager, + firstTimeActionManager: FirstTimeActionManager, ) : BaseViewModel( initialState = SettingsState( - securityCount = settingsRepository.allSecuritySettingsBadgeCountFlow.value, - autoFillCount = settingsRepository.allAutofillSettingsBadgeCountFlow.value, + securityCount = firstTimeActionManager.allSecuritySettingsBadgeCountFlow.value, + autoFillCount = firstTimeActionManager.allAutofillSettingsBadgeCountFlow.value, ), ) { init { combine( - settingsRepository.allSecuritySettingsBadgeCountFlow, - settingsRepository.allAutofillSettingsBadgeCountFlow, + firstTimeActionManager.allSecuritySettingsBadgeCountFlow, + firstTimeActionManager.allAutofillSettingsBadgeCountFlow, ) { securityCount, autofillCount -> SettingsAction.Internal.SettingsNotificationCountUpdate( securityCount = securityCount, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModel.kt index 4b26f2d1d..691e9ba26 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModel.kt @@ -5,9 +5,9 @@ import androidx.lifecycle.viewModelScope import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.UserState +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance -import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.ui.platform.base.BaseViewModel import com.x8bit.bitwarden.ui.platform.base.util.BackgroundEvent import com.x8bit.bitwarden.ui.platform.feature.vaultunlockednavbar.model.VaultUnlockedNavBarTab @@ -24,13 +24,13 @@ import javax.inject.Inject class VaultUnlockedNavBarViewModel @Inject constructor( authRepository: AuthRepository, specialCircumstancesManager: SpecialCircumstanceManager, - settingsRepository: SettingsRepository, + firstTimeActionManager: FirstTimeActionManager, ) : BaseViewModel( initialState = VaultUnlockedNavBarState( vaultNavBarLabelRes = R.string.my_vault, vaultNavBarContentDescriptionRes = R.string.my_vault, notificationState = VaultUnlockedNavBarNotificationState( - settingsTabNotificationCount = settingsRepository.allSettingsBadgeCountFlow.value, + settingsTabNotificationCount = firstTimeActionManager.allSettingsBadgeCountFlow.value, ), ), ) { @@ -42,7 +42,7 @@ class VaultUnlockedNavBarViewModel @Inject constructor( } .launchIn(viewModelScope) - settingsRepository + firstTimeActionManager .allSettingsBadgeCountFlow .onEach { sendAction(VaultUnlockedNavBarAction.Internal.SettingsNotificationCountUpdate(it)) diff --git a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt index b219e288e..116737048 100644 --- a/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/MainViewModelTest.kt @@ -8,6 +8,7 @@ import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.util.getCompleteRegistrationDataIntentOrNull @@ -1156,7 +1157,7 @@ private val DEFAULT_STATE: MainState = MainState( isScreenCaptureAllowed = true, ) -private val DEFAULT_FIRST_TIME_STATE = UserState.FirstTimeState( +private val DEFAULT_FIRST_TIME_STATE = FirstTimeState( showImportLoginsCard = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 60ffe0796..162ba1851 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -65,6 +65,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.AuthState import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult import com.x8bit.bitwarden.data.auth.repository.model.LoginResult import com.x8bit.bitwarden.data.auth.repository.model.NewSsoUserResult @@ -82,7 +83,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations -import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType @@ -102,6 +102,7 @@ import com.x8bit.bitwarden.data.platform.datasource.disk.model.ServerConfig import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeConfigDiskSource import com.x8bit.bitwarden.data.platform.datasource.network.model.ConfigResponseJson import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PushManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager @@ -244,6 +245,11 @@ class AuthRepositoryTest { every { getFeatureFlag(FlagKey.OnboardingFlow) } returns false } + private val firstTimeActionManager = mockk { + every { currentOrDefaultUserFirstTimeState } returns FIRST_TIME_STATE + every { firstTimeStateFlow } returns MutableStateFlow(FIRST_TIME_STATE) + } + private val repository = AuthRepositoryImpl( accountsService = accountsService, devicesService = devicesService, @@ -265,6 +271,7 @@ class AuthRepositoryTest { pushManager = pushManager, policyManager = policyManager, featureFlagManager = featureFlagManager, + firstTimeActionManager = firstTimeActionManager, ) @BeforeEach @@ -6544,7 +6551,7 @@ class AuthRepositoryTest { ), ) - private val FIRST_TIME_STATE = UserState.FirstTimeState( + private val FIRST_TIME_STATE = FirstTimeState( showImportLoginsCard = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt index ec66e87aa..f91e231cc 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt @@ -11,7 +11,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations -import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization @@ -507,48 +506,6 @@ class AuthDiskSourceExtensionsTest { authDiskSource.currentOnboardingStatus, ) } - - @Test - fun `firstTimeStateFlow should emit changes when items in the first time state change`() = - runTest { - authDiskSource.firstTimeStateFlow.test { - authDiskSource.userState = MOCK_USER_STATE - assertEquals( - UserState.FirstTimeState( - showImportLoginsCard = true, - ), - awaitItem(), - ) - authDiskSource.storeShowImportLogins(MOCK_USER_ID, false) - assertEquals( - UserState.FirstTimeState( - showImportLoginsCard = false, - ), - awaitItem(), - ) - } - } - - @Suppress("MaxLineLength") - @Test - fun `currentOrDefaultUserFirstTimeState should return the current first time state or a default state`() { - authDiskSource.userState = MOCK_USER_STATE - // Assert default state when no values set - assertEquals( - UserState.FirstTimeState( - showImportLoginsCard = true, - ), - authDiskSource.currentOrDefaultUserFirstTimeState, - ) - authDiskSource.storeShowImportLogins(MOCK_USER_ID, false) - - assertEquals( - UserState.FirstTimeState( - showImportLoginsCard = false, - ), - authDiskSource.currentOrDefaultUserFirstTimeState, - ) - } } private const val MOCK_USER_ID: String = "mockId-1" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index a6c877f77..ad9697dc1 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserAccountTokens import com.x8bit.bitwarden.data.auth.repository.model.UserKeyConnectorState @@ -358,7 +359,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ), @@ -428,7 +429,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.PIN }, isDeviceTrustedProvider = { false }, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -466,7 +467,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -532,7 +533,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { false }, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -576,7 +577,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -645,7 +646,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -689,7 +690,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -758,7 +759,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -802,7 +803,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -871,7 +872,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -919,7 +920,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -988,7 +989,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -1017,7 +1018,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -1066,7 +1067,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -1095,7 +1096,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -1146,7 +1147,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -1191,7 +1192,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = true, @@ -1262,7 +1263,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = null, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } @@ -1306,7 +1307,7 @@ class UserStateJsonExtensionsTest { hasMasterPassword = false, isUsingKeyConnector = true, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = false, ), ), @@ -1377,7 +1378,7 @@ class UserStateJsonExtensionsTest { vaultUnlockTypeProvider = { VaultUnlockType.MASTER_PASSWORD }, isDeviceTrustedProvider = { true }, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = false, ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt index 596879d34..a011d9c95 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/fido2/processor/Fido2ProviderProcessorTest.kt @@ -24,6 +24,7 @@ import com.bitwarden.sdk.Fido2CredentialStore import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager @@ -537,7 +538,7 @@ private fun createMockAccounts(number: Int): List { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt new file mode 100644 index 000000000..5129f9c32 --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/FirstTimeActionManagerTest.kt @@ -0,0 +1,234 @@ +package com.x8bit.bitwarden.data.platform.manager + +import app.cash.turbine.test +import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson +import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson +import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson +import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource +import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson +import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson +import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager +import com.x8bit.bitwarden.data.platform.datasource.disk.util.FakeSettingsDiskSource +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState +import com.x8bit.bitwarden.data.platform.manager.model.FlagKey +import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource +import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class FirstTimeActionManagerTest { + private val fakeAuthDiskSource = FakeAuthDiskSource() + private val fakeSettingsDiskSource = FakeSettingsDiskSource() + private val mutableCiphersListFlow = MutableStateFlow(emptyList()) + private val vaultDiskSource = mockk { + every { getCiphers(any()) } returns mutableCiphersListFlow + } + + private val mutableImportLoginsFlow = MutableStateFlow(false) + private val featureFlagManager = mockk { + every { getFeatureFlagFlow(FlagKey.ImportLoginsFlow) } returns mutableImportLoginsFlow + } + + private val firstTimeActionManager = FirstTimeActionManagerImpl( + authDiskSource = fakeAuthDiskSource, + settingsDiskSource = fakeSettingsDiskSource, + vaultDiskSource = vaultDiskSource, + featureFlagManager = featureFlagManager, + dispatcherManager = FakeDispatcherManager(), + ) + + @Suppress("MaxLineLength") + @Test + fun `allAutoFillSettingsBadgeCountFlow should emit the value of flags set to true and update when changed`() = + runTest { + fakeAuthDiskSource.userState = MOCK_USER_STATE + firstTimeActionManager.allAutofillSettingsBadgeCountFlow.test { + assertEquals(0, awaitItem()) + fakeSettingsDiskSource.storeShowAutoFillSettingBadge( + userId = USER_ID, + showBadge = true, + ) + assertEquals(1, awaitItem()) + fakeSettingsDiskSource.storeShowAutoFillSettingBadge( + userId = USER_ID, + showBadge = false, + ) + assertEquals(0, awaitItem()) + } + } + + @Suppress("MaxLineLength") + @Test + fun `allSecuritySettingsBadgeCountFlow should emit the value of flags set to true and update when changed`() = + runTest { + fakeAuthDiskSource.userState = MOCK_USER_STATE + firstTimeActionManager.allSecuritySettingsBadgeCountFlow.test { + assertEquals(0, awaitItem()) + fakeSettingsDiskSource.storeShowUnlockSettingBadge( + userId = USER_ID, + showBadge = true, + ) + assertEquals(1, awaitItem()) + fakeSettingsDiskSource.storeShowUnlockSettingBadge( + userId = USER_ID, + showBadge = false, + ) + assertEquals(0, awaitItem()) + } + } + + @Suppress("MaxLineLength") + @Test + fun `allSettingsBadgeCountFlow should emit the value of all flags set to true and update when changed`() = + runTest { + fakeAuthDiskSource.userState = MOCK_USER_STATE + firstTimeActionManager.allSettingsBadgeCountFlow.test { + assertEquals(0, awaitItem()) + fakeSettingsDiskSource.storeShowAutoFillSettingBadge( + userId = USER_ID, + showBadge = true, + ) + assertEquals(1, awaitItem()) + fakeSettingsDiskSource.storeShowUnlockSettingBadge( + userId = USER_ID, + showBadge = true, + ) + assertEquals(2, awaitItem()) + fakeSettingsDiskSource.storeShowAutoFillSettingBadge( + userId = USER_ID, + showBadge = false, + ) + assertEquals(1, awaitItem()) + // for the import logins count it is dependent on the feature flag state and + // cipher list being empty + mutableImportLoginsFlow.update { true } + fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) + assertEquals(2, awaitItem()) + } + } + + @Suppress("MaxLineLength") + @Test + fun `allVaultSettingsBadgeCountFlow should emit the value of all flags set to true and update when dependent states are changed changed`() = + runTest { + fakeAuthDiskSource.userState = MOCK_USER_STATE + val mockCipher = mockk(relaxed = true) + // For the import logins count to register, the feature flag for ImportLoginsFlow must + // be enabled, the cipher list must not be empty, and the value saved to disk should be + // true. + firstTimeActionManager.allVaultSettingsBadgeCountFlow.test { + assertEquals(0, awaitItem()) + mutableImportLoginsFlow.update { true } + fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) + assertEquals(1, awaitItem()) + mutableImportLoginsFlow.update { false } + assertEquals(0, awaitItem()) + mutableImportLoginsFlow.update { true } + assertEquals(1, awaitItem()) + fakeAuthDiskSource.storeShowImportLogins(USER_ID, false) + assertEquals(0, awaitItem()) + fakeAuthDiskSource.storeShowImportLogins(USER_ID, true) + assertEquals(1, awaitItem()) + mutableCiphersListFlow.update { + listOf(mockCipher) + } + assertEquals(0, awaitItem()) + } + } + + @Test + fun `firstTimeStateFlow should emit changes when items in the first time state change`() = + runTest { + firstTimeActionManager.firstTimeStateFlow.test { + fakeAuthDiskSource.userState = + MOCK_USER_STATE + assertEquals( + FirstTimeState( + showImportLoginsCard = true, + ), + awaitItem(), + ) + fakeAuthDiskSource.storeShowImportLogins(USER_ID, false) + assertEquals( + FirstTimeState( + showImportLoginsCard = false, + ), + awaitItem(), + ) + } + } + + @Suppress("MaxLineLength") + @Test + fun `currentOrDefaultUserFirstTimeState should return the current first time state or a default state`() { + fakeAuthDiskSource.userState = MOCK_USER_STATE + // Assert default state when no values set + assertEquals( + FirstTimeState( + showImportLoginsCard = true, + ), + firstTimeActionManager.currentOrDefaultUserFirstTimeState, + ) + fakeAuthDiskSource.storeShowImportLogins(USER_ID, false) + + assertEquals( + FirstTimeState( + showImportLoginsCard = false, + ), + firstTimeActionManager.currentOrDefaultUserFirstTimeState, + ) + } +} + +private const val USER_ID: String = "userId" + +private val MOCK_TRUSTED_DEVICE_USER_DECRYPTION_OPTIONS = TrustedDeviceUserDecryptionOptionsJson( + encryptedPrivateKey = null, + encryptedUserKey = null, + hasAdminApproval = false, + hasLoginApprovingDevice = false, + hasManageResetPasswordPermission = false, +) + +private val MOCK_USER_DECRYPTION_OPTIONS: UserDecryptionOptionsJson = UserDecryptionOptionsJson( + hasMasterPassword = false, + trustedDeviceUserDecryptionOptions = MOCK_TRUSTED_DEVICE_USER_DECRYPTION_OPTIONS, + keyConnectorUserDecryptionOptions = null, +) + +private val MOCK_PROFILE = AccountJson.Profile( + userId = USER_ID, + email = "test@bitwarden.com", + isEmailVerified = true, + name = "Bitwarden Tester", + hasPremium = false, + stamp = null, + organizationId = null, + avatarColorHex = null, + forcePasswordResetReason = null, + kdfType = KdfTypeJson.ARGON2_ID, + kdfIterations = 600000, + kdfMemory = 16, + kdfParallelism = 4, + userDecryptionOptions = MOCK_USER_DECRYPTION_OPTIONS, +) + +private val MOCK_ACCOUNT = AccountJson( + profile = MOCK_PROFILE, + settings = AccountJson.Settings( + environmentUrlData = EnvironmentUrlDataJson.DEFAULT_US, + ), +) + +private val MOCK_USER_STATE = UserStateJson( + activeUserId = USER_ID, + accounts = mapOf( + USER_ID to MOCK_ACCOUNT, + ), +) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt index 36f7ca029..2d8ba01a2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt @@ -1234,71 +1234,6 @@ class SettingsRepositoryTest { assertTrue(awaitItem()) } } - - @Suppress("MaxLineLength") - @Test - fun `allAutoFillSettingsBadgeCountFlow should emit the value of flags set to true and update when changed`() = - runTest { - fakeAuthDiskSource.userState = MOCK_USER_STATE - settingsRepository.allAutofillSettingsBadgeCountFlow.test { - assertEquals(0, awaitItem()) - fakeSettingsDiskSource.storeShowAutoFillSettingBadge( - userId = USER_ID, - showBadge = true, - ) - assertEquals(1, awaitItem()) - fakeSettingsDiskSource.storeShowAutoFillSettingBadge( - userId = USER_ID, - showBadge = false, - ) - assertEquals(0, awaitItem()) - } - } - - @Suppress("MaxLineLength") - @Test - fun `allSecuritySettingsBadgeCountFlow should emit the value of flags set to true and update when changed`() = - runTest { - fakeAuthDiskSource.userState = MOCK_USER_STATE - settingsRepository.allSecuritySettingsBadgeCountFlow.test { - assertEquals(0, awaitItem()) - fakeSettingsDiskSource.storeShowUnlockSettingBadge( - userId = USER_ID, - showBadge = true, - ) - assertEquals(1, awaitItem()) - fakeSettingsDiskSource.storeShowUnlockSettingBadge( - userId = USER_ID, - showBadge = false, - ) - assertEquals(0, awaitItem()) - } - } - - @Suppress("MaxLineLength") - @Test - fun `allSettingsBadgeCountFlow should emit the value of all flags set to true and update when changed`() = - runTest { - fakeAuthDiskSource.userState = MOCK_USER_STATE - settingsRepository.allSettingsBadgeCountFlow.test { - assertEquals(0, awaitItem()) - fakeSettingsDiskSource.storeShowAutoFillSettingBadge( - userId = USER_ID, - showBadge = true, - ) - assertEquals(1, awaitItem()) - fakeSettingsDiskSource.storeShowUnlockSettingBadge( - userId = USER_ID, - showBadge = true, - ) - assertEquals(2, awaitItem()) - fakeSettingsDiskSource.storeShowAutoFillSettingBadge( - userId = USER_ID, - showBadge = false, - ) - assertEquals(1, awaitItem()) - } - } } private const val USER_ID: String = "userId" diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt index 29c35f1d5..22c876854 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt @@ -5,6 +5,7 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository @@ -388,7 +389,7 @@ private val DEFAULT_USER_ACCOUNT = UserState.Account( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.ACCOUNT_LOCK_SETUP, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val CIPHER = mockk() diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt index a61b6516c..b4d31ec86 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt @@ -5,6 +5,7 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager @@ -88,7 +89,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) @@ -226,7 +227,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) val userState = UserState( activeUserId = "activeUserId", @@ -283,7 +284,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) val userState = UserState( activeUserId = "activeUserId", @@ -344,7 +345,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) val userState = UserState( activeUserId = "activeUserId", @@ -521,7 +522,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) val userState = UserState( @@ -557,7 +558,7 @@ class LandingViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) val userState = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt index c05baa675..d7fad0ff3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt @@ -7,6 +7,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult import com.x8bit.bitwarden.data.auth.repository.model.LoginResult import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -131,7 +132,7 @@ class LoginViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt index ca2ac66fe..e8e711d74 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/removepassword/RemovePasswordViewModelTest.kt @@ -5,6 +5,7 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.RemovePasswordResult import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -167,7 +168,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt index 26f223735..0d0798fe5 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/trusteddevice/TrustedDeviceViewModelTest.kt @@ -6,6 +6,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.AuthState +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.NewSsoUserResult import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository @@ -276,7 +277,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( hasMasterPassword = false, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index 5484ec38b..d4179ff53 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -6,6 +6,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType @@ -219,7 +220,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) @@ -259,7 +260,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), @@ -1134,7 +1135,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt index 06908a483..e071510c0 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModelTest.kt @@ -4,6 +4,7 @@ import android.content.pm.SigningInfo import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.AuthState +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.JwtTokenDataJson import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -106,7 +107,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -143,7 +144,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -177,7 +178,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -221,7 +222,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = false, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -262,7 +263,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -303,7 +304,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = false, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -341,7 +342,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -392,7 +393,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -427,7 +428,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -467,7 +468,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -506,7 +507,7 @@ class RootNavViewModelTest : BaseViewModelTest() { trustedDevice = null, hasMasterPassword = true, isUsingKeyConnector = false, - firstTimeState = UserState.FirstTimeState(false), + firstTimeState = FirstTimeState(false), onboardingStatus = OnboardingStatus.COMPLETE, ), ), @@ -548,7 +549,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -591,7 +592,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -641,7 +642,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -691,7 +692,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -736,7 +737,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -780,7 +781,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -859,7 +860,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -913,7 +914,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -975,7 +976,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1017,7 +1018,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1054,7 +1055,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1089,7 +1090,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1127,7 +1128,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.ACCOUNT_LOCK_SETUP, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1165,7 +1166,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.AUTOFILL_SETUP, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1203,7 +1204,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.FINAL_STEP, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1241,7 +1242,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.NOT_STARTED, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1279,7 +1280,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), @@ -1317,7 +1318,7 @@ class RootNavViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState( + firstTimeState = FirstTimeState( showImportLoginsCard = true, ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt index deaa780f0..a66fed09b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/search/SearchViewModelTest.kt @@ -9,6 +9,7 @@ import com.bitwarden.vault.LoginUriView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilitySelectionManager @@ -1537,7 +1538,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt index d21b937dd..4114b7adb 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsViewModelTest.kt @@ -1,9 +1,9 @@ package com.x8bit.bitwarden.ui.platform.feature.settings import app.cash.turbine.test +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance -import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest import io.mockk.every import io.mockk.just @@ -20,7 +20,7 @@ class SettingsViewModelTest : BaseViewModelTest() { private val mutableAutofillBadgeCountFlow = MutableStateFlow(0) private val mutableSecurityBadgeCountFlow = MutableStateFlow(0) - private val settingsRepository = mockk { + private val firstTimeManager = mockk { every { allSecuritySettingsBadgeCountFlow } returns mutableSecurityBadgeCountFlow every { allAutofillSettingsBadgeCountFlow } returns mutableAutofillBadgeCountFlow } @@ -145,7 +145,7 @@ class SettingsViewModelTest : BaseViewModelTest() { } private fun createViewModel() = SettingsViewModel( - settingsRepository = settingsRepository, + firstTimeActionManager = firstTimeManager, specialCircumstanceManager = specialCircumstanceManager, ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt index 7821e9bd2..e6355b3ab 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt @@ -5,6 +5,7 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -792,7 +793,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt index cdbc86393..654bf4985 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/deleteaccount/DeleteAccountViewModelTest.kt @@ -6,6 +6,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.platform.repository.model.Environment @@ -244,7 +245,7 @@ private val DEFAULT_USER_STATE: UserState = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt index 2013d82f6..d939f7ad2 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/loginapproval/LoginApprovalViewModelTest.kt @@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.manager.model.AuthRequest import com.x8bit.bitwarden.data.auth.manager.model.AuthRequestResult import com.x8bit.bitwarden.data.auth.manager.model.AuthRequestUpdatesResult import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData @@ -372,7 +373,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt index 55c3b597a..6352df413 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/exportvault/ExportVaultViewModelTest.kt @@ -8,6 +8,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.PasswordStrengthResult import com.x8bit.bitwarden.data.auth.repository.model.RequestOtpResult import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -724,7 +725,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModelTest.kt index f658b8474..70eb8d1fa 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/vaultunlockednavbar/VaultUnlockedNavBarViewModelTest.kt @@ -4,9 +4,9 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.UserState +import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance -import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest import io.mockk.every import io.mockk.just @@ -29,7 +29,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() { } private val mutableSettingsBadgeCountFlow = MutableStateFlow(0) - private val settingsRepository: SettingsRepository = mockk { + private val firstTimeActionManager: FirstTimeActionManager = mockk { every { allSettingsBadgeCountFlow } returns mutableSettingsBadgeCountFlow } @@ -253,7 +253,7 @@ class VaultUnlockedNavBarViewModelTest : BaseViewModelTest() { VaultUnlockedNavBarViewModel( authRepository = authRepository, specialCircumstancesManager = specialCircumstancesManager, - settingsRepository = settingsRepository, + firstTimeActionManager = firstTimeActionManager, ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt index c7255fed4..9e59b59ad 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorViewModelTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.generators.PasswordGeneratorRequest import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager @@ -2408,7 +2409,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt index 8f548f023..9cabe8764 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/addsend/AddSendViewModelTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.send.SendView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.manager.PolicyManager @@ -1103,7 +1104,7 @@ class AddSendViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt index 3bcc0eb71..c8e639808 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/VaultAddEditViewModelTest.kt @@ -13,6 +13,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult @@ -3965,7 +3966,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), hasPendingAccountAddition = false, diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt index d2f245d67..42ddebca9 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/addedit/util/CipherViewExtensionsTest.kt @@ -14,6 +14,7 @@ import com.bitwarden.vault.SecureNoteType import com.bitwarden.vault.SecureNoteView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType @@ -513,7 +514,7 @@ class CipherViewExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt index 926458ee2..6539b8306 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/attachments/AttachmentsViewModelTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.Environment @@ -564,7 +565,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) 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 4ac0fcfdf..be164eff4 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 @@ -9,6 +9,7 @@ import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager @@ -2581,7 +2582,7 @@ class VaultItemViewModelTest : BaseViewModelTest() { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index 802d11c8a..4b2514d57 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -10,6 +10,7 @@ import com.bitwarden.vault.CipherView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult @@ -4032,7 +4033,7 @@ private val DEFAULT_ACCOUNT = UserState.Account( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) private val DEFAULT_USER_STATE = UserState( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt index e91faa724..45219acb1 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/VaultMoveToOrganizationViewModelTest.kt @@ -7,6 +7,7 @@ import com.bitwarden.vault.CollectionView import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.model.DataState @@ -515,7 +516,7 @@ private val DEFAULT_USER_STATE = UserState( hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt index a74f0af7a..2cfb48ce8 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/movetoorganization/util/VaultMoveToOrganizationExtensionsTest.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.movetoorganization.util import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.model.Environment @@ -130,7 +131,7 @@ private fun createMockUserState(hasOrganizations: Boolean = true): UserState = hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index 3d0f8731c..74455157b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -4,6 +4,7 @@ import app.cash.turbine.test import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult import com.x8bit.bitwarden.data.auth.repository.model.UserState @@ -1639,7 +1640,7 @@ private val VAULT_FILTER_DATA = VaultFilterData( private val DEFAULT_STATE: VaultState = createMockVaultState(viewState = VaultState.ViewState.Loading) -private val DEFAULT_FIRST_TIME_STATE = UserState.FirstTimeState( +private val DEFAULT_FIRST_TIME_STATE = FirstTimeState( showImportLoginsCard = true, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt index 52be5a25e..bcffa3edc 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/util/UserStateExtensionsTest.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.vault.util import com.x8bit.bitwarden.data.auth.datasource.disk.model.EnvironmentUrlDataJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus +import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.repository.model.Environment @@ -87,7 +88,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), UserState.Account( userId = "lockedUserId", @@ -114,7 +115,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), UserState.Account( userId = "unlockedUserId", @@ -145,7 +146,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), UserState.Account( userId = "loggedOutUserId", @@ -176,7 +177,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) @@ -222,7 +223,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) .toAccountSummary(isActive = true), ) @@ -266,7 +267,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) .toAccountSummary(isActive = false), ) @@ -314,7 +315,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ), ), ) @@ -342,7 +343,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) .toVaultFilterData(isIndividualVaultDisabled = false), ) @@ -399,7 +400,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) .toVaultFilterData( isIndividualVaultDisabled = false, @@ -457,7 +458,7 @@ class UserStateExtensionsTest { hasMasterPassword = true, isUsingKeyConnector = false, onboardingStatus = OnboardingStatus.COMPLETE, - firstTimeState = UserState.FirstTimeState(showImportLoginsCard = true), + firstTimeState = FirstTimeState(showImportLoginsCard = true), ) .toVaultFilterData( isIndividualVaultDisabled = true,