diff --git a/yana/Features/MainFeature.swift b/yana/Features/MainFeature.swift index 00b1afd..67d2ad5 100644 --- a/yana/Features/MainFeature.swift +++ b/yana/Features/MainFeature.swift @@ -12,6 +12,15 @@ struct MainFeature: Reducer { var feedList: FeedListFeature.State = .init() var me: MeFeature.State = .init() var accountModel: AccountModel? = nil + // 新增:导航路径和设置页面 State + var navigationPath: [Destination] = [] + var settingState: SettingFeature.State? = nil + } + + // 新增:导航目标 + enum Destination: Hashable, Equatable { + case setting + case test } @CasePathable @@ -21,6 +30,11 @@ struct MainFeature: Reducer { case feedList(FeedListFeature.Action) case me(MeFeature.Action) case accountModelLoaded(AccountModel?) + // 新增:导航相关 + case navigationPathChanged([Destination]) + case settingButtonTapped + case settingAction(SettingFeature.Action) + case testButtonTapped } var body: some ReducerOf { @@ -39,6 +53,7 @@ struct MainFeature: Reducer { } case .selectTab(let tab): state.selectedTab = tab + state.navigationPath = [] if tab == .other, let uidStr = state.accountModel?.uid, let uid = Int(uidStr), uid > 0 { state.me = MeFeature.State(uid: uid) return .send(.me(.onAppear)) @@ -49,9 +64,34 @@ struct MainFeature: Reducer { case let .accountModelLoaded(accountModel): state.accountModel = accountModel return .none + case .me(.settingButtonTapped): + // 触发 push 到设置页 + state.settingState = SettingFeature.State() + state.navigationPath.append(.setting) + return .none case .me: 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() + } } } diff --git a/yana/Features/MeFeature.swift b/yana/Features/MeFeature.swift index c653f0c..4d9ece5 100644 --- a/yana/Features/MeFeature.swift +++ b/yana/Features/MeFeature.swift @@ -25,6 +25,8 @@ struct MeFeature { case loadMore case userInfoResponse(Result) case momentsResponse(Result) + // 设置按钮点击 + case settingButtonTapped } func reduce(into state: inout State, action: Action) -> Effect { @@ -84,6 +86,9 @@ struct MeFeature { state.momentsError = error.localizedDescription } return .none + case .settingButtonTapped: + // 交由 MainFeature 处理 + return .none } } diff --git a/yana/Views/MainView.swift b/yana/Views/MainView.swift index 81ddec8..c144a64 100644 --- a/yana/Views/MainView.swift +++ b/yana/Views/MainView.swift @@ -6,7 +6,7 @@ struct MainView: View { var body: some View { WithViewStore(self.store, observe: { $0 }) { viewStore in - NavigationStack { + NavigationStack(path: viewStore.binding(get: \.navigationPath, send: MainFeature.Action.navigationPathChanged)) { GeometryReader { geometry in ZStack { // 背景图片 @@ -36,6 +36,21 @@ struct MainView: View { } } .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 { Spacer() @@ -47,10 +62,39 @@ struct MainView: View { .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 { viewStore.send(.onAppear) } } } +} + +struct TestPushView: View { + var body: some View { + ZStack { + Color.blue.ignoresSafeArea() + Text("Test Push View") + .font(.largeTitle) + .foregroundColor(.white) + } + } } diff --git a/yana/Views/MeView.swift b/yana/Views/MeView.swift index d54e005..2ad7d68 100644 --- a/yana/Views/MeView.swift +++ b/yana/Views/MeView.swift @@ -5,17 +5,32 @@ struct MeView: View { let store: StoreOf var body: some View { - WithViewStore(self.store, observe: { $0 }) { viewStore in - GeometryReader { geometry in - ZStack { - Image("bg") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .clipped() - .ignoresSafeArea(.all) - VStack(spacing: 0) { - // 用户信息区域 + GeometryReader { geometry in + ZStack { + Image("bg") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .clipped() + .ignoresSafeArea(.all) + 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 { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) @@ -49,7 +64,9 @@ struct MeView: View { } else { Spacer().frame(height: 130) } - // 动态内容区域 + } + // 动态内容区域 + WithViewStore(self.store, observe: { $0 }) { viewStore in if viewStore.isLoadingMoments && viewStore.moments.isEmpty { ProgressView("加载中...") .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -94,14 +111,14 @@ struct MeView: View { 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) } } }