Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@ import Combine
import UIKit
import SnapKit

enum EmotionEditViewEvent {
case editButtonTapped
case emotionDidChange(Emotion?)
}

final class EmotionEditView: BaseView {
private let scrollView = UIScrollView()
private let contentView = UIView()
private let emotionRegistrationView = EmotionRegistrationView()
private let editButton = BKButton(style: .primary, size: .large)

private let editButtonTappedSubject = PassthroughSubject<Void, Never>()
var editButtonTappedPublisher: AnyPublisher<Void, Never> {
editButtonTappedSubject.eraseToAnyPublisher()
}

private let getCurrentEmotionSubject = PassthroughSubject<Void, Never>()
var getCurrentEmotionPublisher: AnyPublisher<Void, Never> {
getCurrentEmotionSubject.eraseToAnyPublisher()
}
let eventPublisher = PassthroughSubject<EmotionEditViewEvent, Never>()
private var cancellables = Set<AnyCancellable>()

var selectedEmotion: Emotion? {
private var currentSelectedEmotion: Emotion? {
guard let form = emotionRegistrationView.registrationForm(),
case .emotion(let emotionForm) = form else { return nil }
return emotionForm.emotion
Expand All @@ -37,6 +35,15 @@ final class EmotionEditView: BaseView {
override func configure() {
editButton.title = "수정하기"
editButton.addTarget(self, action: #selector(editButtonTapped), for: .touchUpInside)

emotionRegistrationView.inputChangedPublisher
.sink { [weak self] _ in
guard let self = self else { return }

let newEmotion = self.currentSelectedEmotion
self.eventPublisher.send(.emotionDidChange(newEmotion))
}
.store(in: &cancellables)
}

override func setupLayout() {
Expand Down Expand Up @@ -69,12 +76,12 @@ final class EmotionEditView: BaseView {
emotionRegistrationView.setSelectedEmotion(emotion)
}

func getCurrentSelectedEmotion() {
getCurrentEmotionSubject.send(())
@objc private func editButtonTapped() {
eventPublisher.send(.editButtonTapped)
}

@objc private func editButtonTapped() {
editButtonTappedSubject.send(())
func setEditButtonEnabled(_ isEnabled: Bool) {
editButton.isDisabled = !isEnabled
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,41 @@ final class EmotionEditViewController: BaseViewController<EmotionEditView> {
weak var coordinator: NoteEditCoordinator?
private var cancellables = Set<AnyCancellable>()

private let currentEmotion: Emotion?
private let initialEmotion: Emotion?
@Published private var selectedEmotion: Emotion?
private let completion: (Emotion) -> Void

init(currentEmotion: Emotion?, completion: @escaping (Emotion) -> Void) {
self.currentEmotion = currentEmotion
self.initialEmotion = currentEmotion
self.completion = completion
self._selectedEmotion = .init(initialValue: currentEmotion)
super.init()
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

// 현재 선택된 감정이 있으면 초기 설정
if let emotion = currentEmotion {
if let emotion = initialEmotion {
contentView.setSelectedEmotion(emotion)
}
contentView.setEditButtonEnabled(false)
}

override func bindAction() {
contentView.editButtonTappedPublisher
.compactMap { [weak self] in self?.contentView.selectedEmotion }
.sink { [weak self] selectedEmotion in
self?.completion(selectedEmotion)
self?.navigationController?.popViewController(animated: true)
contentView.eventPublisher
.sink { [weak self] event in
guard let self = self else { return }
switch event {
case .emotionDidChange(let newEmotion):
self.selectedEmotion = newEmotion
let isDiff = (newEmotion != self.initialEmotion)
self.contentView.setEditButtonEnabled(isDiff)
case .editButtonTapped:
if let emotion = self.selectedEmotion {
self.completion(emotion)
self.navigationController?.popViewController(animated: true)
}
}
}
.store(in: &cancellables)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import UIKit
enum NoteEditViewEvent {
case emotionStatusTapped
case saveButtonTapped
case pageDidChange(String)
case sentenceDidChange(String)
case appreciationDidChange(String)
}

final class NoteEditView: BaseView {
Expand Down Expand Up @@ -110,6 +113,26 @@ final class NoteEditView: BaseView {
emotionStatusView.isUserInteractionEnabled = true

// BKTextFieldView와 BKTextView는 자체적으로 탭을 처리하므로 별도 제스처 불필요
pageField.textDidChangePublisher
.sink { [weak self] _ in
guard let self = self else { return }
self.eventPublisher.send(.pageDidChange(self.pageField.text))
}
.store(in: &cancellables)

sentenceTextView.textDidChangePublisher
.sink { [weak self] _ in
guard let self = self else { return }
self.eventPublisher.send(.sentenceDidChange(self.sentenceTextView.text))
}
.store(in: &cancellables)

appreciationTextView.textDidChangePublisher
.sink { [weak self] _ in
guard let self = self else { return }
self.eventPublisher.send(.appreciationDidChange(self.appreciationTextView.text))
}
.store(in: &cancellables)

// 전체 뷰에 탭 제스처 추가 (키보드 dismiss용)
let dismissTapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
Expand Down Expand Up @@ -156,6 +179,7 @@ final class NoteEditView: BaseView {
target: self,
selector: #selector(pageFieldDidBeginEditing)
)

}

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

public func setSaveButtonEnabled(_ isEnabled: Bool) {
saveButton.isDisabled = !isEnabled
}

func getCurrentFormData() -> (page: Int?, sentence: String, appreciation: String) {
let pageText = pageField.text.trimmingCharacters(in: .whitespacesAndNewlines)
let sentenceText = sentenceTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,31 @@ final class NoteEditViewController: BaseViewController<NoteEditView>, ScreenLogg
}
.store(in: &cancellables)

viewModel.statePublisher
.map { $0.isDiff }
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] isDiff in
self?.contentView.setSaveButtonEnabled(isDiff)
}
.store(in: &cancellables)
}

override func bindAction() {
contentView.eventPublisher
.sink { [weak self] event in
guard let self = self else { return }
switch event {
case .emotionStatusTapped:
self?.presentEmotionEdit()
self.presentEmotionEdit()
case .saveButtonTapped:
self?.handleSaveButtonTapped()
self.handleSaveButtonTapped()
case .pageDidChange(let text):
self.viewModel.send(.pageDidChange(text))
case .sentenceDidChange(let text):
self.viewModel.send(.sentenceDidChange(text))
case .appreciationDidChange(let text):
self.viewModel.send(.appreciationDidChange(text))
}
}
.store(in: &cancellables)
Expand All @@ -153,8 +168,7 @@ private extension NoteEditViewController {
}

func handleSaveButtonTapped() {
let formData = contentView.getCurrentFormData()
viewModel.send(.saveButtonTapped(formData: formData))
viewModel.send(.saveButtonTapped)
}

func presentBookMoreMenu() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ final class NoteEditViewModel: BaseViewModel {
var shouldPresentEmotionEdit: (emotion: Emotion?, timestamp: Date)?
var saveCompleted: Bool = false
var deleteCompleted: Bool = false

var currentFormData: (page: String, sentence: String, appreciation: String) = ("", "", "")

var initialRecordInfo: RecordInfo?
var initialSelectedEmotion: Emotion?
var isDiff: Bool = false // 변경 내용이 있는지 추적
}

enum Action {
Expand All @@ -23,10 +29,15 @@ final class NoteEditViewModel: BaseViewModel {
case errorHandled
case presentEmotionEdit
case emotionSelected(Emotion)
case saveButtonTapped(formData: (page: Int?, sentence: String, appreciation: String?))

case saveButtonTapped
case patchRecordSuccessed(RecordInfo)
case deleteButtonTapped
case deleteRecordSuccessed

case pageDidChange(String)
case sentenceDidChange(String)
case appreciationDidChange(String)
}

enum SideEffect {
Expand Down Expand Up @@ -68,16 +79,31 @@ final class NoteEditViewModel: BaseViewModel {

switch action {
case .onAppear:
guard state.initialRecordInfo == nil else {
break
}
newState.isLoading = true
newState.isDiff = false
effects.append(.fetchRecordDetail(recordId))

case .fetchRecordDetailSuccessed(let recordInfo):
newState.recordInfo = recordInfo
newState.initialRecordInfo = recordInfo

newState.currentFormData = (
page: "\(recordInfo.pageNumber)",
sentence: recordInfo.quote,
appreciation: recordInfo.review ?? ""
)

// 사용자가 이미 감정을 선택했다면 덮어쓰지 않음
if newState.selectedEmotion == nil {
newState.selectedEmotion = recordInfo.emotionTags.first
let initialEmotion = recordInfo.emotionTags.first
newState.selectedEmotion = initialEmotion
newState.initialSelectedEmotion = initialEmotion
}
newState.isLoading = false
newState.isDiff = false

case .errorOccured(let error):
newState.error = error
Expand All @@ -91,28 +117,41 @@ final class NoteEditViewModel: BaseViewModel {

case .emotionSelected(let emotion):
newState.selectedEmotion = emotion
newState.isDiff = checkForDiff(state: newState)

case .saveButtonTapped(let formData):
case .saveButtonTapped:
guard let selectedEmotion = state.selectedEmotion,
let page = formData.page,
!formData.sentence.isEmpty else {
let page = Int(state.currentFormData.page),
!state.currentFormData.sentence.isEmpty else {
break
}

// 감상평이 비어있으면 nil, 아니면 텍스트 전달
let appreciation = state.currentFormData.appreciation.isEmpty
? nil
: state.currentFormData.appreciation

let noteForm = NoteForm(
page: page,
sentence: formData.sentence,
sentence: state.currentFormData.sentence,
emotion: selectedEmotion,
appreciation: formData.appreciation
appreciation: appreciation
)

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

case .patchRecordSuccessed(let recordInfo):
newState.recordInfo = recordInfo
newState.initialRecordInfo = recordInfo
newState.currentFormData = (
page: "\(recordInfo.pageNumber)",
sentence: recordInfo.quote,
appreciation: recordInfo.review ?? ""
)
newState.isLoading = false
newState.saveCompleted = true
newState.isDiff = false

case .deleteButtonTapped:
newState.isLoading = true
Expand All @@ -121,6 +160,18 @@ final class NoteEditViewModel: BaseViewModel {
case .deleteRecordSuccessed:
newState.isLoading = false
newState.deleteCompleted = true

case .pageDidChange(let text):
newState.currentFormData.page = text
newState.isDiff = checkForDiff(state: newState)

case .sentenceDidChange(let text):
newState.currentFormData.sentence = text
newState.isDiff = checkForDiff(state: newState)

case .appreciationDidChange(let text):
newState.currentFormData.appreciation = text
newState.isDiff = checkForDiff(state: newState)
}

return (newState, effects)
Expand Down Expand Up @@ -159,5 +210,19 @@ final class NoteEditViewModel: BaseViewModel {
.sink(receiveValue: send(_:))
.store(in: &cancellables)
}

private func checkForDiff(state: State) -> Bool {
guard let initialInfo = state.initialRecordInfo else {
return false
}

let pageDiff = state.currentFormData.page != "\(initialInfo.pageNumber)"
let sentenceDiff = state.currentFormData.sentence != initialInfo.quote
let appreciationDiff = state.currentFormData.appreciation != (initialInfo.review ?? "")

let emotionDiff = state.selectedEmotion != state.initialSelectedEmotion

return pageDiff || sentenceDiff || appreciationDiff || emotionDiff
}
}