Skip to content

Commit aa8e77c

Browse files
committed
[BOOK-421] feat: 독서 기록 수정사항 여부에 따른 버튼 활성화 상태 변경
1 parent de0cff9 commit aa8e77c

3 files changed

Lines changed: 116 additions & 11 deletions

File tree

src/Projects/BKPresentation/Sources/MainFlow/NoteEdit/View/NoteEditView.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import UIKit
99
enum NoteEditViewEvent {
1010
case emotionStatusTapped
1111
case saveButtonTapped
12+
case pageDidChange(String)
13+
case sentenceDidChange(String)
14+
case appreciationDidChange(String)
1215
}
1316

1417
final class NoteEditView: BaseView {
@@ -110,6 +113,26 @@ final class NoteEditView: BaseView {
110113
emotionStatusView.isUserInteractionEnabled = true
111114

112115
// BKTextFieldView와 BKTextView는 자체적으로 탭을 처리하므로 별도 제스처 불필요
116+
pageField.textDidChangePublisher
117+
.sink { [weak self] _ in
118+
guard let self = self else { return }
119+
self.eventPublisher.send(.pageDidChange(self.pageField.text))
120+
}
121+
.store(in: &cancellables)
122+
123+
sentenceTextView.textDidChangePublisher
124+
.sink { [weak self] _ in
125+
guard let self = self else { return }
126+
self.eventPublisher.send(.sentenceDidChange(self.sentenceTextView.text))
127+
}
128+
.store(in: &cancellables)
129+
130+
appreciationTextView.textDidChangePublisher
131+
.sink { [weak self] _ in
132+
guard let self = self else { return }
133+
self.eventPublisher.send(.appreciationDidChange(self.appreciationTextView.text))
134+
}
135+
.store(in: &cancellables)
113136

114137
// 전체 뷰에 탭 제스처 추가 (키보드 dismiss용)
115138
let dismissTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
@@ -156,6 +179,7 @@ final class NoteEditView: BaseView {
156179
target: self,
157180
selector: #selector(pageFieldDidBeginEditing)
158181
)
182+
159183
}
160184

161185
@objc private func sentenceTextViewDidBeginEditing(_ notification: Notification) {
@@ -270,6 +294,10 @@ final class NoteEditView: BaseView {
270294
emotionLabel.setText(text: text)
271295
}
272296

297+
public func setSaveButtonEnabled(_ isEnabled: Bool) {
298+
saveButton.isDisabled = !isEnabled
299+
}
300+
273301
func getCurrentFormData() -> (page: Int?, sentence: String, appreciation: String) {
274302
let pageText = pageField.text.trimmingCharacters(in: .whitespacesAndNewlines)
275303
let sentenceText = sentenceTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)

src/Projects/BKPresentation/Sources/MainFlow/NoteEdit/View/NoteEditViewController.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ final class NoteEditViewController: BaseViewController<NoteEditView>, ScreenLogg
4242
)
4343
navigationItem.leftBarButtonItem = backButton
4444

45+
contentView.setSaveButtonEnabled(false)
4546
viewModel.send(.onAppear)
4647
}
4748

@@ -121,16 +122,31 @@ final class NoteEditViewController: BaseViewController<NoteEditView>, ScreenLogg
121122
}
122123
.store(in: &cancellables)
123124

125+
viewModel.statePublisher
126+
.map { $0.isDiff }
127+
.removeDuplicates()
128+
.receive(on: DispatchQueue.main)
129+
.sink { [weak self] isDiff in
130+
self?.contentView.setSaveButtonEnabled(isDiff)
131+
}
132+
.store(in: &cancellables)
124133
}
125134

