Skip to content

Commit 8702a5d

Browse files
authored
Merge pull request #380 from Team-return/feature/(#379)-Mypage_Detail_Refactoring
🔗 :: (#379) 마이페이지 상세 ReactorKit
2 parents b8d06ab + 64036a1 commit 8702a5d

18 files changed

+588
-557
lines changed

Projects/Domain/Sources/Enums/DevelopmentType.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,15 @@ public enum DevelopmentType: String, Codable {
2525
return "iOS"
2626
}
2727
}
28+
29+
public init?(localizedString: String) {
30+
switch localizedString {
31+
case "전체", "All": self = .all
32+
case "서버", "Server": self = .server
33+
case "", "Web": self = .web
34+
case "안드로이드", "Android": self = .android
35+
case "iOS": self = .ios
36+
default: return nil
37+
}
38+
}
2839
}

Projects/Flow/Sources/MyPage/BugReport/BugReportFlow.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private extension BugReportFlow {
3333
func navigateToBugReport() -> FlowContributors {
3434
return .one(flowContributor: .contribute(
3535
withNextPresentable: rootViewController,
36-
withNextStepper: rootViewController.viewModel
36+
withNextStepper: rootViewController.reactor
3737
))
3838
}
3939

@@ -42,7 +42,7 @@ private extension BugReportFlow {
4242
Flows.use(majorBottomSheetFlow, when: .created) { root in
4343
let view = root as? MajorBottomSheetViewController
4444
view?.dismiss = { majorType in
45-
self.rootViewController.viewModel.majorType.accept(majorType)
45+
self.rootViewController.reactor.action.onNext(.updateMajorType(majorType))
4646
}
4747
self.rootViewController.present(
4848
root,

Projects/Flow/Sources/MyPage/InterestField/InterestFieldCheckFlow.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public final class InterestFieldCheckFlow: Flow {
2828
return navigateToInterestFieldCheck()
2929

3030
case .popHomeFieldIsRequired:
31-
return navigateToInterestField()
31+
return popToMyPage()
3232
}
3333
}
3434
}
@@ -37,7 +37,7 @@ private extension InterestFieldCheckFlow {
3737
func navigateToInterestField() -> FlowContributors {
3838
return .one(flowContributor: .contribute(
3939
withNextPresentable: rootViewController,
40-
withNextStepper: rootViewController.viewModel
40+
withNextStepper: rootViewController.reactor
4141
))
4242
}
4343

@@ -51,11 +51,12 @@ private extension InterestFieldCheckFlow {
5151

5252
return .one(flowContributor: .contribute(
5353
withNextPresentable: interestFieldCheckViewController,
54-
withNextStepper: interestFieldCheckViewController.viewModel
54+
withNextStepper: interestFieldCheckViewController.reactor
5555
))
5656
}
5757

58-
// func navigateToHomeField() -> FlowContributors {
59-
//
60-
// }
58+
func popToMyPage() -> FlowContributors {
59+
rootViewController.navigationController?.popToRootViewController(animated: true)
60+
return .none
61+
}
6162
}

Projects/Flow/Sources/MyPage/InterestField/InterestFieldFlow.swift

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,31 @@ public final class InterestFieldFlow: Flow {
1818
}
1919

2020
public func navigate(to step: Step) -> FlowContributors {
21-
guard let step = step as? InterestFieldStep else { return .none }
22-
23-
switch step {
24-
case .interestFieldIsRequired:
25-
return navigateToInterestField()
26-
case .interestFieldCheckIsRequired:
27-
return navigateToInterestFieldCheck()
21+
if let step = step as? InterestFieldStep {
22+
switch step {
23+
case .interestFieldIsRequired:
24+
return navigateToInterestField()
25+
case .interestFieldCheckIsRequired:
26+
return navigateToInterestFieldCheck()
27+
}
28+
} else if let step = step as? InterestFieldCheckStep {
29+
switch step {
30+
case .popHomeFieldIsRequired:
31+
return popToMyPage()
32+
default:
33+
return .none
34+
}
2835
}
36+
37+
return .none
2938
}
3039
}
3140

3241
private extension InterestFieldFlow {
3342
func navigateToInterestField() -> FlowContributors {
3443
return .one(flowContributor: .contribute(
3544
withNextPresentable: rootViewController,
36-
withNextStepper: rootViewController.viewModel
45+
withNextStepper: rootViewController.reactor
3746
))
3847
}
3948

@@ -43,7 +52,12 @@ private extension InterestFieldFlow {
4352

4453
return .one(flowContributor: .contribute(
4554
withNextPresentable: interestFieldCheckViewController,
46-
withNextStepper: interestFieldCheckViewController.viewModel
55+
withNextStepper: interestFieldCheckViewController.reactor
4756
))
4857
}
58+
59+
func popToMyPage() -> FlowContributors {
60+
rootViewController.navigationController?.popToRootViewController(animated: true)
61+
return .none
62+
}
4963
}

Projects/Flow/Sources/MyPage/Notification/NotificationSettingFlow.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private extension NotificationSettingFlow {
3030
func navigateToNotificationSetting() -> FlowContributors {
3131
return .one(flowContributor: .contribute(
3232
withNextPresentable: rootViewController,
33-
withNextStepper: rootViewController.viewModel
33+
withNextStepper: rootViewController.reactor
3434
))
3535
}
3636
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import ReactorKit
2+
import RxSwift
3+
import RxCocoa
4+
import RxFlow
5+
import Core
6+
import Domain
7+
8+
public final class BugReportReactor: BaseReactor, Stepper {
9+
public let steps = PublishRelay<Step>()
10+
public let initialState: State
11+
private let reportBugUseCase: ReportBugUseCase
12+
13+
public init(
14+
reportBugUseCase: ReportBugUseCase
15+
) {
16+
self.initialState = .init()
17+
self.reportBugUseCase = reportBugUseCase
18+
}
19+
20+
public enum Action {
21+
case updateTitle(String)
22+
case updateContent(String)
23+
case updateImageList([String])
24+
case updateMajorType(String)
25+
case majorViewDidTap
26+
case bugReportButtonDidTap
27+
}
28+
29+
public enum Mutation {
30+
case setTitle(String)
31+
case setContent(String)
32+
case setImageList([String])
33+
case setMajorType(String)
34+
case setBugReportButtonIsEnabled(Bool)
35+
case setBugReportCompleted
36+
}
37+
38+
public struct State {
39+
var title: String = ""
40+
var content: String = ""
41+
var imageList: [String] = []
42+
var majorType: String = "전체"
43+
var isBugReportButtonEnabled: Bool = false
44+
var isBugReportCompleted: Bool = false
45+
}
46+
}
47+
48+
extension BugReportReactor {
49+
public func mutate(action: Action) -> Observable<Mutation> {
50+
switch action {
51+
case let .updateTitle(title):
52+
let isEnabled = !title.isEmpty && !currentState.content.isEmpty
53+
return .concat([
54+
.just(.setTitle(title)),
55+
.just(.setBugReportButtonIsEnabled(isEnabled))
56+
])
57+
58+
case let .updateContent(content):
59+
let isEnabled = !currentState.title.isEmpty && !content.isEmpty
60+
return .concat([
61+
.just(.setContent(content)),
62+
.just(.setBugReportButtonIsEnabled(isEnabled))
63+
])
64+
65+
case let .updateImageList(imageList):
66+
return .just(.setImageList(imageList))
67+
68+
case let .updateMajorType(majorType):
69+
return .just(.setMajorType(majorType))
70+
71+
case .majorViewDidTap:
72+
steps.accept(BugReportStep.majorBottomSheetIsRequired)
73+
return .empty()
74+
75+
case .bugReportButtonDidTap:
76+
return reportBugUseCase.execute(req: .init(
77+
title: currentState.title,
78+
content: currentState.content,
79+
developmentArea: DevelopmentType(localizedString: currentState.majorType) ?? .all,
80+
attachmentUrls: currentState.imageList
81+
))
82+
.asObservable()
83+
.map { _ in Mutation.setBugReportCompleted }
84+
}
85+
}
86+
87+
public func reduce(state: State, mutation: Mutation) -> State {
88+
var newState = state
89+
switch mutation {
90+
case let .setTitle(title):
91+
newState.title = title
92+
93+
case let .setContent(content):
94+
newState.content = content
95+
96+
case let .setImageList(imageList):
97+
newState.imageList = imageList
98+
99+
case let .setMajorType(majorType):
100+
newState.majorType = majorType
101+
102+
case let .setBugReportButtonIsEnabled(isEnabled):
103+
newState.isBugReportButtonEnabled = isEnabled
104+
105+
case .setBugReportCompleted:
106+
newState.isBugReportCompleted = true
107+
}
108+
return newState
109+
}
110+
}

Projects/Presentation/Sources/BugReport/BugReportViewController.swift

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,9 @@ import Core
77
import DesignSystem
88
import PhotosUI
99

10-
public final class BugReportViewController: BaseViewController<BugReportViewModel> {
11-
private let bugReportButtonDidTap = PublishRelay<Void>()
12-
private let bugReportImageList = BehaviorRelay<[String]>(value: [])
10+
public final class BugReportViewController: BaseReactorViewController<BugReportReactor> {
1311
var imageStringList: [String] = []
1412
var imageList: [UIImage] = []
15-
var titleBool: Bool = false
16-
var contentBool: Bool = false
1713

1814
private let bugReportMajorView = BugReportMajorView()
1915
private let bugReportTitleTextField = JobisTextField().then {
@@ -154,27 +150,41 @@ public final class BugReportViewController: BaseViewController<BugReportViewMode
154150
}
155151
}
156152

157-
public override func bind() {
158-
let input = BugReportViewModel.Input(
159-
title: bugReportTitleTextField.textField.rx.text.orEmpty.asDriver(),
160-
content: bugReportContentTextView.textView.rx.text.orEmpty.asDriver(),
161-
bugReportImageList: bugReportImageList,
162-
majorViewDidTap: self.bugReportMajorView.majorViewDidTap,
163-
bugReportButtonDidTap: self.bugReportButtonDidTap
164-
)
153+
public override func bindAction() {
154+
bugReportTitleTextField.textField.rx.text.orEmpty
155+
.map { BugReportReactor.Action.updateTitle($0) }
156+
.bind(to: reactor.action)
157+
.disposed(by: disposeBag)
165158

166-
let output = viewModel.transform(input)
159+
bugReportContentTextView.textView.rx.text.orEmpty
160+
.map { BugReportReactor.Action.updateContent($0) }
161+
.bind(to: reactor.action)
162+
.disposed(by: disposeBag)
167163

168-
output.bugReportButtonIsEnable.asObservable()
169-
.bind { [weak self] isEnable in
170-
self?.bugReportButton.isEnabled = isEnable
171-
}
164+
bugReportMajorView.majorViewDidTap
165+
.map { BugReportReactor.Action.majorViewDidTap }
166+
.bind(to: reactor.action)
172167
.disposed(by: disposeBag)
173168

174-
output.majorType.asObservable()
175-
.subscribe(onNext: {
176-
self.bugReportMajorView.majorLabel.text = $0
169+
bugReportButton.rx.tap
170+
.do(onNext: { [weak self] in
171+
guard let self = self else { return }
172+
self.reactor.action.onNext(.updateImageList(self.imageStringList))
177173
})
174+
.map { BugReportReactor.Action.bugReportButtonDidTap }
175+
.bind(to: reactor.action)
176+
.disposed(by: disposeBag)
177+
}
178+
179+
public override func bindState() {
180+
reactor.state.map { $0.isBugReportButtonEnabled }
181+
.distinctUntilChanged()
182+
.bind(to: bugReportButton.rx.isEnabled)
183+
.disposed(by: disposeBag)
184+
185+
reactor.state.map { $0.majorType }
186+
.distinctUntilChanged()
187+
.bind(to: bugReportMajorView.majorLabel.rx.text)
178188
.disposed(by: disposeBag)
179189
}
180190

@@ -194,12 +204,12 @@ public final class BugReportViewController: BaseViewController<BugReportViewMode
194204
})
195205
.disposed(by: disposeBag)
196206

197-
bugReportButton.rx.tap.asObservable()
198-
.subscribe(onNext: {
199-
self.bugReportImageList.accept(self.imageStringList)
200-
self.bugReportButtonDidTap.accept(())
201-
self.showJobisToast(text: "버그제보가 완료되었습니다.", inset: 70)
202-
self.navigationController?.popViewController(animated: true)
207+
reactor.state.map { $0.isBugReportCompleted }
208+
.distinctUntilChanged()
209+
.filter { $0 }
210+
.bind(onNext: { [weak self] _ in
211+
self?.showJobisToast(text: "버그제보가 완료되었습니다.", inset: 70)
212+
self?.navigationController?.popViewController(animated: true)
203213
})
204214
.disposed(by: disposeBag)
205215
}

0 commit comments

Comments
 (0)