Skip to content

Commit

Permalink
[Feat] TrainerMainTab Feature, View 작성
Browse files Browse the repository at this point in the history
  • Loading branch information
FpRaArNkK committed Feb 5, 2025
1 parent b26ce8b commit 151cefb
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public struct TraineeMainTabView: View {

BottomTabBar()
}
.edgesIgnoringSafeArea(.bottom)
.ignoresSafeArea(.all, edges: .bottom)
}

// MARK: Section
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// TrainerMainTabFeature.swift
// Presentation
//
// Created by 박민서 on 2/4/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

@Reducer
public struct TrainerMainTabFeature {
@ObservableState
public enum State: Equatable {
case home(TrainerHomeFeature.State)
case feedback
case traineeList
case myPage(TrainerMypageFeature.State)

/// state case와 tabinfo 연결
var tabInfo: TrainerTabInfo {
switch self {
case .home:
return .home
case .feedback:
return .feedback
case .traineeList:
return .traineeList
case .myPage:
return .mypage
}
}

public init() {
self = .home(TrainerHomeFeature.State())
}
}

public enum Action: Sendable, ViewAction {
/// 뷰에서 일어나는 액션을 처리합니다.
case view(View)
/// 하위 화면에서 일어나는 액션을 처리합니다
case subFeature(SubFeatureAction)
/// 화면 네비게이션 설정
case setNavigating(RoutingScreen)

@CasePathable
public enum View: Sendable {
case selectTab(TrainerTabInfo)
}

@CasePathable
public enum SubFeatureAction: Sendable {
/// 홈 화면에서 발생하는 액션 처리
case homeAction(TrainerHomeFeature.Action)
/// 피드백 화면에서 발생하는 액션 처리
case feedbackAction
/// 회원 목록 화면에서 발생하는 액션 처리
case traineeListAction
/// 마이페이지 화면에서 발생하는 액션 처리
case myPageAction(TrainerMypageFeature.Action)
}
}

public init() {}

public var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .view(view):
switch view {
case .selectTab(let tab):
switch tab {
case .home:
state = .home(.init())
return .none
case .feedback:
// TODO: feedback Feature 작성 후 추가해주세요
state = .feedback
return .none
case .traineeList:
// TODO: traineeList Feature 작성 후 추가해주세요
state = .traineeList
return .none
case .mypage:
state = .myPage(.init())
return .none
}
}

case .subFeature(let internalAction):
// TODO: Feature에 RoutingScreen 추가 후 추가해주세요
switch internalAction {
default:
return .none
}
case .setNavigating(let screen):
return .none
}
}
// .ifCaseLet cases in here
}
}

extension TrainerMainTabFeature {
/// 하위 화면에서 파생되는 라우팅을 전달합니다
// TODO: Feature에 RoutingScreen 추가 후 추가해주세요
public enum RoutingScreen: Sendable {
/// 트레이너 홈
case trainerHome
/// 트레이너 피드백
case trainerFeedback
/// 트레이너 회원 목록
case trainerTraineeList
/// 트레이너 마이페이지
case trainerMyPage
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// TrainerMainTabView.swift
// Presentation
//
// Created by 박민서 on 2/4/25.
// Copyright © 2025 yapp25thTeamTnT. All rights reserved.
//

import SwiftUI
import ComposableArchitecture

import DesignSystem

@ViewAction(for: TrainerMainTabFeature.self)
public struct TrainerMainTabView: View {
@Bindable public var store: StoreOf<TrainerMainTabFeature>

public init(store: StoreOf<TrainerMainTabFeature>) {
self.store = store
}

public var body: some View {
VStack(spacing: 0) {
switch store.state {
case .home:
if let store = store.scope(state: \.home, action: \.subFeature.homeAction) {
TrainerHomeView(store: store)
}
case .feedback:
if let store = store.scope(state: \.feedback, action: \.subFeature.feedbackAction) {
Color.clear
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
case .traineeList:
if let store = store.scope(state: \.traineeList, action: \.subFeature.traineeListAction) {
Color.clear
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
case .myPage:
if let store = store.scope(state: \.myPage, action: \.subFeature.myPageAction) {
TrainerMypageView(store: store)
}
}

BottomTabBar()
}
.ignoresSafeArea(.all, edges: .bottom)
}

// MARK: Section
@ViewBuilder
private func BottomTabBar() -> some View {
HStack(alignment: .top) {
ForEach(TrainerTabInfo.allCases, id: \.hashValue) { tab in
Spacer()
TabButton(
icon: store.state.tabInfo == tab ? tab.filledIcn : tab.emptyIcn,
text: tab.rawValue,
action: { send(.selectTab(tab))}
)
.frame(maxHeight: .infinity, alignment: .top)
Spacer()
}
}
.frame(height: 54 + .safeAreaBottom)
.padding(.horizontal, 24)
.background(Color.white.shadow(radius: 5).opacity(0.5))
}
}

private extension TrainerMainTabView {
struct TabButton: View {
let icon: ImageResource
let text: String
let action: () -> Void

var body: some View {
Button(action: action) {
VStack(spacing: 4) {
Image(icon)
.resizable()
.frame(width: 24, height: 24)
Text(text)
.typographyStyle(.label2Bold, with: .neutral900)
}
}
.padding(.vertical, 8)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ public struct TrainerMypageFeature {
var view_isPopUpPresented: Bool = false

public init(
userName: String,
userName: String = "",
userImageUrl: String? = nil,
studentCount: Int,
oldStudentCount: Int,
appPushNotificationAllowed: Bool,
versionInfo: String,
studentCount: Int = 0,
oldStudentCount: Int = 0,
appPushNotificationAllowed: Bool = false,
versionInfo: String = "",
view_popUp: PopUp? = nil,
view_isPopUpPresented: Bool = false
) {
Expand Down

0 comments on commit 151cefb

Please sign in to comment.