// // AppSettingView.swift // yana // // Created by Edwin on 2024/11/20. // import SwiftUI import ComposableArchitecture import PhotosUI struct AppSettingView: View { let store: StoreOf var body: some View { WithPerceptionTracking { mainView() } .onAppear { store.send(.onAppear) } // 登出确认弹窗 .alert(LocalizedString("appSetting.logoutConfirmation.title", comment: "确认退出"), isPresented: Binding( get: { store.showLogoutConfirmation }, set: { store.send(.showLogoutConfirmation($0)) } )) { Button(LocalizedString("common.cancel", comment: "取消"), role: .cancel) { store.send(.showLogoutConfirmation(false)) } Button(LocalizedString("appSetting.logoutConfirmation.confirm", comment: "确认退出"), role: .destructive) { store.send(.logoutConfirmed) store.send(.showLogoutConfirmation(false)) } } message: { Text(LocalizedString("appSetting.logoutConfirmation.message", comment: "确定要退出当前账户吗?")) } // 关于我们弹窗 .alert(LocalizedString("appSetting.aboutUs.title", comment: "关于我们"), isPresented: Binding( get: { store.showAboutUs }, set: { store.send(.showAboutUs($0)) } )) { Button(LocalizedString("common.ok", comment: "确定")) { store.send(.showAboutUs(false)) } } message: { VStack(alignment: .leading, spacing: 8) { Text(LocalizedString("feedList.title", comment: "享受您的生活时光")) .font(.headline) Text(LocalizedString("feedList.slogan", comment: "疾病如同残酷的统治者,\n而时间是我们最宝贵的财富。\n我们活着的每一刻,都是对不可避免命运的胜利。")) .font(.body) } } } @ViewBuilder private func mainView() -> some View { WithPerceptionTracking { let baseView = GeometryReader { geometry in ZStack { // 背景颜色 Color(hex: 0x0C0527) .ignoresSafeArea(.all) VStack(spacing: 0) { // 顶部导航栏 HStack { Button(action: { store.send(.dismissTapped) }) { Image(systemName: "chevron.left") .font(.system(size: 24, weight: .medium)) .foregroundColor(.white) .frame(width: 44, height: 44) } Spacer() Text(LocalizedString("appSetting.title", comment: "编辑")) .font(.system(size: 18, weight: .medium)) .foregroundColor(.white) Spacer() // 占位,保持标题居中 Color.clear .frame(width: 44, height: 44) } .padding(.horizontal, 16) .padding(.top, 8) // 主要内容区域 ScrollView { VStack(spacing: 0) { // 头像设置区域 avatarSection() .padding(.top, 20) // 个人信息设置区域 personalInfoSection() .padding(.top, 30) // 其他设置区域 otherSettingsSection() .padding(.top, 20) Spacer(minLength: 40) // 退出登录按钮 logoutSection() .padding(.bottom, 40) } .padding(.horizontal, 20) } } } } .navigationBarHidden(true) let viewWithActionSheet = baseView .confirmationDialog( "请选择图片来源", isPresented: Binding( get: { store.showImageSourceActionSheet }, set: { store.send(.setShowImageSourceActionSheet($0)) } ), titleVisibility: .visible ) { Button(LocalizedString("app_settings.take_photo", comment: "拍照")) { store.send(.selectImageSource(AppImageSource.camera)) } Button(LocalizedString("app_settings.select_from_album", comment: "从相册选择")) { store.send(.selectImageSource(AppImageSource.photoLibrary)) } Button(LocalizedString("common.cancel", comment: "取消"), role: .cancel) { } } let viewWithCamera = viewWithActionSheet .sheet(isPresented: Binding( get: { store.showCamera }, set: { store.send(.setShowCamera($0)) } )) { CameraPicker { image in store.send(.cameraImagePicked(image)) } } let viewWithPhotoPicker = viewWithCamera .photosPicker( isPresented: Binding( get: { store.showPhotoPicker }, set: { store.send(.setShowPhotoPicker($0)) } ), selection: Binding( get: { store.selectedPhotoItems }, set: { store.send(.photoPickerItemsChanged($0)) } ), maxSelectionCount: 1, matching: .images ) let viewWithAlert = viewWithPhotoPicker .alert(LocalizedString("appSetting.nickname", comment: "编辑昵称"), isPresented: Binding( get: { store.isEditingNickname }, set: { store.send(.nicknameEditAlert($0)) } )) { TextField(LocalizedString("appSetting.nickname", comment: "请输入昵称"), text: Binding( get: { store.nicknameInput }, set: { store.send(.nicknameInputChanged($0)) } )) Button(LocalizedString("common.cancel", comment: "取消")) { store.send(.nicknameEditAlert(false)) } Button(LocalizedString("common.confirm", comment: "确认")) { let trimmed = store.nicknameInput.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmed.isEmpty { store.send(.nicknameEditConfirmed(trimmed)) } store.send(.nicknameEditAlert(false)) } } message: { Text(LocalizedString("appSetting.nickname", comment: "请输入新的昵称")) } let viewWithPrivacyPolicy = viewWithAlert .webView( isPresented: Binding( get: { store.showPrivacyPolicy }, set: { isPresented in if !isPresented { store.send(.privacyPolicyDismissed) } } ), url: APIConfiguration.webURL(for: .privacyPolicy) ) let viewWithUserAgreement = viewWithPrivacyPolicy .webView( isPresented: Binding( get: { store.showUserAgreement }, set: { isPresented in if !isPresented { store.send(.userAgreementDismissed) } } ), url: APIConfiguration.webURL(for: .userAgreement) ) let viewWithDeactivateAccount = viewWithUserAgreement .webView( isPresented: Binding( get: { store.showDeactivateAccount }, set: { isPresented in if !isPresented { store.send(.deactivateAccountDismissed) } } ), url: APIConfiguration.webURL(for: .deactivateAccount) ) viewWithDeactivateAccount } } // MARK: - 头像设置区域 @ViewBuilder private func avatarSection() -> some View { WithPerceptionTracking { VStack(spacing: 16) { // 头像 Button(action: { store.send(.setShowImageSourceActionSheet(true)) }) { ZStack { AsyncImage(url: URL(string: store.userInfo?.avatar ?? "")) { image in image .resizable() .aspectRatio(contentMode: .fill) } placeholder: { Image(systemName: "person.circle.fill") .resizable() .aspectRatio(contentMode: .fill) .foregroundColor(.gray) } .frame(width: 120, height: 120) .clipShape(Circle()) // 相机图标覆盖 VStack { Spacer() HStack { Spacer() Circle() .fill(Color.purple) .frame(width: 32, height: 32) .overlay( Image(systemName: "camera") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) ) } } .frame(width: 120, height: 120) } } } } } // MARK: - 个人信息设置区域 @ViewBuilder private func personalInfoSection() -> some View { WithPerceptionTracking { VStack(spacing: 0) { // 昵称设置 SettingRow( title: LocalizedString("appSetting.nickname", comment: "昵称"), subtitle: store.userInfo?.nick ?? LocalizedString("app_settings.not_set", comment: "未设置"), action: { store.send(.nicknameEditAlert(true)) } ) } } } // MARK: - 其他设置区域 @ViewBuilder private func otherSettingsSection() -> some View { WithPerceptionTracking { VStack(spacing: 0) { SettingRow( title: LocalizedString("appSetting.personalInfoPermissions", comment: "个人信息与权限"), subtitle: "", action: { store.send(.personalInfoPermissionsTapped) } ) Divider() .background(Color.white.opacity(0.2)) .padding(.leading, 16) SettingRow( title: LocalizedString("appSetting.help", comment: "帮助"), subtitle: "", action: { store.send(.helpTapped) } ) Divider() .background(Color.white.opacity(0.2)) .padding(.leading, 16) SettingRow( title: LocalizedString("appSetting.clearCache", comment: "清除缓存"), subtitle: "", action: { store.send(.clearCacheTapped) } ) Divider() .background(Color.white.opacity(0.2)) .padding(.leading, 16) SettingRow( title: LocalizedString("appSetting.checkUpdates", comment: "检查更新"), subtitle: "", action: { store.send(.checkUpdatesTapped) } ) Divider() .background(Color.white.opacity(0.2)) .padding(.leading, 16) SettingRow( title: LocalizedString("appSetting.deactivateAccount", comment: "注销账号"), subtitle: "", action: { store.send(.deactivateAccountTapped) } ) Divider() .background(Color.white.opacity(0.2)) .padding(.leading, 16) SettingRow( title: LocalizedString("appSetting.aboutUs", comment: "关于我们"), subtitle: "", action: { store.send(.aboutUsTapped) } ) } } } // MARK: - 退出登录区域 @ViewBuilder private func logoutSection() -> some View { WithPerceptionTracking { VStack(spacing: 12) { // 退出登录按钮 Button(action: { store.send(.logoutTapped) }) { Text(LocalizedString("appSetting.logoutAccount", comment: "退出账户")) .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) .frame(maxWidth: .infinity) .padding(.vertical, 16) .background(Color.red.opacity(0.8)) .cornerRadius(12) } } } } } // MARK: - 设置行组件 //struct SettingRow: View { // let title: String // let subtitle: String // let action: (() -> Void)? // // var body: some View { // Button(action: { // action?() // }) { // HStack(spacing: 16) { // HStack { // Text(title) // .font(.system(size: 16)) // .foregroundColor(.white) // .multilineTextAlignment(.leading) // // Spacer() // // if !subtitle.isEmpty { // Text(subtitle) // .font(.system(size: 14)) // .foregroundColor(.white.opacity(0.7)) // } // } // // Spacer() // // if action != nil { // Image(systemName: "chevron.right") // .font(.system(size: 14)) // .foregroundColor(.white.opacity(0.5)) // } // } // .padding(.horizontal, 16) // .padding(.vertical, 12) // } // .disabled(action == nil) // } //}