Files
honeyDueKMP/iosApp/iosApp/Login/LoginViewModel.swift
Trey t e24d1d8559 wip
2025-11-06 09:25:21 -06:00

200 lines
5.8 KiB
Swift

import Foundation
import ComposeApp
import Combine
@MainActor
class LoginViewModel: ObservableObject {
// MARK: - Published Properties
@Published var username: String = ""
@Published var password: String = ""
@Published var isLoading: Bool = false
@Published var errorMessage: String?
@Published var isAuthenticated: Bool = false
@Published var isVerified: Bool = false
@Published var currentUser: User?
// MARK: - Private Properties
private let authApi: AuthApi
private let tokenStorage: TokenStorage
// MARK: - Initialization
init() {
self.authApi = AuthApi(client: ApiClient_iosKt.createHttpClient())
self.tokenStorage = TokenStorage.shared
// Check if user is already logged in
checkAuthenticationStatus()
}
// MARK: - Public Methods
func login() {
guard !username.isEmpty else {
errorMessage = "Username is required"
return
}
guard !password.isEmpty else {
errorMessage = "Password is required"
return
}
isLoading = true
errorMessage = nil
let loginRequest = LoginRequest(username: username, password: password)
do {
// Call the KMM AuthApi login method
authApi.login(request: loginRequest) { result, error in
if let successResult = result as? ApiResultSuccess<AuthResponse> {
self.handleSuccess(results: successResult)
return
}
if let errorResult = result as? ApiResultError {
self.handleApiError(errorResult: errorResult)
return
}
if let error = error {
self.handleError(error: error)
return
}
self.isLoading = false
self.isAuthenticated = false
self.errorMessage = "Login failed. Please try again."
print("unknown error")
}
}
}
@MainActor
func handleError(error: any Error) {
self.isLoading = false
self.isAuthenticated = false
self.errorMessage = error.localizedDescription
print(error)
}
@MainActor
func handleApiError(errorResult: ApiResultError) {
self.isLoading = false
self.isAuthenticated = false
// Check for specific error codes
if errorResult.code?.intValue == 401 || errorResult.code?.intValue == 400 {
self.errorMessage = "Invalid username or password"
} else {
self.errorMessage = errorResult.message
}
print("API Error: \(errorResult.message)")
}
@MainActor
func handleSuccess(results: ApiResultSuccess<AuthResponse>) {
if let token = results.data?.token,
let user = results.data?.user {
self.tokenStorage.saveToken(token: token)
// Store user data and verification status
self.currentUser = user
self.isVerified = user.verified
self.isLoading = false
print("Login successful! Token: token")
print("User: \(user.username), Verified: \(user.verified)")
print("isVerified set to: \(self.isVerified)")
// Initialize lookups repository after successful login
LookupsManager.shared.initialize()
// Update authentication state AFTER setting verified status
// Small delay to ensure state updates are processed
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.isAuthenticated = true
print("isAuthenticated set to true, isVerified is: \(self.isVerified)")
}
}
}
func logout() {
let token = tokenStorage.getToken()
if let token = token {
// Call logout API
authApi.logout(token: token) { _, _ in
// Ignore result, clear token anyway
}
}
// Clear token from storage
tokenStorage.clearToken()
// Clear lookups data on logout
LookupsManager.shared.clear()
// Reset state
isAuthenticated = false
isVerified = false
currentUser = nil
username = ""
password = ""
errorMessage = nil
print("Logged out - all state reset")
}
func clearError() {
errorMessage = nil
}
// MARK: - Private Methods
private func checkAuthenticationStatus() {
guard let token = tokenStorage.getToken() else {
isAuthenticated = false
isVerified = false
return
}
// Fetch current user to check verification status
authApi.getCurrentUser(token: token) { result, error in
if let successResult = result as? ApiResultSuccess<User> {
self.handleAuthCheck(user: successResult.data!)
} else {
// Token invalid or expired, clear it
self.tokenStorage.clearToken()
self.isAuthenticated = false
self.isVerified = false
}
}
}
@MainActor
private func handleAuthCheck(user: User) {
self.currentUser = user
self.isVerified = user.verified
self.isAuthenticated = true
// Initialize lookups if verified
if user.verified {
LookupsManager.shared.initialize()
}
print("Auth check - User: \(user.username), Verified: \(user.verified)")
}
}
// MARK: - Error Types
enum LoginError: LocalizedError {
case unknownError
var errorDescription: String? {
switch self {
case .unknownError:
return "An unknown error occurred"
}
}
}