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>
This commit is contained in:
@@ -9,30 +9,33 @@ struct ConnectView: View {
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
AppTheme.surfaceSunken.ignoresSafeArea()
|
||||
AppTheme.background.ignoresSafeArea()
|
||||
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
|
||||
// Logo + title
|
||||
VStack(spacing: 10) {
|
||||
// Header
|
||||
VStack(spacing: 20) {
|
||||
// App icon
|
||||
Image("nas_logo_clean")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 44, height: 44)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 11, style: .continuous))
|
||||
.frame(width: 52, height: 52)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 13, style: .continuous))
|
||||
.shadow(color: .black.opacity(0.08), radius: 14, x: 0, y: 4)
|
||||
.padding(.top, 44)
|
||||
|
||||
Text("NAS Connect")
|
||||
.font(.system(size: 26, weight: .semibold))
|
||||
.foregroundStyle(AppTheme.ink)
|
||||
// Title
|
||||
VStack(spacing: 6) {
|
||||
Text("Kisani")
|
||||
.font(.system(size: 26, weight: .semibold))
|
||||
.foregroundStyle(AppTheme.ink)
|
||||
|
||||
Text("Connect to your UGreen or any SMB / SFTP NAS")
|
||||
.font(AppTheme.caption())
|
||||
.foregroundStyle(AppTheme.inkTertiary)
|
||||
.multilineTextAlignment(.center)
|
||||
// UGreen NAS compatibility badge
|
||||
UGreenCompatibilityBadge()
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 32)
|
||||
.padding(.bottom, 28)
|
||||
.padding(.horizontal, AppTheme.hPad)
|
||||
|
||||
// Protocol picker
|
||||
@@ -50,7 +53,7 @@ struct ConnectView: View {
|
||||
.padding(.vertical, 7)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 7, style: .continuous)
|
||||
.fill(vm.selectedProtocol == proto ? Color.white : Color.clear)
|
||||
.fill(vm.selectedProtocol == proto ? AppTheme.surfaceRaised : Color.clear)
|
||||
.shadow(
|
||||
color: vm.selectedProtocol == proto ? .black.opacity(0.06) : .clear,
|
||||
radius: 3, x: 0, y: 1
|
||||
@@ -61,7 +64,7 @@ struct ConnectView: View {
|
||||
}
|
||||
}
|
||||
.padding(3)
|
||||
.background(Color.black.opacity(0.06))
|
||||
.background(AppTheme.surfaceSunken)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
||||
.padding(.horizontal, AppTheme.hPad)
|
||||
.padding(.bottom, 20)
|
||||
@@ -90,19 +93,14 @@ struct ConnectView: View {
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 14)
|
||||
.background(Color.white)
|
||||
.background(AppTheme.surfaceRaised)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
||||
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
|
||||
|
||||
HStack(spacing: 20) {
|
||||
Text("IP Address, ex: 192.168.1.10")
|
||||
.font(AppTheme.micro(11))
|
||||
.foregroundStyle(AppTheme.inkTertiary)
|
||||
Text("NAS Name, ex: MY_NAS")
|
||||
.font(AppTheme.micro(11))
|
||||
.foregroundStyle(AppTheme.inkTertiary)
|
||||
}
|
||||
.padding(.leading, 4)
|
||||
Text("e.g. 192.168.1.10 or MY_NAS")
|
||||
.font(AppTheme.micro(11))
|
||||
.foregroundStyle(AppTheme.inkTertiary)
|
||||
.padding(.leading, 4)
|
||||
}
|
||||
.padding(.horizontal, AppTheme.hPad)
|
||||
.padding(.bottom, 12)
|
||||
@@ -163,7 +161,7 @@ struct ConnectView: View {
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 14)
|
||||
}
|
||||
.background(Color.white)
|
||||
.background(AppTheme.surfaceRaised)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous)
|
||||
@@ -199,7 +197,7 @@ struct ConnectView: View {
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 14)
|
||||
.background(Color.white)
|
||||
.background(AppTheme.surfaceRaised)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
|
||||
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
|
||||
}
|
||||
@@ -255,25 +253,11 @@ struct ConnectView: View {
|
||||
.disabled(!vm.canConnect)
|
||||
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: vm.isConnected)
|
||||
|
||||
// Help
|
||||
Button {
|
||||
// future: show help sheet
|
||||
} label: {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "questionmark.circle")
|
||||
.font(.system(size: 13))
|
||||
Text("Help")
|
||||
.font(AppTheme.caption())
|
||||
}
|
||||
.foregroundStyle(AppTheme.inkTertiary)
|
||||
}
|
||||
.padding(.top, 20)
|
||||
.padding(.bottom, 48)
|
||||
Spacer().frame(height: 48)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("NAS Connect")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarHidden(true)
|
||||
.navigationDestination(isPresented: $navigateToDashboard) { MainTabView() }
|
||||
.sheet(isPresented: $vm.showFolderBrowser) {
|
||||
if let conn = ConnectionStore.shared.savedConnection {
|
||||
|
||||
Reference in New Issue
Block a user