
- 在APIEndpoints中新增用户信息更新端点。 - 实现UpdateUserRequest和UpdateUserResponse结构体,支持用户信息更新请求和响应。 - 在APIService中添加updateUser方法,处理用户信息更新请求。 - 更新AppSettingFeature以支持头像和昵称的修改,整合用户信息更新逻辑。 - 在AppSettingView中实现头像选择和昵称编辑功能,提升用户体验。
220 lines
8.5 KiB
Swift
220 lines
8.5 KiB
Swift
import Foundation
|
||
import ComposableArchitecture
|
||
|
||
@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 isUploadingAvatar: Bool = false
|
||
var avatarUploadError: String? = nil
|
||
var isEditingNickname: Bool = false
|
||
var nicknameInput: String = ""
|
||
var isUpdatingUser: Bool = false
|
||
var updateUserError: String? = nil
|
||
}
|
||
|
||
enum Action: Equatable {
|
||
case onAppear
|
||
case editNicknameTapped
|
||
case logoutTapped
|
||
|
||
// 用户信息相关
|
||
case loadUserInfo
|
||
case userInfoResponse(Result<UserInfo, APIError>)
|
||
|
||
// WebView 导航
|
||
case personalInfoPermissionsTapped
|
||
case helpTapped
|
||
case clearCacheTapped
|
||
case checkUpdatesTapped
|
||
case aboutUsTapped
|
||
|
||
// WebView 关闭
|
||
case userAgreementDismissed
|
||
case privacyPolicyDismissed
|
||
|
||
// 头像/昵称修改
|
||
case avatarTapped
|
||
case avatarSelected(Data)
|
||
case avatarUploadResult(Result<String, APIError>)
|
||
case nicknameEditConfirmed(String)
|
||
case updateUser(Result<UpdateUserResponse, APIError>)
|
||
case nicknameInputChanged(String)
|
||
case nicknameEditAlert(Bool)
|
||
}
|
||
|
||
@Dependency(\.apiService) var apiService
|
||
|
||
func reduce(into state: inout State, action: Action) -> Effect<Action> {
|
||
switch action {
|
||
case .onAppear:
|
||
return .send(.loadUserInfo)
|
||
|
||
case .editNicknameTapped:
|
||
// 预留编辑昵称逻辑
|
||
return .none
|
||
|
||
case .logoutTapped:
|
||
// 清理所有认证信息,并向上层发送登出事件
|
||
return .run { send in
|
||
await UserInfoManager.clearAllAuthenticationData()
|
||
// 向上层Feature传递登出事件(需在MainFeature处理)
|
||
// 这里直接返回.none,由MainFeature监听AppSettingFeature.Action.logoutTapped后处理
|
||
}
|
||
|
||
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:
|
||
// 预留关于我们逻辑
|
||
return .none
|
||
|
||
case .userAgreementDismissed:
|
||
state.showUserAgreement = false
|
||
return .none
|
||
|
||
case .privacyPolicyDismissed:
|
||
state.showPrivacyPolicy = 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 }
|
||
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 let .updateUser(.success(resp)):
|
||
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
|
||
}
|
||
}
|
||
}
|