| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- import SwiftUI
- /// Account details opened from Settings.
- struct ProfileView: View {
- @ObservedObject var authManager: AuthManager = .shared
-
- @State private var showAuthModal = false
- @State private var showLogoutConfirmation = false
- @State private var usernameInput = ""
- @State private var emailInput = ""
- @State private var phoneInput = ""
- @State private var profileMessage: String?
- @State private var profileMessageIsError = false
-
- var body: some View {
- ZStack {
- Color.spaceBlack.ignoresSafeArea()
-
- ScrollView {
- VStack(spacing: 20) {
- // 1. Account Profile Header
- accountHeaderCard
- .padding(.top, 12)
-
- // 2. Inline profile editing (if logged in)
- if authManager.isLoggedIn {
- profileEditorCard
- }
- // Footer
- footer
- .padding(.top, 12)
- .padding(.bottom, 40)
- }
- .padding(.horizontal, 16)
- }
- }
- .navigationTitle("账号")
- .navigationBarTitleDisplayMode(.inline)
- .sheet(isPresented: $showAuthModal) {
- AuthModalView(authManager: authManager)
- }
- .onAppear(perform: loadProfileInputs)
- .confirmationDialog("确定要退出登录吗?", isPresented: $showLogoutConfirmation, titleVisibility: .visible) {
- Button("退出登录", role: .destructive) {
- authManager.logout()
- }
- Button("取消", role: .cancel) {}
- }
- }
-
- // MARK: - 1. Account Header
-
- private var accountHeaderCard: some View {
- VStack(spacing: 16) {
- if let user = authManager.currentUser {
- // Authenticated Profile State
- HStack(spacing: 16) {
- ZStack {
- Circle()
- .fill(Color.primary.opacity(0.12))
- .frame(width: 60, height: 60)
-
- Image(systemName: "person.crop.circle.fill")
- .font(.system(size: 34))
- .foregroundStyle(Color.primary)
- }
-
- VStack(alignment: .leading, spacing: 6) {
- Text(user.username)
- .font(.system(size: 18, weight: .bold))
- .foregroundStyle(Color.primary)
-
- Text(user.email ?? user.phoneNumber ?? ("用户 ID: " + String(user.id.prefix(12))))
- .font(.system(size: 12))
- .foregroundStyle(Color.secondary)
- }
-
- Spacer()
-
- Button {
- showLogoutConfirmation = true
- } label: {
- Text("退出")
- .font(.system(size: 12, weight: .regular))
- .foregroundStyle(Color.recordingRed)
- .padding(.horizontal, 10)
- .padding(.vertical, 5)
- .overlay(
- RoundedRectangle(cornerRadius: 6)
- .stroke(Color.recordingRed.opacity(0.4), lineWidth: 1)
- )
- }
- }
- } else {
- // Guest / Unauthenticated State
- VStack(spacing: 14) {
- HStack(spacing: 14) {
- ZStack {
- Circle()
- .fill(Color.primary.opacity(0.06))
- .frame(width: 52, height: 52)
-
- Image(systemName: "person.crop.circle.badge.plus")
- .font(.system(size: 24))
- .foregroundStyle(Color.secondary)
- }
-
- VStack(alignment: .leading, spacing: 4) {
- Text("未登录账号")
- .font(.system(size: 16, weight: .semibold))
- .foregroundStyle(Color.primary)
-
- Text("登录/注册后支持全功能体验与 BLE 硬件绑定")
- .font(.system(size: 12))
- .foregroundStyle(Color.secondary)
- }
-
- Spacer()
- }
-
- Button {
- showAuthModal = true
- } label: {
- Text("注册 / 登录账号")
- .font(.system(size: 14, weight: .semibold))
- .foregroundStyle(Color.spaceBlack)
- .frame(maxWidth: .infinity)
- .padding(.vertical, 11)
- .background(Color.primary)
- .cornerRadius(8)
- }
- }
- }
- }
- .padding(16)
- .background(Color.cardBackground.opacity(0.25))
- .businessBorder(cornerRadius: 12)
- }
-
- // MARK: - 2. Inline Profile Editing
-
- private var profileEditorCard: some View {
- VStack(alignment: .leading, spacing: 14) {
- Text("基本资料")
- .font(.system(size: 15, weight: .semibold))
- .foregroundStyle(Color.primary)
- profileField(label: "账号名", text: $usernameInput, icon: "person")
- profileField(label: "电子邮箱", text: $emailInput, icon: "envelope", keyboard: .emailAddress)
- profileField(label: "手机号码", text: $phoneInput, icon: "phone", keyboard: .phonePad)
- if let profileMessage {
- Text(profileMessage)
- .font(.system(size: 12))
- .foregroundStyle(profileMessageIsError ? Color.recordingRed : Color.green)
- }
- Button {
- saveProfile()
- } label: {
- Group {
- if authManager.isProcessing {
- ProgressView().tint(Color.spaceBlack)
- } else {
- Text("保存基本资料")
- .font(.system(size: 13, weight: .medium))
- }
- }
- .foregroundStyle(Color.spaceBlack)
- .frame(maxWidth: .infinity)
- .padding(.vertical, 10)
- .background(Color.primary)
- .cornerRadius(8)
- }
- .disabled(authManager.isProcessing)
- NavigationLink {
- ChangePasswordModalView(authManager: authManager)
- } label: {
- HStack {
- Image(systemName: "key")
- Text("修改安全密码")
- Spacer()
- Image(systemName: "chevron.right")
- .font(.system(size: 11, weight: .semibold))
- .foregroundStyle(Color.secondary)
- }
- .font(.system(size: 13, weight: .medium))
- .foregroundStyle(Color.primary)
- .padding(.vertical, 10)
- }
- }
- .padding(16)
- .background(Color.cardBackground.opacity(0.25))
- .businessBorder(cornerRadius: 12)
- }
-
- // MARK: - Actions
- private func profileField(
- label: String,
- text: Binding<String>,
- icon: String,
- keyboard: UIKeyboardType = .default
- ) -> some View {
- VStack(alignment: .leading, spacing: 6) {
- Text(label)
- .font(.system(size: 12))
- .foregroundStyle(Color.secondary)
- HStack(spacing: 10) {
- Image(systemName: icon)
- .foregroundStyle(Color.secondary)
- .frame(width: 18)
- TextField(label, text: text)
- .textInputAutocapitalization(.never)
- .autocorrectionDisabled()
- .keyboardType(keyboard)
- }
- .font(.system(size: 14))
- .padding(.horizontal, 12)
- .padding(.vertical, 11)
- .background(Color.cardBackground.opacity(0.3))
- .businessBorder(cornerRadius: 8)
- }
- }
- private func loadProfileInputs() {
- guard let user = authManager.currentUser else { return }
- usernameInput = user.username
- emailInput = user.email ?? ""
- phoneInput = user.phoneNumber ?? ""
- }
- private func saveProfile() {
- profileMessage = nil
- authManager.updateProfile(
- username: usernameInput,
- email: emailInput,
- phoneNumber: phoneInput,
- avatarURL: nil
- ) { success, error in
- profileMessageIsError = !success
- profileMessage = success ? "基本资料已保存" : (error ?? "保存失败")
- if success {
- loadProfileInputs()
- }
- }
- }
-
- // MARK: - Footer
-
- private var footer: some View {
- VStack(spacing: 4) {
- Text("CELESTIA TRACE USER & DEVICE")
- .font(.system(size: 10, weight: .bold, design: .monospaced))
- .foregroundStyle(Color.secondary.opacity(0.4))
- .tracking(2)
-
- Text("安全本地注册与极简日志空间")
- .font(.system(size: 9))
- .foregroundStyle(Color.secondary.opacity(0.3))
- }
- }
- }
- #Preview {
- NavigationStack {
- ProfileView()
- }
- }
|