ios_client
This commit is contained in:
192
ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift
Normal file
192
ios_client 0.8.5/Kecalek/ViewModels/AuthViewModel.swift
Normal file
@@ -0,0 +1,192 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user