diff --git a/app/src/main/java/com/kecalek/chat/core/SessionManager.kt b/app/src/main/java/com/kecalek/chat/core/SessionManager.kt index cf01470..560a9f2 100644 --- a/app/src/main/java/com/kecalek/chat/core/SessionManager.kt +++ b/app/src/main/java/com/kecalek/chat/core/SessionManager.kt @@ -64,9 +64,9 @@ class SessionManager @Inject constructor( // Re-authenticate automatically whenever the connection is (re)established. // During the initial login() call, lastEmail is null (cleared before connect), // so this handler is a no-op for the first connection. - connection.onConnected = { - val email = lastEmail ?: return@onConnected - val key = lastRsaPrivateKey ?: return@onConnected + connection.onConnected = reconnect@{ + val email = lastEmail ?: return@reconnect + val key = lastRsaPrivateKey ?: return@reconnect scope.launch { try { val session = performAuthHandshake(email, key, lastDeviceId, "Android") @@ -100,9 +100,13 @@ class SessionManager @Inject constructor( lastRsaPrivateKey = null try { - if (connection.state.value != ConnectionManager.State.CONNECTED) { - connection.connect(host, port, useTls) + // Always start with a fresh connection. + // This handles stale post-registration connections and ensures reconnect is armed. + if (connection.state.value == ConnectionManager.State.CONNECTED) { + connection.disconnect() } + connection.enableReconnect() + connection.connect(host, port, useTls) val session = performAuthHandshake(email, rsaPrivateKey, deviceId, deviceName) diff --git a/app/src/main/java/com/kecalek/chat/ui/auth/AuthViewModel.kt b/app/src/main/java/com/kecalek/chat/ui/auth/AuthViewModel.kt index 148deb0..0e75adb 100644 --- a/app/src/main/java/com/kecalek/chat/ui/auth/AuthViewModel.kt +++ b/app/src/main/java/com/kecalek/chat/ui/auth/AuthViewModel.kt @@ -211,32 +211,47 @@ class AuthViewModel @Inject constructor( viewModelScope.launch { _uiState.update { it.copy(isLoading = true, loadingMessage = "Potvrzuji kód…", error = null) } try { + // Step 1: Verify the email code — this is the only thing that should report + // "wrong code". Any error here is genuinely a bad/expired code. sessionManager.confirmRegistration(email, code) - // Auto-login immediately after confirmation using the already-decrypted - // key material from register(). This avoids re-asking for the password. + // Step 2: Try auto-login using the key material saved during register(). + // This is best-effort — failure here does NOT mean the code was wrong. + // On failure the user is sent back to LoginScreen to log in manually. val auth = pendingAuth + var loggedIn = false if (auth != null && auth.email == email) { - _uiState.update { it.copy(loadingMessage = "Přihlašuji se…") } - val state = _uiState.value - sessionManager.login( - email = email, - rsaPrivateKey = auth.rsaPrivate, - host = state.serverHost, - port = state.serverPort, - useTls = state.useTls, - ) - // Init local DB encryption key (no password needed — bytes already decrypted) - keyStorage.initLocalKey(auth.identityPrivateBytes) - pendingAuth = null // consumed — clear for security + try { + _uiState.update { it.copy(loadingMessage = "Přihlašuji se…") } + val state = _uiState.value + sessionManager.login( + email = email, + rsaPrivateKey = auth.rsaPrivate, + host = state.serverHost, + port = state.serverPort, + useTls = state.useTls, + ) + keyStorage.initLocalKey(auth.identityPrivateBytes) + loggedIn = true + } catch (loginEx: Exception) { + // Auto-login failed (e.g. stale connection, transient server error). + // Log it but don't surface to user as a "wrong code" error. + android.util.Log.w( + "AuthViewModel", + "Auto-login after confirm failed, user must log in manually: ${loginEx.message}" + ) + } finally { + pendingAuth = null + } } _uiState.update { it.copy( isLoading = false, loadingMessage = null, - isLoggedIn = true, + isLoggedIn = loggedIn, needsConfirmation = false, + hasExistingAccount = true, ) } } catch (e: AuthException) {