import Foundation enum ConnectField { case host, username, password } enum ConnectStep: Equatable { case credentials // not yet authenticated case folder // authenticated, no destination chosen case ready // authenticated + valid destination chosen } @MainActor final class ConnectViewModel: ObservableObject { @Published var host: String @Published var username: String @Published var password: String @Published var remotePath: String @Published var selectedProtocol: NASProtocol @Published var isVerifying = false @Published var isConnected: Bool @Published var verifyError: String? = nil @Published var showFolderBrowser = false init() { if let conn = ConnectionStore.shared.savedConnection { host = conn.host username = conn.username password = conn.password remotePath = conn.remotePath selectedProtocol = conn.nasProtocol isConnected = true } else { host = "" username = "" password = "" remotePath = "/" selectedProtocol = .smb isConnected = false } } var step: ConnectStep { guard isConnected else { return .credentials } return remotePath == "/" ? .folder : .ready } var canProceed: Bool { switch step { case .credentials: return !host.trimmingCharacters(in: .whitespaces).isEmpty && !username.isEmpty && !password.isEmpty && !isVerifying case .folder, .ready: return true } } var statusMessage: String { switch step { case .credentials: return verifyError != nil ? verifyError! : "Enter your NAS credentials" case .folder: return "Authenticated ยท select a destination folder" case .ready: return "Ready to sync" } } var statusIsError: Bool { verifyError != nil && step == .credentials } var statusIsPositive: Bool { step == .folder || step == .ready } func proceed() async { switch step { case .credentials: await verify() case .folder: showFolderBrowser = true case .ready: break // navigation handled in the view } } func verify() async { let service: any NASTransferProtocol = selectedProtocol == .smb ? SMBService() : SFTPService() await verifyWith(transfer: service) } func verifyWith(transfer: any NASTransferProtocol) async { isVerifying = true isConnected = false verifyError = nil let connection = NASConnection( host: host.trimmingCharacters(in: .whitespaces), nasProtocol: selectedProtocol, username: username, password: password, remotePath: remotePath ) do { try await transfer.connect( to: connection.host, port: connection.port, username: connection.username, password: connection.password ) transfer.disconnect() isConnected = true ConnectionStore.shared.savedConnection = connection } catch let e as BackupError { verifyError = e.errorDescription } catch { verifyError = error.localizedDescription } isVerifying = false } }