Redesign Connect flow with step-based UX and matching logo
Replace the SF Symbol server.rack with the custom KisaniLogoMark extracted into a shared component used by both ConnectView and BackupView. Introduce ConnectStep enum (credentials → folder → ready) in ConnectViewModel. The primary CTA changes label and colour at each step: "Connect" → "Select Destination" → "Get Started". The folder row animates in only after successful authentication; its border goes green once a valid path is chosen. Status message updates live with the current step. Submitting the password field triggers verify(). Rename project.yml name to Kisani so xcodegen produces Kisani.xcodeproj. Co-Authored-By: Kutesir <kutesir@provoc.ug> Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import Foundation
|
||||
|
||||
enum ConnectField { case host, username, password, folder }
|
||||
enum FieldState { case idle, valid, invalid }
|
||||
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 {
|
||||
@@ -17,28 +22,62 @@ final class ConnectViewModel: ObservableObject {
|
||||
|
||||
init() {
|
||||
if let conn = ConnectionStore.shared.savedConnection {
|
||||
host = conn.host
|
||||
username = conn.username
|
||||
password = conn.password
|
||||
remotePath = conn.remotePath
|
||||
host = conn.host
|
||||
username = conn.username
|
||||
password = conn.password
|
||||
remotePath = conn.remotePath
|
||||
selectedProtocol = conn.nasProtocol
|
||||
isConnected = true
|
||||
isConnected = true
|
||||
} else {
|
||||
host = ""
|
||||
username = ""
|
||||
password = ""
|
||||
remotePath = "/"
|
||||
host = ""
|
||||
username = ""
|
||||
password = ""
|
||||
remotePath = "/"
|
||||
selectedProtocol = .smb
|
||||
isConnected = false
|
||||
isConnected = false
|
||||
}
|
||||
}
|
||||
|
||||
var fieldState: FieldState {
|
||||
isConnected ? .valid : (verifyError != nil ? .invalid : .idle)
|
||||
var step: ConnectStep {
|
||||
guard isConnected else { return .credentials }
|
||||
return remotePath == "/" ? .folder : .ready
|
||||
}
|
||||
|
||||
var canConnect: Bool {
|
||||
!host.isEmpty && !username.isEmpty && !password.isEmpty && !isVerifying
|
||||
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 {
|
||||
@@ -52,7 +91,7 @@ final class ConnectViewModel: ObservableObject {
|
||||
verifyError = nil
|
||||
|
||||
let connection = NASConnection(
|
||||
host: host,
|
||||
host: host.trimmingCharacters(in: .whitespaces),
|
||||
nasProtocol: selectedProtocol,
|
||||
username: username,
|
||||
password: password,
|
||||
@@ -60,8 +99,10 @@ final class ConnectViewModel: ObservableObject {
|
||||
)
|
||||
|
||||
do {
|
||||
try await transfer.connect(to: connection.host, port: connection.port,
|
||||
username: connection.username, password: connection.password)
|
||||
try await transfer.connect(
|
||||
to: connection.host, port: connection.port,
|
||||
username: connection.username, password: connection.password
|
||||
)
|
||||
transfer.disconnect()
|
||||
isConnected = true
|
||||
ConnectionStore.shared.savedConnection = connection
|
||||
|
||||
Reference in New Issue
Block a user