Files
timetracker/ios/TimeTracker/TimeTracker/Features/Auth/LoginView.swift
2026-02-18 22:37:49 +01:00

90 lines
2.8 KiB
Swift

import SwiftUI
import AuthenticationServices
struct LoginView: View {
@EnvironmentObject var authManager: AuthManager
@State private var isLoading = false
@State private var errorMessage: String?
var body: some View {
VStack(spacing: 24) {
Spacer()
Image(systemName: "timer")
.font(.system(size: 64))
.foregroundStyle(Color.accentColor)
Text("TimeTracker")
.font(.largeTitle)
.fontWeight(.bold)
Text("Track your time spent on projects")
.font(.subheadline)
.foregroundStyle(.secondary)
Spacer()
if let error = errorMessage {
Text(error)
.font(.subheadline)
.foregroundStyle(.red)
.padding(.horizontal)
}
Button {
login()
} label: {
if isLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.frame(maxWidth: .infinity)
} else {
Text("Sign In")
.frame(maxWidth: .infinity)
}
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.disabled(isLoading)
.padding(.horizontal, 40)
Spacer()
.frame(height: 40)
}
.padding()
.onReceive(NotificationCenter.default.publisher(for: .authCallbackReceived)) { notification in
handleAuthCallback(notification.userInfo)
}
.onReceive(NotificationCenter.default.publisher(for: .authError)) { notification in
if let authError = notification.userInfo?["error"] as? AuthError {
isLoading = false
errorMessage = authError.errorDescription
}
}
}
private func login() {
isLoading = true
errorMessage = nil
Task {
let authService = AuthService.shared
do {
try await authService.login(presentationAnchor: nil)
} catch {
await MainActor.run {
isLoading = false
errorMessage = error.localizedDescription
}
}
}
}
private func handleAuthCallback(_ userInfo: [AnyHashable: Any]?) {
// AuthManager.handleTokenResponse() already set isAuthenticated = true
// and populated currentUser during the token exchange in AuthService.
// No further network call is needed here.
isLoading = false
}
}