Skip to content

Commit 440e92c

Browse files
committed
fix(onboarding): preserve seenAccessKey flag across restart so onboarding resumes at access key
onAccountPurchased was removing seenAccessKeyKey, and login always set LoggedInWithUser when flags.isRegistered was true, skipping the access key screen on restart. Now onAccountPurchased preserves the flag value, hasSeenAccessKey falls back to selectedAccountIdKey, login gates LoggedInWithUser on seenAccessKey, and RealSessionController respects incomplete onboarding state.
1 parent e77e9ae commit 440e92c

4 files changed

Lines changed: 30 additions & 7 deletions

File tree

apps/flipcash/shared/authentication/src/main/kotlin/com/flipcash/app/auth/AuthManager.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,17 @@ class AuthManager @Inject constructor(
172172
accountController.getUserFlags().getOrNull()
173173
}
174174

175+
val seenAccessKey = credentialManager.hasSeenAccessKey()
175176
if (flags != null) {
176177
userManager.set(flags)
177-
userManager.set(if (flags.isRegistered) AuthState.LoggedInWithUser else AuthState.Registered())
178+
if (flags.isRegistered && seenAccessKey) {
179+
userManager.set(AuthState.LoggedInWithUser)
180+
} else {
181+
userManager.set(AuthState.Registered(seenAccessKey))
182+
}
178183
} else {
179184
taggedTrace("Failed to get user flags after retries", type = TraceType.Error)
180-
userManager.set(authState = AuthState.Registered())
185+
userManager.set(authState = AuthState.Registered(seenAccessKey))
181186
}
182187

183188
profileController.updateUserProfile()

apps/flipcash/shared/authentication/src/main/kotlin/com/flipcash/app/auth/internal/credentials/PassphraseCredentialManager.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,27 @@ class PassphraseCredentialManager @Inject constructor(
108108

109109
suspend fun onUserAccessKeySeen(): Result<Unit> {
110110
storage.edit { prefs ->
111-
prefs[temporaryUserIdKey]?.let { userId ->
111+
val userId = prefs[temporaryUserIdKey] ?: prefs[selectedAccountIdKey]
112+
if (userId != null) {
112113
prefs[seenAccessKeyKey(userId)] = true
113114
}
114115
}
115116

116117
return Result.success(Unit)
117118
}
118119

120+
suspend fun hasSeenAccessKey(): Boolean {
121+
val tempUserId = storage.data.map { it[temporaryUserIdKey] }.firstOrNull()
122+
if (tempUserId != null) {
123+
return storage.data.map { it[seenAccessKeyKey(tempUserId)] }.firstOrNull() ?: false
124+
}
125+
// Temporary keys cleared by onAccountPurchased — check selected account.
126+
// Default to true so existing production users (who never had this flag) aren't affected.
127+
val selectedId = storage.data.map { it[selectedAccountIdKey] }.firstOrNull()
128+
?: return true
129+
return storage.data.map { it[seenAccessKeyKey(selectedId)] }.firstOrNull() ?: true
130+
}
131+
119132
suspend fun presentSaveOption(): Result<AccountMetadata> {
120133
val tempUserId = storage.data.map { it[temporaryUserIdKey] }.firstOrNull()
121134
val entropy = storage.data.map { it[temporaryEntropyKey] }.firstOrNull()
@@ -150,11 +163,13 @@ class PassphraseCredentialManager @Inject constructor(
150163
return Result.failure(Throwable("No user id found"))
151164
}
152165

153-
// remove temporary states
166+
// remove temporary states; persist seenAccessKey as false for the
167+
// selected account so a restart before the access-key screen resumes there
168+
val seenAccessKey = storage.data.map { it[seenAccessKeyKey(accountId.base58)] }.firstOrNull() ?: false
154169
storage.edit {
155170
it.remove(temporaryEntropyKey)
156171
it.remove(temporaryUserIdKey)
157-
it.remove(seenAccessKeyKey(accountId.base58))
172+
it[seenAccessKeyKey(accountId.base58)] = seenAccessKey
158173
}
159174

160175
// Store metadata
@@ -202,7 +217,7 @@ class PassphraseCredentialManager @Inject constructor(
202217
val selectedMetadata = getSelectedMetadata()
203218
if (selectedMetadata != null && selectedMetadata.entropy == entropy) {
204219
storeMetadata(selectedMetadata, isSelected = true)
205-
updateUserManager(selectedMetadata.id, AuthState.LoggedInWithUser)
220+
userManager.set(selectedMetadata.id)
206221
return Result.success(selectedMetadata)
207222
}
208223

apps/flipcash/shared/authentication/src/test/kotlin/com/flipcash/app/auth/AuthManagerTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class AuthManagerTest {
6868
coEvery { credentialManager.onAccountPurchased() } returns Result.success(mockk(relaxed = true))
6969
coEvery { pushController.addToken(any()) } returns Result.success(Unit)
7070
coEvery { pushController.deleteTokens() } returns Result.success(Unit)
71+
coEvery { credentialManager.hasSeenAccessKey() } returns true
7172

7273
authManager = AuthManager(
7374
credentialManager = credentialManager,

apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/RealSessionController.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,9 @@ class RealSessionController @Inject constructor(
302302
accountController.getUserFlags()
303303
.onSuccess { flags ->
304304
userManager.set(flags)
305-
if (flags.isRegistered && !userManager.authState.canAccessAuthenticatedApis) {
305+
val currentState = userManager.authState
306+
val onboardingIncomplete = currentState is AuthState.Registered && !currentState.seenAccessKey
307+
if (flags.isRegistered && !currentState.canAccessAuthenticatedApis && !onboardingIncomplete) {
306308
userManager.set(authState = AuthState.LoggedInWithUser)
307309
}
308310
}

0 commit comments

Comments
 (0)