fix: SMB share enumeration, BrowseView polish, sign out, Login branding

- Fix "Bad Network Name": BrowseView at root "/" for SMB now calls
  listShares() instead of listDirectory with empty share name.
  SMBClient.listShares() filters IPC/admin/hidden ($) shares.
- Add listShares() to NASTransferProtocol; implement in SMB (filters
  ipc/special/$-suffix) and SFTP (returns []).
- BrowseView: premium error state (wifi.exclamationmark icon + Retry
  button with ScaleButtonStyle), empty state, safeAreaInset bottom bar
  replacing toolbar bottomBar (shows current path chip + compact Select
  button), proper disabled state for root selection on SMB.
- FolderRow: replace hardcoded .white with AppTheme.inkInverse for dark
  mode correctness; use folder.fill icon; ScaleButtonStyle for tap feedback.
- LoginView: larger logo (80pt), bolder "Kisani" wordmark (32pt bold),
  tagline, staggered spring entrance animations (0.08-0.36s delay).
- SettingsView: Sign Out button in ACCOUNT section with
  confirmationDialog (destructive, clears connection + resets session).
- ConnectionStore: signOut() resets isSessionActive, onboardingPhotoShown,
  and clears savedConnection (triggers Keychain delete).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Robin Kutesa
2026-05-16 16:41:16 +03:00
parent ae38f6e417
commit 19d14e58fb
9 changed files with 333 additions and 141 deletions

View File

@@ -3,6 +3,7 @@ import SwiftUI
struct SettingsView: View {
@EnvironmentObject var store: ConnectionStore
@EnvironmentObject var lanMonitor: LANMonitor
@State private var showSignOutConfirm = false
var body: some View {
ZStack {
@@ -17,6 +18,7 @@ struct SettingsView: View {
connectivitySection
remoteAccessSection
notificationSection
signOutSection
}
.padding(.horizontal, AppTheme.hPad)
.padding(.top, 20)
@@ -25,6 +27,16 @@ struct SettingsView: View {
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
.confirmationDialog(
"Sign out of Kisani?",
isPresented: $showSignOutConfirm,
titleVisibility: .visible
) {
Button("Sign Out & Forget NAS", role: .destructive) { store.signOut() }
Button("Cancel", role: .cancel) {}
} message: {
Text("Your NAS connection and backup settings will be removed from this device.")
}
}
// MARK: Appearance
@@ -305,4 +317,38 @@ struct SettingsView: View {
.frame(height: 0.5)
.padding(.leading, AppTheme.cardPad)
}
// MARK: Sign Out
private var signOutSection: some View {
VStack(alignment: .leading, spacing: 8) {
sectionLabel("ACCOUNT")
Button(action: { showSignOutConfirm = true }) {
HStack(spacing: 12) {
Image(systemName: "rectangle.portrait.and.arrow.right")
.font(.system(size: 14, weight: .regular))
.foregroundStyle(AppTheme.destructive)
.frame(width: 20)
Text("Sign Out")
.font(AppTheme.body())
.foregroundStyle(AppTheme.destructive)
Spacer()
}
.padding(.horizontal, AppTheme.cardPad)
.padding(.vertical, 14)
.background(AppTheme.surfaceRaised)
.clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous))
.shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2)
}
.buttonStyle(ScaleButtonStyle())
}
}
private func sectionLabel(_ text: String) -> some View {
Text(text)
.font(AppTheme.micro())
.foregroundStyle(AppTheme.inkTertiary)
.padding(.leading, 4)
}
}