diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/Book/View/BookViewController.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/Book/View/BookViewController.swift index 459397d9..055acad2 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/Book/View/BookViewController.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/Book/View/BookViewController.swift @@ -35,14 +35,20 @@ final class BookViewController: UIViewController { setup() configureNavigationBar() configureConstraints() - input.send(.viewDidLoad) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + input.send(.loadBook) } // MARK: - Binding private func bind() { let output = viewModel.transform(input: input.eraseToAnyPublisher()) - output.receive(on: DispatchQueue.main) + output + .receive(on: DispatchQueue.main) .sink { [weak self] event in switch event { case .setBookTitle(let bookTitle): @@ -50,6 +56,8 @@ final class BookViewController: UIViewController { case .loadFirstPage(let page): guard let page else { return } self?.configureFirstPageViewController(firstPage: page) + case .moveToEdit(let bookID): + self?.presentEditBookView(bookID: bookID) } } .store(in: &cancellables) @@ -94,8 +102,8 @@ final class BookViewController: UIViewController { title: "수정", normal: normalAttributes, selected: selectedAttributes - ) { - // TODO: - 추후 책 속지 수정 페이지로 넘어가는 로직 필요 + ) { [weak self] in + self?.input.send(.editBook) } } @@ -119,6 +127,18 @@ final class BookViewController: UIViewController { return ReadPageViewController(viewModel: readPageViewModel) } + + // MARK: - PresentEditBookView + private func presentEditBookView(bookID: UUID) { + do { + let editBookViewModelFactory = try DIContainer.shared.resolve(EditBookViewModelFactory.self) + let editBookViewModel = editBookViewModelFactory.make(bookID: bookID) + let editBookViewController = EditBookViewController(viewModel: editBookViewModel, mode: .modify) + navigationController?.pushViewController(editBookViewController, animated: true) + } catch { + MHLogger.error(error) + } + } } // MARK: - UIPageViewControllerDelegate diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/Book/ViewModel/BookViewModel.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/Book/ViewModel/BookViewModel.swift index ca1b4b41..14f7d57f 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/Book/ViewModel/BookViewModel.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/Book/ViewModel/BookViewModel.swift @@ -4,14 +4,16 @@ import Combine public final class BookViewModel: ViewModelType { enum Input { - case viewDidLoad + case loadBook case loadPreviousPage case loadNextPage + case editBook } enum Output { case setBookTitle(with: String?) case loadFirstPage(page: Page?) + case moveToEdit(bookID: UUID) } private let fetchBookUseCase: FetchBookUseCase @@ -32,11 +34,10 @@ public final class BookViewModel: ViewModelType { self.identifier = identifier } - @MainActor func transform(input: AnyPublisher) -> AnyPublisher { input.sink { [weak self] event in switch event { - case .viewDidLoad: + case .loadBook: Task { try await self?.fetchBook() } case .loadPreviousPage: if self?.nowPageIndex ?? 0 > 0 { @@ -46,6 +47,9 @@ public final class BookViewModel: ViewModelType { if self?.nowPageIndex ?? 0 < (self?.book?.pages.count ?? 0) - 1 { self?.nowPageIndex += 1 } + case .editBook: + guard let self else { return } + self.output.send(.moveToEdit(bookID: self.identifier)) } } .store(in: &cancellables) diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/BookCover/View/BookCoverViewController.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/BookCover/View/BookCoverViewController.swift index 97ad31f4..e4f439cf 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/BookCover/View/BookCoverViewController.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/BookCover/View/BookCoverViewController.swift @@ -313,6 +313,7 @@ extension BookCoverViewController { self?.modifyInput.send(.saveBookCover) } } + private func configureAddSubviews() { view.addSubview(bookPreviewViewBackground) view.addSubview(bookTitleTextFieldBackground) @@ -320,6 +321,7 @@ extension BookCoverViewController { view.addSubview(categorySelectionButtonBackground) view.addSubview(imageSelectionButtonBackground) } + private func configureConstraints() { bookPreviewViewBackground.setAnchor( top: view.safeAreaLayoutGuide.topAnchor, @@ -352,6 +354,7 @@ extension BookCoverViewController { height: 63 ) } + private func configureAction() { bookColorButtons.enumerated().forEach { index, button in let colorButtonAction = UIAction { [weak self] _ in @@ -393,6 +396,7 @@ extension BookCoverViewController { } categorySelectionButton.addAction(selectCategoryAction, for: .touchUpInside) } + private func configuredColorButtons() -> UIView { let firstLineColorButtonStackView = UIStackView() firstLineColorButtonStackView.axis = .horizontal diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/View/EditBookViewController.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/View/EditBookViewController.swift index 82179b08..076703fc 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/View/EditBookViewController.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/View/EditBookViewController.swift @@ -5,6 +5,10 @@ import Combine // TODO: - 페이지 없애는 기능 추가 final class EditBookViewController: UIViewController { + enum Mode { + case create + case modify + } // MARK: - Constant static let buttonBottomConstant: CGFloat = -20 @@ -76,16 +80,24 @@ final class EditBookViewController: UIViewController { private let viewModel: EditBookViewModel private let input = PassthroughSubject() private var cancellables = Set() + private let mode: Mode // MARK: - Initializer - init(viewModel: EditBookViewModel) { + init( + viewModel: EditBookViewModel, + mode: Mode = .create + ) { self.viewModel = viewModel + self.mode = mode super.init(nibName: nil, bundle: nil) } + required init?(coder: NSCoder) { guard let viewModel = try? DIContainer.shared.resolve(EditBookViewModelFactory.self) else { return nil } self.viewModel = viewModel.make(bookID: .init()) + self.mode = .create + super.init(coder: coder) } @@ -135,19 +147,28 @@ final class EditBookViewController: UIViewController { alert.addAction(UIAlertAction(title: "취소", style: .cancel)) alert.addAction(UIAlertAction(title: "확인", style: .default) { _ in self?.input.send(.didCancelButtonTapped) - self?.navigationController?.popViewController(animated: true) }) self?.present(alert, animated: true) } // 네비게이션 오른쪽 아이템 - navigationItem.rightBarButtonItem = UIBarButtonItem( - title: "기록 마치기", - normal: normalAttributes, - selected: selectedAttributes - ) { [weak self] in - self?.input.send(.didSaveButtonTapped) - self?.navigationController?.popToRootViewController(animated: true) + switch mode { + case .create: + navigationItem.rightBarButtonItem = UIBarButtonItem( + title: "기록 마치기", + normal: normalAttributes, + selected: selectedAttributes + ) { [weak self] in + self?.input.send(.didSaveButtonTapped) + } + case .modify: + navigationItem.rightBarButtonItem = UIBarButtonItem( + title: "수정 마치기", + normal: normalAttributes, + selected: selectedAttributes + ) { [weak self] in + self?.input.send(.didSaveButtonTapped) + } } } private func configureAddSubView() { @@ -203,12 +224,24 @@ final class EditBookViewController: UIViewController { } private func configureBinding() { let output = viewModel.transform(input: input.eraseToAnyPublisher()) - output.receive(on: DispatchQueue.main) + + output + .receive(on: DispatchQueue.main) .sink { [weak self] event in switch event { - case let .updateViewController(title): + case .updateViewController(let title): self?.navigationItem.title = title self?.editPageTableView.reloadData() + case .saveDone: + guard let self else { return } + switch self.mode { + case .create: + self.navigationController?.popToRootViewController(animated: true) + case .modify: + self.navigationController?.popViewController(animated: true) + } + case .revokeDone: + self?.navigationController?.popViewController(animated: true) case .error(message: let message): MHLogger.error(message) // TODO: - Alert 띄우기 } diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/ViewModel/EditBookViewModel.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/ViewModel/EditBookViewModel.swift index db5650a1..1f8ff43b 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/ViewModel/EditBookViewModel.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/EditBook/ViewModel/EditBookViewModel.swift @@ -15,6 +15,8 @@ final class EditBookViewModel: ViewModelType { } enum Output { case updateViewController(title: String) + case saveDone + case revokeDone case error(message: String) } @@ -135,6 +137,7 @@ final class EditBookViewModel: ViewModelType { do { try await updateBookUseCase.execute(id: bookID, book: book) try await storeMediaUseCase.execute(to: bookID, mediaList: mediaList) + output.send(.saveDone) } catch { output.send(.error(message: "책을 저장하는데 실패했습니다.")) MHLogger.error(error.localizedDescription + #function) @@ -144,6 +147,7 @@ final class EditBookViewModel: ViewModelType { private func revokeMediaAll() async { do { try await storeMediaUseCase.execute(to: bookID, mediaList: nil) + output.send(.revokeDone) } catch { output.send(.error(message: "저장 취소하는데 실패했습니다.")) MHLogger.error(error.localizedDescription + #function) @@ -154,6 +158,7 @@ final class EditBookViewModel: ViewModelType { func numberOfPages() -> Int { return editPageViewModels.count } + func editPageViewModel(at index: Int) -> EditPageViewModel { let editPageViewModel = editPageViewModels[index] diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/View/ReadPageViewController.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/View/ReadPageViewController.swift index 9070adbb..480af65b 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/View/ReadPageViewController.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/View/ReadPageViewController.swift @@ -57,7 +57,9 @@ final class ReadPageViewController: UIViewController { private func bind() { let output = viewModel.transform(input: input.eraseToAnyPublisher()) - output.sink { [weak self] event in + output + .receive(on: DispatchQueue.main) + .sink { [weak self] event in switch event { case .loadPage(let page): guard let page else { return } @@ -68,8 +70,7 @@ final class ReadPageViewController: UIViewController { // TODO: Alert 띄우기 ? MHLogger.error(message) } - } - .store(in: &cancellables) + }.store(in: &cancellables) } // MARK: - Setup & Configure diff --git a/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/ViewModel/ReadPageViewModel.swift b/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/ViewModel/ReadPageViewModel.swift index d9a58d33..2293f4eb 100644 --- a/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/ViewModel/ReadPageViewModel.swift +++ b/MemorialHouse/MHPresentation/MHPresentation/Source/ReadPage/ViewModel/ReadPageViewModel.swift @@ -45,7 +45,6 @@ public final class ReadPageViewModel: ViewModelType { return output.eraseToAnyPublisher() } - @MainActor private func loadMediaForData(media: MediaDescription) async { do { let mediaData: Data = try await fetchMediaUseCase.execute(media: media, in: bookID)