126135
override func bindAction() {
127136
contentView.eventPublisher
128137
.sink { [weak self] event in
138+
guard let self = self else { return }
129139
switch event {
130140
case .emotionStatusTapped:
131-
self?.presentEmotionEdit()
141+
self.presentEmotionEdit()
132142
case .saveButtonTapped:
133-
self?.handleSaveButtonTapped()
143+
self.handleSaveButtonTapped()
144+
case .pageDidChange(let text):
145+
self.viewModel.send(.pageDidChange(text))
146+
case .sentenceDidChange(let text):
147+
self.viewModel.send(.sentenceDidChange(text))
148+
case .appreciationDidChange(let text):
149+
self.viewModel.send(.appreciationDidChange(text))
134150
}
135151
}
136152
.store(in: &cancellables)
@@ -153,8 +169,7 @@ private extension NoteEditViewController {
153169
}
154170

155171
func handleSaveButtonTapped() {
156-
let formData = contentView.getCurrentFormData()
157-
viewModel.send(.saveButtonTapped(formData: formData))
172+
viewModel.send(.saveButtonTapped)
158173
}
159174

160175
func presentBookMoreMenu() {

src/Projects/BKPresentation/Sources/MainFlow/NoteEdit/ViewModel/NoteEditViewModel.swift

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ final class NoteEditViewModel: BaseViewModel {
1414
var shouldPresentEmotionEdit: (emotion: Emotion?, timestamp: Date)?
1515
var saveCompleted: Bool = false
1616
var deleteCompleted: Bool = false
17+
18+
var currentFormData: (page: String, sentence: String, appreciation: String) = ("", "", "")
19+
20+
var initialRecordInfo: RecordInfo?
21+
var initialSelectedEmotion: Emotion?
22+
var isDiff: Bool = false // 변경 내용이 있는지 추적
1723
}
1824

1925
enum Action {
@@ -23,10 +29,15 @@ final class NoteEditViewModel: BaseViewModel {
2329
case errorHandled
2430
case presentEmotionEdit
2531
case emotionSelected(Emotion)
26-
case saveButtonTapped(formData: (page: Int?, sentence: String, appreciation: String?))
32+
33+
case saveButtonTapped
2734
case patchRecordSuccessed(RecordInfo)
2835
case deleteButtonTapped
2936
case deleteRecordSuccessed
37+
38+
case pageDidChange(String)
39+
case sentenceDidChange(String)
40+
case appreciationDidChange(String)
3041
}
3142

3243
enum SideEffect {
@@ -69,15 +80,27 @@ final class NoteEditViewModel: BaseViewModel {
6980
switch action {
7081
case .onAppear:
7182
newState.isLoading = true
83+
newState.isDiff = false
7284
effects.append(.fetchRecordDetail(recordId))
7385

7486
case .fetchRecordDetailSuccessed(let recordInfo):
7587
newState.recordInfo = recordInfo
88+
newState.initialRecordInfo = recordInfo
89+
90+
newState.currentFormData = (
91+
page: "\(recordInfo.pageNumber)",
92+
sentence: recordInfo.quote,
93+
appreciation: recordInfo.review ?? ""
94+
)
95+
7696
// 사용자가 이미 감정을 선택했다면 덮어쓰지 않음
7797
if newState.selectedEmotion == nil {
78-
newState.selectedEmotion = recordInfo.emotionTags.first
98+
let initialEmotion = recordInfo.emotionTags.first
99+
newState.selectedEmotion = initialEmotion
100+
newState.initialSelectedEmotion = initialEmotion
79101
}
80102
newState.isLoading = false
103+
newState.isDiff = false
81104

82105
case .errorOccured(let error):
83106
newState.error = error
@@ -91,28 +114,41 @@ final class NoteEditViewModel: BaseViewModel {
91114

92115
case .emotionSelected(let emotion):
93116
newState.selectedEmotion = emotion
117+
newState.isDiff = checkForDiff(state: newState)
94118

95-
case .saveButtonTapped(let formData):
119+
case .saveButtonTapped:
96120
guard let selectedEmotion = state.selectedEmotion,
97-
let page = formData.page,
98-
!formData.sentence.isEmpty else {
121+
let page = Int(state.currentFormData.page),
122+
!state.currentFormData.sentence.isEmpty else {
99123
break
100124
}
101125

126+
// 감상평이 비어있으면 nil, 아니면 텍스트 전달
127+
let appreciation = state.currentFormData.appreciation.isEmpty
128+
? nil
129+
: state.currentFormData.appreciation
130+
102131
let noteForm = NoteForm(
103132
page: page,
104-
sentence: formData.sentence,
133+
sentence: state.currentFormData.sentence,
105134
emotion: selectedEmotion,
106-
appreciation: formData.appreciation
135+
appreciation: appreciation
107136
)
108137

109138
newState.isLoading = true
110139
effects.append(.patchRecord(recordId, noteForm))
111140

112141
case .patchRecordSuccessed(let recordInfo):
113142
newState.recordInfo = recordInfo
143+
newState.initialRecordInfo = recordInfo
144+
newState.currentFormData = (
145+
page: "\(recordInfo.pageNumber)",
146+
sentence: recordInfo.quote,
147+
appreciation: recordInfo.review ?? ""
148+
)
114149
newState.isLoading = false
115150
newState.saveCompleted = true
151+
newState.isDiff = false
116152

117153
case .deleteButtonTapped:
118154
newState.isLoading = true
@@ -121,6 +157,18 @@ final class NoteEditViewModel: BaseViewModel {
121157
case .deleteRecordSuccessed:
122158
newState.isLoading = false
123159
newState.deleteCompleted = true
160+
161+
case .pageDidChange(let text):
162+
newState.currentFormData.page = text
163+
newState.isDiff = checkForDiff(state: newState)
164+
165+
case .sentenceDidChange(let text):
166+
newState.currentFormData.sentence = text
167+
newState.isDiff = checkForDiff(state: newState)
168+
169+
case .appreciationDidChange(let text):
170+
newState.currentFormData.appreciation = text
171+
newState.isDiff = checkForDiff(state: newState)
124172
}
125173

126174
return (newState, effects)
@@ -159,5 +207,19 @@ final class NoteEditViewModel: BaseViewModel {
159207
.sink(receiveValue: send(_:))
160208
.store(in: &cancellables)
161209
}
210+
211+
private func checkForDiff(state: State) -> Bool {
212+
guard let initialInfo = state.initialRecordInfo else {
213+
return false
214+
}
215+
216+
let pageDiff = state.currentFormData.page != "\(initialInfo.pageNumber)"
217+
let sentenceDiff = state.currentFormData.sentence != initialInfo.quote
218+
let appreciationDiff = state.currentFormData.appreciation != (initialInfo.review ?? "")
219+
220+
let emotionDiff = state.selectedEmotion != state.initialSelectedEmotion
221+
222+
return pageDiff || sentenceDiff || appreciationDiff || emotionDiff
223+
}
162224
}
163225

0 commit comments

Comments
 (0)