From ddb680c2105d690d70941649ef83975043ec5c1f Mon Sep 17 00:00:00 2001 From: Ramsey Smith <142836716+ramsey-livefront@users.noreply.github.com> Date: Thu, 9 May 2024 10:44:30 -0600 Subject: [PATCH] BIT-2335: Onboarding flow (data) (#1346) --- .../auth/repository/AuthRepositoryImpl.kt | 2 ++ .../datasource/disk/SettingsDiskSource.kt | 10 +++++++ .../datasource/disk/SettingsDiskSourceImpl.kt | 14 ++++++++++ .../platform/repository/SettingsRepository.kt | 10 +++++++ .../repository/SettingsRepositoryImpl.kt | 16 +++++++++++ .../auth/repository/AuthRepositoryTest.kt | 1 + .../datasource/disk/SettingsDiskSourceTest.kt | 27 +++++++++++++++++++ .../disk/util/FakeSettingsDiskSource.kt | 16 +++++++++++ .../repository/SettingsRepositoryTest.kt | 13 +++++++++ 9 files changed, 109 insertions(+) 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 d5c6652c25..dca0d5bd20 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 @@ -723,6 +723,7 @@ class AuthRepositoryImpl( } is RegisterResponseJson.Success -> { + settingsRepository.hasUserLoggedInOrCreatedAccount = true RegisterResult.Success(captchaToken = it.captchaBypassToken) } @@ -1280,6 +1281,7 @@ class AuthRepositoryImpl( refreshToken = loginResponse.refreshToken, ), ) + settingsRepository.hasUserLoggedInOrCreatedAccount = true authDiskSource.userState = userStateJson loginResponse.key?.let { // Only set the value if it's present, since we may have set it already diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt index a7fbaf83d9..72d4ede447 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSource.kt @@ -58,6 +58,16 @@ interface SettingsDiskSource { */ val isCrashLoggingEnabledFlow: Flow + /** + * The current status if a user has logged in or created an account. + */ + var hasUserLoggedInOrCreatedAccount: Boolean? + + /** + * Emits updates that track [hasUserLoggedInOrCreatedAccount]. + */ + val hasUserLoggedInOrCreatedAccountFlow: Flow + /** * Clears all the settings data for the given user. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt index 379a0de70c..f37cb0c8b7 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceImpl.kt @@ -32,6 +32,7 @@ private const val ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY = "accountBiometricInteg private const val CRASH_LOGGING_ENABLED_KEY = "crashLoggingEnabled" private const val CLEAR_CLIPBOARD_INTERVAL_KEY = "clearClipboard" private const val INITIAL_AUTOFILL_DIALOG_SHOWN = "addSitePromptShown" +private const val HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY = "hasUserLoggedInOrCreatedAccount" /** * Primary implementation of [SettingsDiskSource]. @@ -59,6 +60,8 @@ class SettingsDiskSourceImpl( private val mutableIsCrashLoggingEnabledFlow = bufferedMutableSharedFlow() + private val mutableHasUserLoggedInOrCreatedAccountFlow = bufferedMutableSharedFlow() + private val mutableScreenCaptureAllowedFlowMap = mutableMapOf>() @@ -129,6 +132,17 @@ class SettingsDiskSourceImpl( get() = mutableIsCrashLoggingEnabledFlow .onSubscription { emit(getBoolean(CRASH_LOGGING_ENABLED_KEY)) } + override var hasUserLoggedInOrCreatedAccount: Boolean? + get() = getBoolean(key = HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY) + set(value) { + putBoolean(key = HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY, value = value) + mutableHasUserLoggedInOrCreatedAccountFlow.tryEmit(value) + } + + override val hasUserLoggedInOrCreatedAccountFlow: Flow + get() = mutableHasUserLoggedInOrCreatedAccountFlow + .onSubscription { emit(getBoolean(HAS_USER_LOGGED_IN_OR_CREATED_AN_ACCOUNT_KEY)) } + override fun clearData(userId: String) { storeVaultTimeoutInMinutes(userId = userId, vaultTimeoutInMinutes = null) storeVaultTimeoutAction(userId = userId, vaultTimeoutAction = null) 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 5c15fe7ea1..61adc350e4 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 @@ -72,6 +72,16 @@ interface SettingsRepository { */ val isCrashLoggingEnabledFlow: Flow + /** + * The current status if a user has logged in or created an account. + */ + var hasUserLoggedInOrCreatedAccount: Boolean + + /** + * Emits updates that track the [hasUserLoggedInOrCreatedAccount] value. + */ + val hasUserLoggedInOrCreatedAccountFlow: Flow + /** * The [VaultTimeout] for the current user. */ 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 1fa6c1cf57..82c5f23b2c 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 @@ -145,6 +145,22 @@ class SettingsRepositoryImpl( initialValue = isCrashLoggingEnabled, ) + override var hasUserLoggedInOrCreatedAccount: Boolean + get() = settingsDiskSource.hasUserLoggedInOrCreatedAccount ?: false + set(value) { + settingsDiskSource.hasUserLoggedInOrCreatedAccount = value + } + + override val hasUserLoggedInOrCreatedAccountFlow: Flow + get() = settingsDiskSource + .hasUserLoggedInOrCreatedAccountFlow + .map { it ?: hasUserLoggedInOrCreatedAccount } + .stateIn( + scope = unconfinedScope, + started = SharingStarted.Eagerly, + initialValue = hasUserLoggedInOrCreatedAccount, + ) + override var vaultTimeout: VaultTimeout get() = activeUserId ?.let { 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 723489ae1b..1ebe033e4e 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 @@ -149,6 +149,7 @@ class AuthRepositoryTest { } private val settingsRepository: SettingsRepository = mockk { every { setDefaultsIfNecessary(any()) } just runs + every { hasUserLoggedInOrCreatedAccount = true } just runs } private val authSdkSource = mockk { coEvery { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt index ab5bb37b75..deaf2a224d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/SettingsDiskSourceTest.kt @@ -285,6 +285,33 @@ class SettingsDiskSourceTest { ) } + @Test + fun `hasUserLoggedInOrCreatedAccount should pull from and update SharedPreferences`() { + val hasUserLoggedInOrCreatedAccount = "bwPreferencesStorage:hasUserLoggedInOrCreatedAccount" + val expected = false + + assertNull(settingsDiskSource.hasUserLoggedInOrCreatedAccount) + + fakeSharedPreferences + .edit { + putBoolean( + hasUserLoggedInOrCreatedAccount, + expected, + ) + } + assertEquals( + expected, + settingsDiskSource.hasUserLoggedInOrCreatedAccount, + ) + + settingsDiskSource.hasUserLoggedInOrCreatedAccount = true + assertTrue( + fakeSharedPreferences.getBoolean( + hasUserLoggedInOrCreatedAccount, false, + ), + ) + } + @Test fun `appTheme when values are present should pull from SharedPreferences`() { val appThemeBaseKey = "bwPreferencesStorage:appTheme" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt index 479fe72e7c..1e63e04bcf 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/datasource/disk/util/FakeSettingsDiskSource.kt @@ -36,6 +36,9 @@ class FakeSettingsDiskSource : SettingsDiskSource { private val mutableIsCrashLoggingEnabled = bufferedMutableSharedFlow() + private val mutableHasUserLoggedInOrCreatedAccount = + bufferedMutableSharedFlow() + private val mutableScreenCaptureAllowedFlowMap = mutableMapOf>() @@ -52,6 +55,7 @@ class FakeSettingsDiskSource : SettingsDiskSource { private val storedBlockedAutofillUris = mutableMapOf?>() private var storedIsIconLoadingDisabled: Boolean? = null private var storedIsCrashLoggingEnabled: Boolean? = null + private var storedHasUserLoggedInOrCreatedAccount: Boolean? = null private var storedInitialAutofillDialogShown: Boolean? = null private val storedScreenCaptureAllowed = mutableMapOf() private var storedSystemBiometricIntegritySource: String? = null @@ -107,6 +111,18 @@ class FakeSettingsDiskSource : SettingsDiskSource { emit(isCrashLoggingEnabled) } + override var hasUserLoggedInOrCreatedAccount: Boolean? + get() = storedHasUserLoggedInOrCreatedAccount + set(value) { + storedHasUserLoggedInOrCreatedAccount = value + mutableHasUserLoggedInOrCreatedAccount.tryEmit(value) + } + + override val hasUserLoggedInOrCreatedAccountFlow: Flow + get() = mutableHasUserLoggedInOrCreatedAccount.onSubscription { + emit(hasUserLoggedInOrCreatedAccount) + } + override fun getAccountBiometricIntegrityValidity( userId: String, systemBioIntegrityState: String, 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 65af832d6b..03c052c671 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 @@ -273,6 +273,19 @@ class SettingsRepositoryTest { assertTrue(fakeSettingsDiskSource.isCrashLoggingEnabled!!) } + @Test + fun `hasUserLoggedInOrCreatedAccount should pull from and update SettingsDiskSource`() { + assertFalse(settingsRepository.hasUserLoggedInOrCreatedAccount) + + // Updates to the disk source change the repository value. + fakeSettingsDiskSource.hasUserLoggedInOrCreatedAccount = false + assertFalse(settingsRepository.hasUserLoggedInOrCreatedAccount) + + // Updates to the repository change the disk source value + settingsRepository.hasUserLoggedInOrCreatedAccount = true + assertTrue(fakeSettingsDiskSource.hasUserLoggedInOrCreatedAccount!!) + } + @Test fun `appTheme should pull from and update SettingsDiskSource`() { fakeAuthDiskSource.userState = null