Files
Kisani/Features/Login/LoginViewModel.swift
Robin Kutesa ae38f6e417 feat: Kisani rebrand, step-based onboarding flow, dark mode fixes
- Rename app to Kisani in all UI text and Face ID prompt
- Replace flat 2-state RootView with 5-step flow: ConnectView →
  LoginView → FolderSetupView → PhotoPermissionView → MainTabView
- Remove NavigationStack from LoginView (was causing nested-stack crash);
  drive navigation via ConnectionStore.isSessionActive
- Add FolderSetupView and PhotoPermissionView onboarding screens with
  step indicator (2/3, 3/3), spring entrance animations, and UGreen
  connection chip
- Fix dark mode button invisibility: PrimaryButtonStyle and PillButtonStyle
  now use AppTheme.inkInverse; GhostButtonStyle border uses ink.opacity(0.25)
- Add AppTheme.inkInverse adaptive token (dark bg ink on dark, white on light)
- Add ConnectionStore.isSessionActive (non-persisted) and
  onboardingPhotoShown (UserDefaults-persisted)
- Add os_log logging in LoginViewModel at auth start/success/failure
- Remove dead Help button from ConnectView
- UGreenCompatibilityBadge, KeychainStore, SavedConnections, extensions,
  PrivacyInfo.xcprivacy, unit test target and test stubs
- AppTheme: full UIColor dynamic provider tokens for dark/light adaptive color
- AppearanceMode enum + segmented picker in SettingsView
- BackupView: enlarged ring, removed options card and linear progress bar
- HistoryView: duration and bytes formatting, improved typography hierarchy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:20:53 +03:00

46 lines
1.7 KiB
Swift

import Foundation
import LocalAuthentication
import os.log
@MainActor
final class LoginViewModel: ObservableObject {
@Published var showMoreSheet = false
@Published var isAuthenticating = false
@Published var authError: String? = nil
var onAuthenticated: (() -> Void)?
private let logger = Logger(subsystem: "com.albert.nasbackup", category: "Login")
func authenticateWithFaceID() {
logger.info("Face ID authentication started")
isAuthenticating = true
authError = nil
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
let reason = error?.localizedDescription ?? "unknown"
logger.error("Face ID not available: \(reason, privacy: .public)")
authError = "Face ID not available"
isAuthenticating = false
return
}
context.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "Sign in to Kisani"
) { [weak self] success, evalError in
Task { @MainActor in
self?.isAuthenticating = false
if success {
self?.logger.info("Face ID authentication succeeded — advancing session")
self?.onAuthenticated?()
} else {
let reason = evalError?.localizedDescription ?? "Authentication failed"
self?.logger.error("Face ID authentication failed: \(reason, privacy: .public)")
self?.authError = reason
}
}
}
}
}