Files
e-party-iOS/yana/Views/SettingView.swift
edwinQQQ 0fe3b6cb7a feat: 新增用户信息获取功能及相关模型
- 在APIEndpoints.swift中新增getUserInfo端点以支持获取用户信息。
- 在APIModels.swift中实现获取用户信息请求和响应模型,处理用户信息的请求与解析。
- 在UserInfoManager中新增方法以从服务器获取用户信息,并在登录成功后自动获取用户信息。
- 在SettingFeature中新增用户信息刷新状态管理,支持用户信息的刷新操作。
- 在SettingView中集成用户信息刷新按钮,提升用户体验。
- 在SplashFeature中实现自动获取用户信息的逻辑,优化用户登录流程。
- 在yanaAPITests中添加用户信息相关的单元测试,确保功能的正确性。
2025-07-23 11:46:46 +08:00

333 lines
12 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 SwiftUI
import ComposableArchitecture
struct SettingView: View {
let store: StoreOf<SettingFeature>
@ObservedObject private var localizationManager = LocalizationManager.shared
var body: some View {
NavigationStack {
WithPerceptionTracking {
GeometryReader { geometry in
ZStack {
// - 使"bg"
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea(.all)
VStack(spacing: 0) {
// Navigation Bar
HStack {
//
Button(action: {
store.send(.dismissTapped)
}) {
Image(systemName: "chevron.left")
.font(.title2)
.foregroundColor(.white)
}
.padding(.leading, 16)
Spacer()
//
Text(NSLocalizedString("setting.title", comment: "Settings"))
.font(.custom("PingFang SC-Semibold", size: 16))
.foregroundColor(.white)
Spacer()
//
Color.clear
.frame(width: 44, height: 44)
}
.padding(.top, 8)
.padding(.horizontal)
//
ScrollView {
VStack(spacing: 24) {
UserInfoCardView(userInfo: store.userInfo, accountModel: store.accountModel, isRefreshing: store.isRefreshingUserInfo, onRefresh: {
store.send(.refreshUserInfo)
})
// .padding()
.padding(.top, 32)
SettingOptionsView(
onLanguageTapped: {
// TODO:
},
onAboutTapped: {
// TODO:
}
)
Spacer(minLength: 50)
LogoutButtonView {
store.send(.logoutTapped)
}
.padding(.bottom, 50)
}
}
}
}
}
.onAppear {
store.send(.onAppear)
}
}
}
}
}
// MARK: - User Info Card View
struct UserInfoCardView: View {
let userInfo: UserInfo?
let accountModel: AccountModel?
let isRefreshing: Bool //
let onRefresh: () -> Void //
var body: some View {
VStack(spacing: 16) {
//
Image(systemName: "person.circle.fill")
.font(.system(size: 60))
.foregroundColor(.white.opacity(0.8))
//
VStack(spacing: 8) {
if let userInfo = userInfo, let userName = userInfo.username {
Text(userName)
.font(.title2.weight(.semibold))
.foregroundColor(.white)
} else {
Text(NSLocalizedString("setting.user", comment: "User"))
.font(.title2.weight(.semibold))
.foregroundColor(.white)
}
// ID
if let userInfo = userInfo, let userId = userInfo.userId {
Text("ID: \(userId)")
.font(.caption)
.foregroundColor(.white.opacity(0.8))
} else if let accountModel = accountModel, let uid = accountModel.uid {
Text("UID: \(uid)")
.font(.caption)
.foregroundColor(.white.opacity(0.8))
}
}
//
Button(action: onRefresh) {
HStack(spacing: 4) {
if isRefreshing {
ProgressView()
.scaleEffect(0.8)
.progressViewStyle(CircularProgressViewStyle(tint: .white))
} else {
Image(systemName: "arrow.clockwise")
.font(.caption)
}
Text(isRefreshing ? "刷新中..." : "刷新")
.font(.caption)
}
.foregroundColor(.white)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color.white.opacity(0.2))
.cornerRadius(12)
}
.disabled(isRefreshing)
}
.padding(.vertical, 24)
.padding(.horizontal, 20)
.background(Color.black.opacity(0.3))
.cornerRadius(16)
.padding(.horizontal, 24)
}
}
// MARK: - Setting Options View
// Add this new view for testing COS upload
struct TestCOSUploadView: View {
@State private var imageURL: String = "https://img.toto.im/mw600/66b3de17ly1i3mpcw0k7yj20hs0md0tf.jpg.webp"
@State private var uploadResult: String = ""
@State private var isUploading: Bool = false
@Dependency(\.apiService) private var apiService
var body: some View {
VStack(spacing: 20) {
TextField("Enter image URL", text: $imageURL)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button(action: {
Task {
await uploadImageFromURL()
}
}) {
Text(isUploading ? "Uploading..." : "Upload to COS")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.disabled(isUploading || imageURL.isEmpty)
Text(uploadResult)
.multilineTextAlignment(.center)
.padding()
}
.navigationTitle("Test COS Upload")
}
private func uploadImageFromURL() async {
guard let url = URL(string: imageURL) else {
uploadResult = "Invalid URL"
return
}
isUploading = true
uploadResult = ""
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let cloudURL = await COSManager.shared.uploadImage(data, apiService: apiService) {
uploadResult = "Upload successful! Cloud URL: \(cloudURL)"
} else {
uploadResult = "Upload failed"
}
} catch {
uploadResult = "Download failed: \(error.localizedDescription)"
}
isUploading = false
}
}
// Modify SettingOptionsView to add the test row
struct SettingOptionsView: View {
let onLanguageTapped: () -> Void
let onAboutTapped: () -> Void
var body: some View {
VStack(spacing: 12) {
SettingRowView(
icon: "globe",
title: NSLocalizedString("setting.language", comment: "Language Settings"),
action: onLanguageTapped
)
SettingRowView(
icon: "info.circle",
title: NSLocalizedString("setting.about", comment: "About Us"),
action: onAboutTapped
)
#if DEBUG
NavigationLink(destination: TestCOSUploadView()) {
HStack {
Image(systemName: "cloud.upload")
.foregroundColor(.white.opacity(0.8))
.frame(width: 24)
Text("Test COS Upload")
.foregroundColor(.white)
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.white.opacity(0.6))
.font(.caption)
}
.padding(.horizontal, 20)
.padding(.vertical, 16)
.background(Color.black.opacity(0.2))
.cornerRadius(12)
}
#endif
HStack {
Image(systemName: "app.badge")
.foregroundColor(.white.opacity(0.8))
.frame(width: 24)
Text(NSLocalizedString("setting.version", comment: "Version Info"))
.foregroundColor(.white)
Spacer()
Text("1.0.0")
.foregroundColor(.white.opacity(0.6))
.font(.caption)
}
.padding(.horizontal, 20)
.padding(.vertical, 16)
.background(Color.black.opacity(0.2))
.cornerRadius(12)
}
.padding(.horizontal, 24)
}
}
// MARK: - Logout Button View
struct LogoutButtonView: View {
let action: () -> Void
var body: some View {
Button(action: action) {
HStack {
Image(systemName: "arrow.right.square")
Text(NSLocalizedString("setting.logout", comment: "Logout"))
}
.font(.body.weight(.medium))
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(Color.red.opacity(0.7))
.cornerRadius(12)
}
.padding(.horizontal, 24)
}
}
// MARK: - Setting Row View
struct SettingRowView: View {
let icon: String
let title: String
let action: () -> Void
var body: some View {
Button(action: action) {
HStack {
Image(systemName: icon)
.foregroundColor(.white.opacity(0.8))
.frame(width: 24)
Text(title)
.foregroundColor(.white)
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(.white.opacity(0.6))
.font(.caption)
}
.padding(.horizontal, 20)
.padding(.vertical, 16)
.background(Color.black.opacity(0.2))
.cornerRadius(12)
}
}
}
//#Preview {
// SettingView(
// store: Store(
// initialState: SettingFeature.State()
// ) {
// SettingFeature()
// }
// )
//}