feat: 移除设置功能并优化主功能状态管理
- 从HomeFeature和MainFeature中移除设置相关状态和逻辑,简化状态管理。 - 更新视图以去除设置页面的展示,提升用户体验。 - 删除SettingFeature及其相关视图,减少冗余代码,增强代码可维护性。
This commit is contained in:
@@ -11,8 +11,6 @@ struct HomeFeature: Reducer {
|
|||||||
var userInfo: UserInfo?
|
var userInfo: UserInfo?
|
||||||
var accountModel: AccountModel?
|
var accountModel: AccountModel?
|
||||||
var error: String?
|
var error: String?
|
||||||
var isSettingPresented = false
|
|
||||||
var settingState = SettingFeature.State()
|
|
||||||
var feedState = FeedFeature.State()
|
var feedState = FeedFeature.State()
|
||||||
var meDynamic = MeDynamicFeature.State(uid: 0)
|
var meDynamic = MeDynamicFeature.State(uid: 0)
|
||||||
var isLoggedOut = false
|
var isLoggedOut = false
|
||||||
@@ -28,8 +26,6 @@ struct HomeFeature: Reducer {
|
|||||||
case accountModelLoaded(AccountModel?)
|
case accountModelLoaded(AccountModel?)
|
||||||
case logoutTapped
|
case logoutTapped
|
||||||
case logout
|
case logout
|
||||||
case settingDismissed
|
|
||||||
case setting(SettingFeature.Action)
|
|
||||||
case feed(FeedFeature.Action)
|
case feed(FeedFeature.Action)
|
||||||
case meDynamic(MeDynamicFeature.Action)
|
case meDynamic(MeDynamicFeature.Action)
|
||||||
case logoutCompleted
|
case logoutCompleted
|
||||||
@@ -38,9 +34,6 @@ struct HomeFeature: Reducer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some ReducerOf<Self> {
|
var body: some ReducerOf<Self> {
|
||||||
Scope(state: \.settingState, action: \.setting) {
|
|
||||||
SettingFeature()
|
|
||||||
}
|
|
||||||
Scope(state: \.feedState, action: \.feed) {
|
Scope(state: \.feedState, action: \.feed) {
|
||||||
FeedFeature()
|
FeedFeature()
|
||||||
}
|
}
|
||||||
@@ -83,10 +76,9 @@ struct HomeFeature: Reducer {
|
|||||||
case .logoutCompleted:
|
case .logoutCompleted:
|
||||||
state.isLoggedOut = true
|
state.isLoggedOut = true
|
||||||
return .none
|
return .none
|
||||||
case .settingDismissed:
|
case .feed:
|
||||||
state.isSettingPresented = false
|
|
||||||
return .none
|
return .none
|
||||||
case .setting:
|
case .meDynamic:
|
||||||
return .none
|
return .none
|
||||||
case .showCreateFeed:
|
case .showCreateFeed:
|
||||||
state.route = .createFeed
|
state.route = .createFeed
|
||||||
@@ -94,10 +86,6 @@ struct HomeFeature: Reducer {
|
|||||||
case .createFeedDismissed:
|
case .createFeedDismissed:
|
||||||
state.route = nil
|
state.route = nil
|
||||||
return .none
|
return .none
|
||||||
case .feed:
|
|
||||||
return .none
|
|
||||||
case .meDynamic:
|
|
||||||
return .none
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@ struct MainFeature: Reducer {
|
|||||||
var accountModel: AccountModel? = nil
|
var accountModel: AccountModel? = nil
|
||||||
// 新增:导航路径和设置页面 State
|
// 新增:导航路径和设置页面 State
|
||||||
var navigationPath: [Destination] = []
|
var navigationPath: [Destination] = []
|
||||||
var settingState: SettingFeature.State? = nil
|
|
||||||
var appSettingState: AppSettingFeature.State? = nil
|
var appSettingState: AppSettingFeature.State? = nil
|
||||||
// 新增:登出标志
|
// 新增:登出标志
|
||||||
var isLoggedOut: Bool = false
|
var isLoggedOut: Bool = false
|
||||||
@@ -22,7 +21,6 @@ struct MainFeature: Reducer {
|
|||||||
|
|
||||||
// 新增:导航目标
|
// 新增:导航目标
|
||||||
enum Destination: Hashable, Equatable {
|
enum Destination: Hashable, Equatable {
|
||||||
case setting
|
|
||||||
case test
|
case test
|
||||||
case appSetting
|
case appSetting
|
||||||
}
|
}
|
||||||
@@ -36,8 +34,6 @@ struct MainFeature: Reducer {
|
|||||||
case accountModelLoaded(AccountModel?)
|
case accountModelLoaded(AccountModel?)
|
||||||
// 新增:导航相关
|
// 新增:导航相关
|
||||||
case navigationPathChanged([Destination])
|
case navigationPathChanged([Destination])
|
||||||
case settingButtonTapped
|
|
||||||
case settingAction(SettingFeature.Action)
|
|
||||||
case testButtonTapped
|
case testButtonTapped
|
||||||
case appSettingButtonTapped
|
case appSettingButtonTapped
|
||||||
case appSettingAction(AppSettingFeature.Action)
|
case appSettingAction(AppSettingFeature.Action)
|
||||||
@@ -84,20 +80,11 @@ struct MainFeature: Reducer {
|
|||||||
return .none
|
return .none
|
||||||
case .navigationPathChanged(let newPath):
|
case .navigationPathChanged(let newPath):
|
||||||
// pop 回来时清空 settingState
|
// pop 回来时清空 settingState
|
||||||
if !newPath.contains(.setting) {
|
|
||||||
state.settingState = nil
|
|
||||||
}
|
|
||||||
if !newPath.contains(.appSetting) {
|
if !newPath.contains(.appSetting) {
|
||||||
state.appSettingState = nil
|
state.appSettingState = nil
|
||||||
}
|
}
|
||||||
state.navigationPath = newPath
|
state.navigationPath = newPath
|
||||||
return .none
|
return .none
|
||||||
case .settingButtonTapped:
|
|
||||||
state.settingState = SettingFeature.State()
|
|
||||||
state.navigationPath.append(.setting)
|
|
||||||
return .none
|
|
||||||
case .settingAction:
|
|
||||||
return .none
|
|
||||||
case .testButtonTapped:
|
case .testButtonTapped:
|
||||||
state.navigationPath.append(.test)
|
state.navigationPath.append(.test)
|
||||||
return .none
|
return .none
|
||||||
@@ -117,9 +104,6 @@ struct MainFeature: Reducer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 设置页作用域
|
// 设置页作用域
|
||||||
.ifLet(\ .settingState, action: \.settingAction) {
|
|
||||||
SettingFeature()
|
|
||||||
}
|
|
||||||
.ifLet(\ .appSettingState, action: \.appSettingAction) {
|
.ifLet(\ .appSettingState, action: \.appSettingAction) {
|
||||||
AppSettingFeature()
|
AppSettingFeature()
|
||||||
}
|
}
|
||||||
|
@@ -1,102 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import ComposableArchitecture
|
|
||||||
|
|
||||||
@Reducer
|
|
||||||
struct SettingFeature {
|
|
||||||
@ObservableState
|
|
||||||
struct State: Equatable {
|
|
||||||
var userInfo: UserInfo?
|
|
||||||
var accountModel: AccountModel?
|
|
||||||
var isLoading = false
|
|
||||||
var error: String?
|
|
||||||
var isRefreshingUserInfo = false // 新增:用户信息刷新状态
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Action: Equatable {
|
|
||||||
case onAppear
|
|
||||||
case loadUserInfo
|
|
||||||
case userInfoLoaded(UserInfo?)
|
|
||||||
case loadAccountModel
|
|
||||||
case accountModelLoaded(AccountModel?)
|
|
||||||
case refreshUserInfo // 新增:刷新用户信息
|
|
||||||
case refreshUserInfoResponse(TaskResult<UserInfo?>) // 新增:刷新用户信息响应
|
|
||||||
case logoutTapped
|
|
||||||
case logout
|
|
||||||
case dismissTapped
|
|
||||||
}
|
|
||||||
|
|
||||||
@Dependency(\.apiService) var apiService // 新增:API服务依赖
|
|
||||||
|
|
||||||
var body: some ReducerOf<Self> {
|
|
||||||
Reduce { state, action in
|
|
||||||
switch action {
|
|
||||||
case .onAppear:
|
|
||||||
return .concatenate(
|
|
||||||
.send(.loadUserInfo),
|
|
||||||
.send(.loadAccountModel)
|
|
||||||
)
|
|
||||||
|
|
||||||
case .loadUserInfo:
|
|
||||||
return .run { send in
|
|
||||||
let userInfo = await UserInfoManager.getUserInfo()
|
|
||||||
await send(.userInfoLoaded(userInfo))
|
|
||||||
}
|
|
||||||
|
|
||||||
case let .userInfoLoaded(userInfo):
|
|
||||||
state.userInfo = userInfo
|
|
||||||
return .none
|
|
||||||
|
|
||||||
case .loadAccountModel:
|
|
||||||
return .run { send in
|
|
||||||
let accountModel = await UserInfoManager.getAccountModel()
|
|
||||||
await send(.accountModelLoaded(accountModel))
|
|
||||||
}
|
|
||||||
|
|
||||||
case let .accountModelLoaded(accountModel):
|
|
||||||
state.accountModel = accountModel
|
|
||||||
return .none
|
|
||||||
|
|
||||||
case .refreshUserInfo: // 新增:刷新用户信息
|
|
||||||
return .none
|
|
||||||
// state.isRefreshingUserInfo = true
|
|
||||||
// state.error = nil
|
|
||||||
// return .run { send in
|
|
||||||
// let userInfo = await UserInfoManager.refreshCurrentUserInfo(apiService: apiService)
|
|
||||||
// await send(.refreshUserInfoResponse(.success(userInfo)))
|
|
||||||
// }
|
|
||||||
|
|
||||||
case let .refreshUserInfoResponse(.success(userInfo)): // 新增:处理刷新响应
|
|
||||||
state.isRefreshingUserInfo = false
|
|
||||||
if let userInfo = userInfo {
|
|
||||||
state.userInfo = userInfo
|
|
||||||
state.error = nil
|
|
||||||
} else {
|
|
||||||
state.error = "刷新用户信息失败"
|
|
||||||
}
|
|
||||||
return .none
|
|
||||||
|
|
||||||
case let .refreshUserInfoResponse(.failure(error)): // 新增:处理刷新错误
|
|
||||||
state.isRefreshingUserInfo = false
|
|
||||||
state.error = error.localizedDescription
|
|
||||||
return .none
|
|
||||||
|
|
||||||
case .logoutTapped:
|
|
||||||
return .send(.logout)
|
|
||||||
|
|
||||||
case .logout:
|
|
||||||
state.isLoading = true
|
|
||||||
return .run { _ in
|
|
||||||
await UserInfoManager.clearAllAuthenticationData()
|
|
||||||
}
|
|
||||||
|
|
||||||
case .dismissTapped:
|
|
||||||
return .none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除:未使用的通知名称定义
|
|
||||||
// extension Notification.Name {
|
|
||||||
// static let settingsDismiss = Notification.Name("settingsDismiss")
|
|
||||||
// }
|
|
@@ -58,12 +58,6 @@ struct HomeView: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
store.send(.onAppear)
|
store.send(.onAppear)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: Binding(
|
|
||||||
get: { store.withState(\.isSettingPresented) },
|
|
||||||
set: { _ in store.send(.settingDismissed) }
|
|
||||||
)) {
|
|
||||||
SettingView(store: store.scope(state: \.settingState, action: \.setting))
|
|
||||||
}
|
|
||||||
.navigationDestination(isPresented: Binding(
|
.navigationDestination(isPresented: Binding(
|
||||||
get: { store.withState(\.route) == .createFeed },
|
get: { store.withState(\.route) == .createFeed },
|
||||||
set: { isPresented in
|
set: { isPresented in
|
||||||
|
@@ -48,21 +48,6 @@ struct InternalMainView: View {
|
|||||||
.isHidden(viewStore.selectedTab != .other)
|
.isHidden(viewStore.selectedTab != .other)
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
// 测试按钮
|
|
||||||
VStack {
|
|
||||||
Spacer()
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Button("Test Push") {
|
|
||||||
viewStore.send(.testButtonTapped)
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(Color.red)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.cornerRadius(8)
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 底部导航栏
|
// 底部导航栏
|
||||||
VStack {
|
VStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -76,18 +61,6 @@ struct InternalMainView: View {
|
|||||||
}
|
}
|
||||||
.navigationDestination(for: MainFeature.Destination.self) { destination in
|
.navigationDestination(for: MainFeature.Destination.self) { destination in
|
||||||
switch destination {
|
switch destination {
|
||||||
case .setting:
|
|
||||||
IfLetStore(
|
|
||||||
self.store.scope(
|
|
||||||
state: \.settingState,
|
|
||||||
action: MainFeature.Action.settingAction
|
|
||||||
),
|
|
||||||
then: { settingStore in
|
|
||||||
WithPerceptionTracking {
|
|
||||||
SettingView(store: settingStore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
case .test:
|
case .test:
|
||||||
TestPushView()
|
TestPushView()
|
||||||
case .appSetting:
|
case .appSetting:
|
||||||
|
@@ -1,332 +0,0 @@
|
|||||||
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()
|
|
||||||
// }
|
|
||||||
// )
|
|
||||||
//}
|
|
Reference in New Issue
Block a user