Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
|
|
struct ConnectView: View {
|
|
|
|
|
@StateObject private var vm = ConnectViewModel()
|
|
|
|
|
@State private var navigateToDashboard = false
|
2026-05-16 11:11:52 +03:00
|
|
|
@State private var showPassword = false
|
2026-05-16 00:43:31 +03:00
|
|
|
@FocusState private var focused: ConnectField?
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
|
NavigationStack {
|
|
|
|
|
ZStack {
|
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
|
|
|
AppTheme.background.ignoresSafeArea()
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
|
|
|
|
ScrollView {
|
|
|
|
|
VStack(spacing: 0) {
|
2026-05-16 11:11:52 +03:00
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Header — identical treatment to BackupView ──
|
|
|
|
|
VStack(spacing: 10) {
|
2026-05-16 18:59:36 +03:00
|
|
|
Text("kisani.")
|
2026-05-17 13:58:03 +03:00
|
|
|
.font(.system(size: 11, weight: .medium, design: .monospaced))
|
|
|
|
|
.foregroundStyle(AppTheme.ink)
|
|
|
|
|
.kerning(2)
|
|
|
|
|
.padding(.top, 52)
|
2026-05-16 11:11:52 +03:00
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
KisaniLogoMark(size: 66)
|
2026-05-16 11:11:52 +03:00
|
|
|
|
2026-05-16 19:19:37 +03:00
|
|
|
UGreenCompatibilityBadge(selectedProtocol: vm.selectedProtocol)
|
2026-05-17 13:58:03 +03:00
|
|
|
.padding(.top, 4)
|
2026-05-16 11:11:52 +03:00
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
.frame(maxWidth: .infinity)
|
|
|
|
|
.padding(.bottom, 28)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Protocol picker ──
|
2026-05-16 00:43:31 +03:00
|
|
|
HStack(spacing: 0) {
|
|
|
|
|
ForEach(NASProtocol.allCases, id: \.self) { proto in
|
|
|
|
|
Button {
|
|
|
|
|
withAnimation(.spring(response: 0.25, dampingFraction: 0.8)) {
|
|
|
|
|
vm.selectedProtocol = proto
|
2026-05-17 13:58:03 +03:00
|
|
|
vm.isConnected = false
|
|
|
|
|
vm.verifyError = nil
|
2026-05-16 00:43:31 +03:00
|
|
|
}
|
|
|
|
|
} label: {
|
|
|
|
|
Text(proto.rawValue)
|
2026-05-16 11:11:52 +03:00
|
|
|
.font(.system(size: 12, weight: .medium))
|
2026-05-16 00:43:31 +03:00
|
|
|
.foregroundStyle(vm.selectedProtocol == proto ? AppTheme.ink : AppTheme.inkTertiary)
|
|
|
|
|
.frame(maxWidth: .infinity)
|
2026-05-16 11:11:52 +03:00
|
|
|
.padding(.vertical, 7)
|
2026-05-16 00:43:31 +03:00
|
|
|
.background(
|
2026-05-16 11:11:52 +03:00
|
|
|
RoundedRectangle(cornerRadius: 7, style: .continuous)
|
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
|
|
|
.fill(vm.selectedProtocol == proto ? AppTheme.surfaceRaised : Color.clear)
|
2026-05-16 00:43:31 +03:00
|
|
|
.shadow(
|
2026-05-16 11:11:52 +03:00
|
|
|
color: vm.selectedProtocol == proto ? .black.opacity(0.06) : .clear,
|
|
|
|
|
radius: 3, x: 0, y: 1
|
2026-05-16 00:43:31 +03:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
.buttonStyle(PlainButtonStyle())
|
|
|
|
|
}
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
2026-05-16 11:11:52 +03:00
|
|
|
.padding(3)
|
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
|
|
|
.background(AppTheme.surfaceSunken)
|
2026-05-16 11:11:52 +03:00
|
|
|
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
2026-05-16 00:43:31 +03:00
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
2026-05-16 11:11:52 +03:00
|
|
|
.padding(.bottom, 20)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Step 1: Host ──
|
2026-05-16 11:11:52 +03:00
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
|
|
|
HStack(spacing: 12) {
|
|
|
|
|
Image(systemName: "network")
|
|
|
|
|
.font(.system(size: 14, weight: .regular))
|
|
|
|
|
.foregroundStyle(AppTheme.inkSecondary)
|
|
|
|
|
.frame(width: 20)
|
|
|
|
|
TextField("IP address or hostname", text: $vm.host)
|
|
|
|
|
.font(AppTheme.body())
|
|
|
|
|
.foregroundStyle(AppTheme.ink)
|
|
|
|
|
.keyboardType(.URL)
|
|
|
|
|
.autocorrectionDisabled()
|
|
|
|
|
.textInputAutocapitalization(.never)
|
|
|
|
|
.focused($focused, equals: .host)
|
2026-05-17 13:58:03 +03:00
|
|
|
.submitLabel(.next)
|
|
|
|
|
.onSubmit { focused = .username }
|
2026-05-16 11:11:52 +03:00
|
|
|
if !vm.host.isEmpty {
|
|
|
|
|
Button { vm.host = "" } label: {
|
|
|
|
|
Image(systemName: "xmark.circle.fill")
|
|
|
|
|
.foregroundStyle(AppTheme.inkQuaternary)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 16)
|
|
|
|
|
.padding(.vertical, 14)
|
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
|
|
|
.background(AppTheme.surfaceRaised)
|
2026-05-16 11:11:52 +03:00
|
|
|
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
|
|
|
|
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
|
|
|
|
|
|
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
|
|
|
Text("e.g. 192.168.1.10 or MY_NAS")
|
|
|
|
|
.font(AppTheme.micro(11))
|
|
|
|
|
.foregroundStyle(AppTheme.inkTertiary)
|
|
|
|
|
.padding(.leading, 4)
|
2026-05-16 11:11:52 +03:00
|
|
|
}
|
|
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
|
|
|
|
.padding(.bottom, 12)
|
|
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Step 1: Credentials card ──
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
VStack(spacing: 0) {
|
2026-05-16 11:11:52 +03:00
|
|
|
HStack(spacing: 12) {
|
|
|
|
|
Image(systemName: "person")
|
|
|
|
|
.font(.system(size: 14, weight: .regular))
|
|
|
|
|
.foregroundStyle(AppTheme.inkSecondary)
|
|
|
|
|
.frame(width: 20)
|
|
|
|
|
TextField("Username", text: $vm.username)
|
|
|
|
|
.font(AppTheme.body())
|
|
|
|
|
.foregroundStyle(AppTheme.ink)
|
|
|
|
|
.autocorrectionDisabled()
|
|
|
|
|
.textInputAutocapitalization(.never)
|
|
|
|
|
.focused($focused, equals: .username)
|
2026-05-17 13:58:03 +03:00
|
|
|
.submitLabel(.next)
|
|
|
|
|
.onSubmit { focused = .password }
|
2026-05-16 11:11:52 +03:00
|
|
|
if !vm.username.isEmpty {
|
|
|
|
|
Button { vm.username = "" } label: {
|
|
|
|
|
Image(systemName: "xmark.circle.fill")
|
|
|
|
|
.foregroundStyle(AppTheme.inkQuaternary)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 16)
|
|
|
|
|
.padding(.vertical, 14)
|
|
|
|
|
|
|
|
|
|
Rectangle()
|
|
|
|
|
.fill(AppTheme.separator)
|
|
|
|
|
.frame(height: 0.5)
|
|
|
|
|
.padding(.leading, 44)
|
|
|
|
|
|
|
|
|
|
HStack(spacing: 12) {
|
|
|
|
|
Image(systemName: "lock")
|
|
|
|
|
.font(.system(size: 14, weight: .regular))
|
|
|
|
|
.foregroundStyle(AppTheme.inkSecondary)
|
|
|
|
|
.frame(width: 20)
|
|
|
|
|
Group {
|
|
|
|
|
if showPassword {
|
|
|
|
|
TextField("Password", text: $vm.password)
|
|
|
|
|
} else {
|
|
|
|
|
SecureField("Password", text: $vm.password)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.font(AppTheme.body())
|
|
|
|
|
.foregroundStyle(AppTheme.ink)
|
|
|
|
|
.focused($focused, equals: .password)
|
2026-05-17 13:58:03 +03:00
|
|
|
.submitLabel(.go)
|
|
|
|
|
.onSubmit { Task { await vm.verify() } }
|
2026-05-16 11:11:52 +03:00
|
|
|
Button {
|
|
|
|
|
showPassword.toggle()
|
|
|
|
|
} label: {
|
|
|
|
|
Image(systemName: showPassword ? "eye.slash" : "eye")
|
|
|
|
|
.font(.system(size: 14))
|
|
|
|
|
.foregroundStyle(AppTheme.inkTertiary)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 16)
|
|
|
|
|
.padding(.vertical, 14)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
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
|
|
|
.background(AppTheme.surfaceRaised)
|
2026-05-16 00:43:31 +03:00
|
|
|
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
.overlay(
|
2026-05-16 00:43:31 +03:00
|
|
|
RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous)
|
|
|
|
|
.stroke(
|
2026-05-17 13:58:03 +03:00
|
|
|
vm.isConnected ? AppTheme.positive.opacity(0.45) :
|
|
|
|
|
vm.verifyError != nil ? AppTheme.destructive.opacity(0.45) :
|
2026-05-16 11:11:52 +03:00
|
|
|
Color.clear,
|
|
|
|
|
lineWidth: 1
|
2026-05-16 00:43:31 +03:00
|
|
|
)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
)
|
2026-05-16 00:43:31 +03:00
|
|
|
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
|
|
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
2026-05-16 11:11:52 +03:00
|
|
|
.padding(.bottom, 12)
|
2026-05-16 00:43:31 +03:00
|
|
|
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected)
|
|
|
|
|
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.verifyError != nil)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Step 2: Destination folder — slides in after auth ──
|
|
|
|
|
if vm.step != .credentials {
|
|
|
|
|
Button(action: { vm.showFolderBrowser = true }) {
|
|
|
|
|
HStack(spacing: 12) {
|
|
|
|
|
Image(systemName: vm.remotePath == "/" ? "folder" : "folder.fill")
|
|
|
|
|
.font(.system(size: 14, weight: .regular))
|
|
|
|
|
.foregroundStyle(vm.remotePath == "/" ? AppTheme.inkSecondary : AppTheme.positive)
|
|
|
|
|
.frame(width: 20)
|
|
|
|
|
Text(vm.remotePath == "/" ? "Select destination folder" : vm.remotePath)
|
|
|
|
|
.font(AppTheme.body())
|
|
|
|
|
.foregroundStyle(vm.remotePath == "/" ? AppTheme.inkTertiary : AppTheme.ink)
|
|
|
|
|
.lineLimit(2)
|
|
|
|
|
.multilineTextAlignment(.leading)
|
|
|
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
|
|
|
Image(systemName: "chevron.right")
|
|
|
|
|
.font(.system(size: 12, weight: .medium))
|
|
|
|
|
.foregroundStyle(AppTheme.inkQuaternary)
|
|
|
|
|
}
|
|
|
|
|
.padding(.horizontal, 16)
|
|
|
|
|
.padding(.vertical, 14)
|
|
|
|
|
.background(AppTheme.surfaceRaised)
|
|
|
|
|
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
|
|
|
|
.overlay(
|
|
|
|
|
RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous)
|
|
|
|
|
.stroke(
|
|
|
|
|
vm.step == .ready ? AppTheme.positive.opacity(0.45) : Color.clear,
|
|
|
|
|
lineWidth: 1
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
|
2026-05-16 11:11:52 +03:00
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
.buttonStyle(ScaleButtonStyle())
|
|
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
|
|
|
|
.padding(.bottom, 12)
|
|
|
|
|
.transition(.opacity.combined(with: .move(edge: .top)))
|
|
|
|
|
.animation(.spring(response: 0.35, dampingFraction: 0.8), value: vm.step)
|
2026-05-16 11:11:52 +03:00
|
|
|
}
|
|
|
|
|
|
2026-05-17 13:58:03 +03:00
|
|
|
// ── Status message ──
|
|
|
|
|
HStack(spacing: 6) {
|
|
|
|
|
Image(systemName: vm.statusIsError ? "exclamationmark.circle" :
|
|
|
|
|
vm.statusIsPositive ? "checkmark.circle" : "circle.dotted")
|
|
|
|
|
.font(.system(size: 13))
|
|
|
|
|
Text(vm.statusMessage)
|
|
|
|
|
.font(AppTheme.caption())
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
.foregroundStyle(
|
|
|
|
|
vm.statusIsError ? AppTheme.destructive :
|
|
|
|
|
vm.statusIsPositive ? AppTheme.positive :
|
|
|
|
|
AppTheme.inkTertiary
|
|
|
|
|
)
|
|
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
|
|
|
|
.padding(.bottom, 20)
|
|
|
|
|
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.step)
|
2026-05-16 00:43:31 +03:00
|
|
|
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.verifyError)
|
2026-05-16 19:19:37 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.safeAreaInset(edge: .bottom) {
|
|
|
|
|
VStack(spacing: 0) {
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
Button(action: {
|
2026-05-17 13:58:03 +03:00
|
|
|
focused = nil
|
|
|
|
|
if vm.step == .ready {
|
|
|
|
|
navigateToDashboard = true
|
|
|
|
|
} else {
|
|
|
|
|
Task { await vm.proceed() }
|
|
|
|
|
}
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}) {
|
|
|
|
|
Group {
|
|
|
|
|
if vm.isVerifying {
|
|
|
|
|
ProgressView().tint(.white)
|
|
|
|
|
} else {
|
2026-05-17 13:58:03 +03:00
|
|
|
Text(ctaLabel)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
.frame(maxWidth: .infinity)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
.buttonStyle(PrimaryButtonStyle(color: ctaColor))
|
2026-05-16 00:43:31 +03:00
|
|
|
.padding(.horizontal, AppTheme.hPad)
|
2026-05-16 19:19:37 +03:00
|
|
|
.padding(.top, 12)
|
|
|
|
|
.padding(.bottom, 32)
|
2026-05-17 13:58:03 +03:00
|
|
|
.disabled(!vm.canProceed)
|
|
|
|
|
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.step)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
2026-05-16 19:19:37 +03:00
|
|
|
.background(AppTheme.background)
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
.navigationBarHidden(true)
|
2026-05-16 00:43:31 +03:00
|
|
|
.navigationDestination(isPresented: $navigateToDashboard) { MainTabView() }
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
.sheet(isPresented: $vm.showFolderBrowser) {
|
|
|
|
|
if let conn = ConnectionStore.shared.savedConnection {
|
feat: Kisani UI overhaul — brand identity, gallery, visual polish
- Rename app to Kisani (CFBundleDisplayName, xcodeproj → Kisani.xcodeproj)
- BackupView: kisani. wordmark + SwiftUI-drawn server logo mark (no asset,
adaptive ink/bg), 206pt progress ring with active-state glow, 4-page
swipeable ring (progress/pending/failed/uploaded), 4-metric stats strip,
friendly NAS card (Home Server label), hamburger → AppMenuView sheet
- GalleryView: new NAS + local photo grid with source picker chip, 3-col
lazy grid, ThumbnailCache, sync-status dots (green = backed up)
- MainTabView: Browse tab → Gallery tab; tab bar uses ultraThinMaterial
blur background (systemGray3 icons, 9.5pt labels)
- All app icon sizes regenerated from 1024pt source via sips
- LANMonitor: nasReachable Bool?, checkNASReachability(host:port:) TCP ping
- project.yml: photo library usage description updated to Kisani branding
Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
2026-05-17 11:17:13 +03:00
|
|
|
BrowseView(connection: conn, selectedPath: $vm.remotePath, isPickerMode: true)
|
2026-05-17 13:58:03 +03:00
|
|
|
.onDisappear {
|
|
|
|
|
if vm.remotePath != "/", var c = ConnectionStore.shared.savedConnection {
|
|
|
|
|
c.remotePath = vm.remotePath
|
|
|
|
|
ConnectionStore.shared.savedConnection = c
|
|
|
|
|
}
|
|
|
|
|
}
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-17 13:58:03 +03:00
|
|
|
|
|
|
|
|
private var ctaLabel: String {
|
|
|
|
|
switch vm.step {
|
|
|
|
|
case .credentials: return "Connect"
|
|
|
|
|
case .folder: return "Select Destination"
|
|
|
|
|
case .ready: return "Get Started"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var ctaColor: Color {
|
|
|
|
|
switch vm.step {
|
|
|
|
|
case .credentials: return AppTheme.ink
|
|
|
|
|
case .folder: return AppTheme.ink
|
|
|
|
|
case .ready: return AppTheme.positive
|
|
|
|
|
}
|
|
|
|
|
}
|
Initial scaffold — NASBackup iOS app
Full project scaffold: XcodeGen project.yml, Core models/protocols/errors,
SMBService (kishikawakatsumi/SMBClient), SFTPService (Citadel 0.9.2),
PhotoLibraryService, LANMonitor, BackupEngine, BackgroundTaskManager,
all feature UIs (Login, Connect, Browse, Backup, History, Settings),
Shared components and theme. Builds clean with zero errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 17:05:04 +03:00
|
|
|
}
|