feat: 新增导航功能与设置页面支持

- 在MainFeature中新增导航路径和设置页面状态管理,支持页面导航。
- 更新MainView以集成导航功能,添加测试按钮以触发导航。
- 在MeFeature中新增设置按钮点击事件,交由MainFeature处理。
- 增强MeView以支持设置按钮,提升用户体验。
This commit is contained in:
edwinQQQ
2025-07-23 20:10:37 +08:00
parent 772543243f
commit bb49b00a59
4 changed files with 124 additions and 18 deletions

View File

@@ -12,6 +12,15 @@ struct MainFeature: Reducer {
var feedList: FeedListFeature.State = .init() var feedList: FeedListFeature.State = .init()
var me: MeFeature.State = .init() var me: MeFeature.State = .init()
var accountModel: AccountModel? = nil var accountModel: AccountModel? = nil
// State
var navigationPath: [Destination] = []
var settingState: SettingFeature.State? = nil
}
//
enum Destination: Hashable, Equatable {
case setting
case test
} }
@CasePathable @CasePathable
@@ -21,6 +30,11 @@ struct MainFeature: Reducer {
case feedList(FeedListFeature.Action) case feedList(FeedListFeature.Action)
case me(MeFeature.Action) case me(MeFeature.Action)
case accountModelLoaded(AccountModel?) case accountModelLoaded(AccountModel?)
//
case navigationPathChanged([Destination])
case settingButtonTapped
case settingAction(SettingFeature.Action)
case testButtonTapped
} }
var body: some ReducerOf<Self> { var body: some ReducerOf<Self> {
@@ -39,6 +53,7 @@ struct MainFeature: Reducer {
} }
case .selectTab(let tab): case .selectTab(let tab):
state.selectedTab = tab state.selectedTab = tab
state.navigationPath = []
if tab == .other, let uidStr = state.accountModel?.uid, let uid = Int(uidStr), uid > 0 { if tab == .other, let uidStr = state.accountModel?.uid, let uid = Int(uidStr), uid > 0 {
state.me = MeFeature.State(uid: uid) state.me = MeFeature.State(uid: uid)
return .send(.me(.onAppear)) return .send(.me(.onAppear))
@@ -49,9 +64,34 @@ struct MainFeature: Reducer {
case let .accountModelLoaded(accountModel): case let .accountModelLoaded(accountModel):
state.accountModel = accountModel state.accountModel = accountModel
return .none return .none
case .me(.settingButtonTapped):
// push
state.settingState = SettingFeature.State()
state.navigationPath.append(.setting)
return .none
case .me: case .me:
return .none return .none
case .navigationPathChanged(let newPath):
// pop settingState
if !newPath.contains(.setting) {
state.settingState = nil
}
state.navigationPath = newPath
return .none
case .settingButtonTapped:
state.settingState = SettingFeature.State()
state.navigationPath.append(.setting)
return .none
case .settingAction:
return .none
case .testButtonTapped:
state.navigationPath.append(.test)
return .none
} }
} }
//
.ifLet(\ .settingState, action: /Action.settingAction) {
SettingFeature()
}
} }
} }

View File

@@ -25,6 +25,8 @@ struct MeFeature {
case loadMore case loadMore
case userInfoResponse(Result<UserInfo, APIError>) case userInfoResponse(Result<UserInfo, APIError>)
case momentsResponse(Result<MyMomentsResponse, APIError>) case momentsResponse(Result<MyMomentsResponse, APIError>)
//
case settingButtonTapped
} }
func reduce(into state: inout State, action: Action) -> Effect<Action> { func reduce(into state: inout State, action: Action) -> Effect<Action> {
@@ -84,6 +86,9 @@ struct MeFeature {
state.momentsError = error.localizedDescription state.momentsError = error.localizedDescription
} }
return .none return .none
case .settingButtonTapped:
// MainFeature
return .none
} }
} }

View File

@@ -6,7 +6,7 @@ struct MainView: View {
var body: some View { var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in WithViewStore(self.store, observe: { $0 }) { viewStore in
NavigationStack { NavigationStack(path: viewStore.binding(get: \.navigationPath, send: MainFeature.Action.navigationPathChanged)) {
GeometryReader { geometry in GeometryReader { geometry in
ZStack { ZStack {
// //
@@ -36,6 +36,21 @@ struct MainView: View {
} }
} }
.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()
@@ -47,6 +62,24 @@ struct MainView: View {
.padding(.bottom, geometry.safeAreaInsets.bottom + 60) .padding(.bottom, geometry.safeAreaInsets.bottom + 60)
} }
} }
.navigationDestination(for: MainFeature.Destination.self) { destination in
switch destination {
case .setting:
IfLetStore(
self.store.scope(
state: \.settingState,
action: MainFeature.Action.settingAction
),
then: { settingStore in
WithPerceptionTracking {
SettingView(store: settingStore)
}
}
)
case .test:
TestPushView()
}
}
} }
.onAppear { .onAppear {
viewStore.send(.onAppear) viewStore.send(.onAppear)
@@ -54,3 +87,14 @@ struct MainView: View {
} }
} }
} }
struct TestPushView: View {
var body: some View {
ZStack {
Color.blue.ignoresSafeArea()
Text("Test Push View")
.font(.largeTitle)
.foregroundColor(.white)
}
}
}

View File

@@ -5,17 +5,32 @@ struct MeView: View {
let store: StoreOf<MeFeature> let store: StoreOf<MeFeature>
var body: some View { var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in GeometryReader { geometry in
GeometryReader { geometry in ZStack {
ZStack { Image("bg")
Image("bg") .resizable()
.resizable() .aspectRatio(contentMode: .fill)
.aspectRatio(contentMode: .fill) .frame(maxWidth: .infinity, maxHeight: .infinity)
.frame(maxWidth: .infinity, maxHeight: .infinity) .clipped()
.clipped() .ignoresSafeArea(.all)
.ignoresSafeArea(.all) VStack(spacing: 0) {
VStack(spacing: 0) { //
// HStack {
Spacer()
WithViewStore(self.store, observe: { $0 }) { viewStore in
Button(action: {
viewStore.send(.settingButtonTapped)
}) {
Image(systemName: "gearshape")
.font(.system(size: 22, weight: .medium))
.foregroundColor(.white)
}
.padding(.trailing, 16)
.padding(.top, 8)
}
}
//
WithViewStore(self.store, observe: { $0 }) { viewStore in
if viewStore.isLoadingUserInfo { if viewStore.isLoadingUserInfo {
ProgressView() ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white)) .progressViewStyle(CircularProgressViewStyle(tint: .white))
@@ -49,7 +64,9 @@ struct MeView: View {
} else { } else {
Spacer().frame(height: 130) Spacer().frame(height: 130)
} }
// }
//
WithViewStore(self.store, observe: { $0 }) { viewStore in
if viewStore.isLoadingMoments && viewStore.moments.isEmpty { if viewStore.isLoadingMoments && viewStore.moments.isEmpty {
ProgressView("加载中...") ProgressView("加载中...")
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -94,14 +111,14 @@ struct MeView: View {
viewStore.send(.refresh) viewStore.send(.refresh)
} }
} }
Spacer()
} }
.frame(maxWidth: .infinity, alignment: .top) Spacer()
} }
.frame(maxWidth: .infinity, alignment: .top)
} }
.onAppear { }
viewStore.send(.onAppear) .onAppear {
} ViewStore(self.store, observe: { $0 }).send(.onAppear)
} }
} }
} }