Files
Kisani/Features/Connect/ConnectViewModel.swift

108 lines
3.7 KiB
Swift
Raw Normal View History

import Foundation
2026-05-17 15:03:12 +03:00
import UIKit
enum ConnectField { case host, username, password }
2026-05-17 15:03:12 +03:00
enum ConnectionState: Equatable {
case idle
case authenticating
case authenticated
case destinationSelected
case failed(String)
var isAuthenticated: Bool {
switch self { case .authenticated, .destinationSelected: return true; default: return false }
}
var errorMessage: String? {
if case .failed(let msg) = self { return msg }
return nil
}
}
@MainActor
final class ConnectViewModel: ObservableObject {
2026-05-17 15:03:12 +03:00
@Published var host: String = ""
@Published var username: String = ""
@Published var password: String = ""
@Published var selectedProtocol: NASProtocol = .smb
@Published var selectedDestination: String = ""
@Published var connectionState: ConnectionState = .idle
@Published var showFolderBrowser = false
init() {
if let conn = ConnectionStore.shared.savedConnection {
2026-05-17 15:03:12 +03:00
host = conn.host
username = conn.username
password = conn.password
selectedProtocol = conn.nasProtocol
selectedDestination = conn.remotePath == "/" ? "" : conn.remotePath
connectionState = conn.remotePath == "/" ? .authenticated : .destinationSelected
}
}
2026-05-17 15:03:12 +03:00
var canConnect: Bool {
!host.trimmingCharacters(in: .whitespaces).isEmpty
&& !username.isEmpty
&& !password.isEmpty
&& connectionState != .authenticating
}
2026-05-17 15:03:12 +03:00
var canContinue: Bool {
connectionState == .destinationSelected && !selectedDestination.isEmpty
}
var statusMessage: String {
2026-05-17 15:03:12 +03:00
switch connectionState {
case .idle: return "Connect to your NAS to choose a destination"
case .authenticating: return "Connecting to NAS…"
case .authenticated: return "Authenticated — select a destination folder"
case .destinationSelected: return "Backup destination ready"
case .failed(let msg): return msg
}
}
2026-05-17 15:03:12 +03:00
func authenticate() async {
let trimmedHost = host.trimmingCharacters(in: .whitespaces)
connectionState = .authenticating
let service: any NASTransferProtocol = selectedProtocol == .smb ? SMBService() : SFTPService()
let connection = NASConnection(
2026-05-17 15:03:12 +03:00
host: trimmedHost,
nasProtocol: selectedProtocol,
username: username,
password: password,
2026-05-17 15:03:12 +03:00
remotePath: selectedDestination.isEmpty ? "/" : selectedDestination
)
do {
2026-05-17 15:03:12 +03:00
try await service.connect(
to: connection.host, port: connection.port,
username: connection.username, password: connection.password
)
2026-05-17 15:03:12 +03:00
service.disconnect()
ConnectionStore.shared.savedConnection = connection
2026-05-17 15:03:12 +03:00
connectionState = .authenticated
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
} catch let e as BackupError {
2026-05-17 15:03:12 +03:00
connectionState = .failed(e.errorDescription ?? e.localizedDescription)
} catch {
2026-05-17 15:03:12 +03:00
connectionState = .failed(error.localizedDescription)
}
2026-05-17 15:03:12 +03:00
}
func confirmDestination(_ path: String) {
guard !path.isEmpty, path != "/" else { return }
selectedDestination = path
connectionState = .destinationSelected
guard var conn = ConnectionStore.shared.savedConnection else { return }
conn.remotePath = path
ConnectionStore.shared.savedConnection = conn
UIImpactFeedbackGenerator(style: .light).impactOccurred()
}
func resetAuth() {
connectionState = .idle
selectedDestination = ""
}
}