import Foundation import ComposableArchitecture // 图片源选择枚举 enum AppImageSource: Equatable { case camera case photoLibrary } @Reducer struct AppSettingFeature { @ObservableState struct State: Equatable { var nickname: String = "" var avatarURL: String? = nil var userInfo: UserInfo? = nil var isLoadingUserInfo: Bool = false var userInfoError: String? = nil // WebView 导航状态 var showUserAgreement: Bool = false var showPrivacyPolicy: Bool = false var showDeactivateAccount: Bool = false // 头像/昵称修改相关 var isUploadingAvatar: Bool = false var avatarUploadError: String? = nil var isEditingNickname: Bool = false var nicknameInput: String = "" var isUpdatingUser: Bool = false var updateUserError: String? = nil // 默认初始化器 init() { // 默认初始化 } // 带userInfo、avatarURL、nickname的init init(nickname: String = "", avatarURL: String? = nil, userInfo: UserInfo? = nil) { self.nickname = nickname self.avatarURL = avatarURL self.userInfo = userInfo } // 新增:TCA驱动图片选择弹窗 var showImagePicker: Bool = false // 新增:图片源选择 ActionSheet var showImageSourceActionSheet: Bool = false // 新增:选择的图片源 var selectedImageSource: AppImageSource? = nil // 新增:弹窗状态 var showLogoutConfirmation: Bool = false var showAboutUs: Bool = false } enum Action: Equatable { case onAppear case editNicknameTapped case logoutTapped case dismissTapped // 用户信息相关 case loadUserInfo case userInfoResponse(Result) // WebView 导航 case personalInfoPermissionsTapped case helpTapped case clearCacheTapped case checkUpdatesTapped case aboutUsTapped case deactivateAccountTapped // WebView 关闭 case userAgreementDismissed case privacyPolicyDismissed case deactivateAccountDismissed // 头像/昵称修改 case avatarTapped case avatarSelected(Data) case avatarUploadResult(Result) case nicknameEditConfirmed(String) case updateUser(Result) case nicknameInputChanged(String) case nicknameEditAlert(Bool) case testPushTapped // 新增:TCA驱动图片选择弹窗 case setShowImagePicker(Bool) // 新增:图片源选择 ActionSheet case setShowImageSourceActionSheet(Bool) // 新增:图片源选择 case selectImageSource(AppImageSource) // 新增:弹窗相关 case showLogoutConfirmation(Bool) case showAboutUs(Bool) case logoutConfirmed } @Dependency(\.apiService) var apiService func reduce(into state: inout State, action: Action) -> Effect { switch action { case .onAppear: return .send(.loadUserInfo) case .editNicknameTapped: // 预留编辑昵称逻辑 return .none case .logoutTapped: // 显示登出确认弹窗 state.showLogoutConfirmation = true return .none case .logoutConfirmed: // 清理所有认证信息,并向上层发送登出事件 return .run { send in await UserInfoManager.clearAllAuthenticationData() // 向上层Feature传递登出事件(需在MainFeature处理) // 这里直接返回.none,由MainFeature监听AppSettingFeature.Action.logoutTapped后处理 } case .dismissTapped: // 返回上一页,由 MainFeature 处理 navigationPath 的 pop 操作 return .none case .loadUserInfo: state.isLoadingUserInfo = true state.userInfoError = nil return .run { send in // do { if let userInfo = await UserInfoManager.getUserInfo() { await send(.userInfoResponse(.success(userInfo))) } else { await send(.userInfoResponse(.failure(APIError.custom("用户信息不存在")))) } // } catch { // let apiError: APIError // if let error = error as? APIError { // apiError = error // } else { // apiError = APIError.custom(error.localizedDescription) // } // await send(.userInfoResponse(.failure(apiError))) // } } case let .userInfoResponse(.success(userInfo)): state.userInfo = userInfo state.nickname = userInfo.nick ?? "" state.avatarURL = userInfo.avatar // 确保同步 state.isLoadingUserInfo = false return .none case let .userInfoResponse(.failure(error)): state.userInfoError = error.localizedDescription state.isLoadingUserInfo = false return .none case .personalInfoPermissionsTapped: state.showPrivacyPolicy = true return .none case .helpTapped: state.showUserAgreement = true return .none case .clearCacheTapped: // 预留清除缓存逻辑 return .none case .checkUpdatesTapped: // 预留检查更新逻辑 return .none case .aboutUsTapped: state.showAboutUs = true return .none case .deactivateAccountTapped: state.showDeactivateAccount = true return .none case .userAgreementDismissed: state.showUserAgreement = false return .none case .privacyPolicyDismissed: state.showPrivacyPolicy = false return .none case .deactivateAccountDismissed: state.showDeactivateAccount = false return .none case .avatarTapped: // 触发头像选择器 return .none case let .avatarSelected(imageData): state.isUploadingAvatar = true state.avatarUploadError = nil return .run { [avatarData = imageData] send in guard let uiImage = UIImage(data: avatarData) else { await send(.avatarUploadResult(.failure(APIError.custom("图片格式错误")))) return } // 上传图片到腾讯云 if let url = await COSManager.shared.uploadUIImage(uiImage, apiService: apiService) { await send(.avatarUploadResult(.success(url))) } else { await send(.avatarUploadResult(.failure(APIError.custom("头像上传失败")))) } } case let .avatarUploadResult(.success(url)): state.isUploadingAvatar = false // 调用 updateUser API,仅传 avatar state.isUpdatingUser = true state.updateUserError = nil guard let userInfo = state.userInfo else { return .none } // 头像上传后,先临时更新本地avatarURL,提升UI响应 state.avatarURL = url return .run { send in let ticket = await UserInfoManager.getCurrentUserTicket() ?? "" let req = UpdateUserRequest(avatar: url, nick: nil, uid: userInfo.uid ?? 0, ticket: ticket) do { let resp: UpdateUserResponse = try await apiService.request(req) await send(.updateUser(.success(resp))) } catch { let apiError = error as? APIError ?? APIError.custom(error.localizedDescription) await send(.updateUser(.failure(apiError))) } } case let .avatarUploadResult(.failure(error)): state.isUploadingAvatar = false state.avatarUploadError = error.localizedDescription return .none case .nicknameEditAlert(let show): state.isEditingNickname = show state.nicknameInput = state.nickname return .none case .nicknameInputChanged(let text): state.nicknameInput = String(text.prefix(15)) return .none case .nicknameEditConfirmed(let newNick): guard let userInfo = state.userInfo else { return .none } state.isUpdatingUser = true state.updateUserError = nil return .run { send in let ticket = await UserInfoManager.getCurrentUserTicket() ?? "" let req = UpdateUserRequest(avatar: nil, nick: newNick, uid: userInfo.uid ?? 0, ticket: ticket) do { let resp: UpdateUserResponse = try await apiService.request(req) await send(.updateUser(.success(resp))) } catch { let apiError = error as? APIError ?? APIError.custom(error.localizedDescription) await send(.updateUser(.failure(apiError))) } } case .updateUser(.success(_)): state.isUpdatingUser = false // 不直接用 resp.data,触发拉取完整 userinfo,延迟1秒 if let uid = state.userInfo?.uid { return .run { send in try? await Task.sleep(nanoseconds: 1_000_000_000) if let newUser = await UserInfoManager.fetchUserInfoFromServer(uid: String(uid), apiService: apiService) { await send(.userInfoResponse(.success(newUser))) } else { await send(.userInfoResponse(.failure(APIError.custom("获取最新用户信息失败")))) } } } state.isEditingNickname = false return .none case let .updateUser(.failure(error)): state.isUpdatingUser = false state.updateUserError = error.localizedDescription return .none case .testPushTapped: return .none case .setShowImagePicker(let show): state.showImagePicker = show return .none case .setShowImageSourceActionSheet(let show): state.showImageSourceActionSheet = show return .none case .selectImageSource(let source): state.showImageSourceActionSheet = false state.showImagePicker = true state.selectedImageSource = source // 这里可以传递选择的源到 ImagePickerWithPreviewView return .none case .showLogoutConfirmation(let show): state.showLogoutConfirmation = show return .none case .showAboutUs(let show): state.showAboutUs = show return .none } } }