
- 在DynamicsModels.swift中为动态响应结构和列表数据添加Sendable协议,提升并发安全性。 - 在FeedListFeature.swift中实现动态内容的加载逻辑,集成API请求以获取最新动态。 - 在FeedListView.swift中新增动态内容列表展示,优化用户交互体验。 - 在MeView.swift中添加设置按钮,支持弹出设置视图。 - 在SettingView.swift中新增COS上传测试功能,允许用户测试图片上传至腾讯云COS。 - 在OptimizedDynamicCardView.swift中实现优化的动态卡片组件,提升动态展示效果。
307 lines
11 KiB
Swift
307 lines
11 KiB
Swift
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)
|
||
// .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?
|
||
|
||
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))
|
||
}
|
||
}
|
||
}
|
||
.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()
|
||
// }
|
||
// )
|
||
//}
|