Files
Kisani/Core/Models/NASConnection.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

62 lines
2.0 KiB
Swift

import Foundation
enum NASProtocol: String, Codable, CaseIterable {
case smb = "SMB"
case sftp = "SFTP"
var defaultPort: Int {
switch self {
case .smb: return 445
case .sftp: return 22
}
}
var localizedDescription: String { rawValue }
}
struct NASConnection: Codable, Identifiable, Equatable {
var id: UUID = UUID()
var host: String
var port: Int
var nasProtocol: NASProtocol
var username: String
var password: String // Runtime only persisted in Keychain, not UserDefaults
var remotePath: String
var displayName: String
// Excludes password so it is never written to UserDefaults.
enum CodingKeys: String, CodingKey {
case id, host, port, nasProtocol, username, remotePath, displayName
}
init(
host: String,
port: Int? = nil,
nasProtocol: NASProtocol = .smb,
username: String,
password: String,
remotePath: String = "/",
displayName: String = ""
) {
self.host = host
self.port = port ?? nasProtocol.defaultPort
self.nasProtocol = nasProtocol
self.username = username
self.password = password
self.remotePath = remotePath
self.displayName = displayName.isEmpty ? host : displayName
}
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
id = try c.decode(UUID.self, forKey: .id)
host = try c.decode(String.self, forKey: .host)
port = try c.decode(Int.self, forKey: .port)
nasProtocol = try c.decode(NASProtocol.self, forKey: .nasProtocol)
username = try c.decode(String.self, forKey: .username)
remotePath = try c.decode(String.self, forKey: .remotePath)
displayName = try c.decode(String.self, forKey: .displayName)
password = "" // filled from Keychain by SavedConnections.load()
}
}