Files
e-party-iOS/yana/Features/AppSettingFeature.swift
edwinQQQ ac0d622c97 feat: 更新Info.plist和AppSettingView以支持相机和图片选择功能
- 在Info.plist中新增相机使用说明,确保应用能够访问相机功能。
- 在AppSettingFeature中新增showImagePicker状态和setShowImagePicker动作,支持图片选择弹窗的显示。
- 在AppSettingView中整合图片选择与预览功能,优化用户体验。
- 更新MainView以简化导航逻辑,提升代码可读性与维护性。
- 在ImagePickerWithPreview组件中实现相机和相册选择功能,增强交互性。
2025-07-25 18:47:11 +08:00

244 lines
9.4 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
// userInfoavatarURLnicknameinit
init(nickname: String = "", avatarURL: String? = nil, userInfo: UserInfo? = nil) {
self.nickname = nickname
self.avatarURL = avatarURL
self.userInfo = userInfo
}
// TCA
var showImagePicker: Bool = false
}
enum Action: Equatable {
case onAppear
case editNicknameTapped
case logoutTapped
case dismissTapped
//
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)
case testPushTapped
// TCA
case setShowImagePicker(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()
// FeatureMainFeature
// .noneMainFeatureAppSettingFeature.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:
//
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 }
// avatarURLUI
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 userinfo1
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
}
}
}