import SwiftUI /// Modal sheet for server-backed account login and registration. struct AuthModalView: View { @Environment(\.dismiss) private var dismiss @ObservedObject var authManager: AuthManager = .shared @State private var selectedTab: AuthTab = .login @State private var usernameInput: String = "" @State private var identifierInput: String = "" @State private var passwordInput: String = "" @State private var confirmPasswordInput: String = "" @State private var errorMessage: String? enum AuthTab { case login case register } var body: some View { ZStack { Color.spaceBlack.ignoresSafeArea() VStack(spacing: 20) { // Header HStack { Spacer() Button { dismiss() } label: { Image(systemName: "xmark.circle.fill") .font(.system(size: 22)) .foregroundStyle(Color.secondary.opacity(0.6)) } } .padding(.horizontal, 16) .padding(.top, 16) // Title VStack(spacing: 6) { Text(selectedTab == .login ? "账号登录" : "创建新账号") .font(.system(size: 22, weight: .bold)) .foregroundStyle(Color.primary) Text(selectedTab == .login ? "登录以同步录音并管理专属设备" : "注册后可将本机记录安全同步至云端") .font(.system(size: 12)) .foregroundStyle(Color.secondary) .multilineTextAlignment(.center) } .padding(.horizontal, 24) // Tab Picker HStack(spacing: 0) { tabButton(title: "登录", tab: .login) tabButton(title: "注册账号", tab: .register) } .background(Color.cardBackground.opacity(0.4)) .cornerRadius(8) .padding(.horizontal, 24) .padding(.top, 8) // Input Fields VStack(spacing: 14) { if selectedTab == .register { inputField( icon: "person", placeholder: "设置昵称 (如: 星痕探索者)", text: $usernameInput ) } inputField( icon: "envelope", placeholder: "手机号或电子邮箱", text: $identifierInput ) inputField( icon: "lock", placeholder: "密码 (至少 6 位)", text: $passwordInput, isSecure: true ) if selectedTab == .register { inputField( icon: "lock.shield", placeholder: "再次确认密码", text: $confirmPasswordInput, isSecure: true ) } if let error = errorMessage { Text(error) .font(.system(size: 12)) .foregroundStyle(Color.recordingRed) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 4) } } .padding(.horizontal, 24) .padding(.top, 12) Spacer() // Submit Button Button { handleAuthSubmit() } label: { if authManager.isProcessing { ProgressView() .tint(Color.spaceBlack) } else { Text(selectedTab == .login ? "安全登录" : "注册并自动登录") .font(.system(size: 15, weight: .semibold)) .foregroundStyle(Color.spaceBlack) } } .frame(maxWidth: .infinity) .padding(.vertical, 14) .background(Color.primary) .cornerRadius(8) .padding(.horizontal, 24) .padding(.bottom, 32) .disabled(authManager.isProcessing) } } } private func tabButton(title: String, tab: AuthTab) -> some View { Button { withAnimation(.snappy) { selectedTab = tab errorMessage = nil } } label: { Text(title) .font(.system(size: 14, weight: selectedTab == tab ? .semibold : .regular)) .foregroundStyle(selectedTab == tab ? Color.primary : Color.secondary) .frame(maxWidth: .infinity) .padding(.vertical, 10) .background( selectedTab == tab ? Color.cardBackground : Color.clear ) .cornerRadius(6) .padding(2) } } private func inputField(icon: String, placeholder: String, text: Binding, isSecure: Bool = false) -> some View { HStack(spacing: 12) { Image(systemName: icon) .font(.system(size: 14)) .foregroundStyle(Color.secondary) .frame(width: 20) if isSecure { SecureField(placeholder, text: text) .font(.system(size: 14)) } else { TextField(placeholder, text: text) .font(.system(size: 14)) .autocapitalization(.none) .disableAutocorrection(true) } } .padding(.horizontal, 14) .padding(.vertical, 12) .background(Color.cardBackground.opacity(0.3)) .businessBorder(cornerRadius: 8) } private func handleAuthSubmit() { errorMessage = nil if selectedTab == .login { guard !identifierInput.trimmingCharacters(in: .whitespaces).isEmpty else { errorMessage = "请输入手机号或邮箱" return } guard passwordInput.count >= 6 else { errorMessage = "密码不能少于 6 位" return } authManager.login(identifier: identifierInput, password: passwordInput) { success, err in if success { dismiss() } else { errorMessage = err ?? "登录失败" } } } else { let name = usernameInput.trimmingCharacters(in: .whitespaces) guard !name.isEmpty else { errorMessage = "请输入用户昵称" return } guard !identifierInput.trimmingCharacters(in: .whitespaces).isEmpty else { errorMessage = "请输入注册手机号或邮箱" return } guard passwordInput.count >= 6 else { errorMessage = "密码不能少于 6 位" return } guard passwordInput == confirmPasswordInput else { errorMessage = "两次输入的密码不一致" return } authManager.register(username: name, identifier: identifierInput, password: passwordInput) { success, err in if success { dismiss() } else { errorMessage = err ?? "注册失败" } } } } } #Preview { AuthModalView() }