Files
Kecalek_python/ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift
2026-03-14 12:43:56 +01:00

193 lines
6.0 KiB
Swift

import Foundation
import SwiftUI
@Observable
final class AuthViewModel {
var email = ""
var password = ""
var confirmPassword = ""
var username = ""
var confirmationCode = ""
var isLoading = false
var errorMessage: String?
var showConfirmation = false
var registrationMessage: String?
var serverHost = Constants.defaultHost
var serverPort = String(Constants.defaultPort)
var hasSavedCredentials = false
var isBiometricLoading = false
enum AuthMode {
case login, register, pairing
}
var mode: AuthMode = .login
func checkSavedCredentials() {
hasSavedCredentials = KeychainService.hasSavedCredentials() && KeychainService.isBiometricAvailable()
}
func login(appState: AppState) async {
guard !email.isEmpty, !password.isEmpty else {
errorMessage = "Email and password are required"
return
}
isLoading = true
errorMessage = nil
// Only connect if not already connected
if await !appState.chatClient.isConnected {
do {
let port = UInt16(serverPort) ?? Constants.defaultPort
try await appState.chatClient.connect(host: serverHost, port: port)
} catch {
isLoading = false
errorMessage = "Connection failed: \(error.localizedDescription)"
return
}
}
let (success, message) = await appState.chatClient.login(email: email, password: password)
isLoading = false
if success {
appState.email = email
appState.isLoggedIn = true
appState.connectionStatus = .connected
appState.startConnectionMonitor()
if let userId = await appState.chatClient.userId {
appState.currentUser = User(id: userId, username: await appState.chatClient.username, email: email)
}
// Save credentials for biometric login next time
if KeychainService.isBiometricAvailable() {
let port = UInt16(serverPort) ?? Constants.defaultPort
try? KeychainService.saveCredentials(
email: email, password: password,
host: serverHost, port: port
)
}
// Clear password from memory after successful login
password = ""
confirmPassword = ""
} else {
errorMessage = message
}
}
func biometricLogin(appState: AppState) async {
isBiometricLoading = true
errorMessage = nil
do {
let creds = try KeychainService.loadCredentials()
email = creds.email
password = creds.password
serverHost = creds.host
serverPort = String(creds.port)
isBiometricLoading = false
await login(appState: appState)
// If login failed, reset to defaults so the form isn't stuck on stale values
if !appState.isLoggedIn {
serverHost = Constants.defaultHost
serverPort = String(Constants.defaultPort)
password = ""
KeychainService.deleteCredentials()
hasSavedCredentials = false
}
} catch KeychainService.KeychainError.biometricFailed {
isBiometricLoading = false
// User cancelled just let them type manually
} catch {
isBiometricLoading = false
errorMessage = error.localizedDescription
}
}
func register(appState: AppState) async {
guard !email.isEmpty, !password.isEmpty, !username.isEmpty else {
errorMessage = "All fields are required"
return
}
guard password == confirmPassword else {
errorMessage = "Passwords don't match"
return
}
isLoading = true
errorMessage = nil
#if DEBUG
print("DEBUG register: connecting to \(serverHost):\(serverPort)")
#endif
if await !appState.chatClient.isConnected {
do {
let port = UInt16(serverPort) ?? Constants.defaultPort
try await appState.chatClient.connect(host: serverHost, port: port)
#if DEBUG
print("DEBUG register: connected successfully")
#endif
} catch {
isLoading = false
#if DEBUG
print("DEBUG register: connection failed - \(error)")
#endif
errorMessage = "Connection failed: \(error.localizedDescription)"
return
}
}
#if DEBUG
print("DEBUG register: calling chatClient.register")
#endif
let (success, message) = await appState.chatClient.register(username: username, password: password, email: email)
isLoading = false
#if DEBUG
print("DEBUG AuthViewModel: register returned success=\(success), message=\(message)")
#endif
if success {
registrationMessage = message
showConfirmation = true
#if DEBUG
print("DEBUG AuthViewModel: showConfirmation set to true")
#endif
} else {
errorMessage = message
#if DEBUG
print("DEBUG AuthViewModel: errorMessage set to \(message)")
#endif
}
}
func confirmRegistration(appState: AppState) async {
guard !confirmationCode.isEmpty else {
errorMessage = "Enter the confirmation code"
return
}
isLoading = true
errorMessage = nil
let (success, message) = await appState.chatClient.confirmRegistration(
email: email, username: username, code: confirmationCode
)
isLoading = false
if success {
registrationMessage = message
// Auto-login after registration
await login(appState: appState)
} else {
errorMessage = message
}
}
}