Skip to content

Commit

Permalink
Merge pull request #114 from boostcampwm-2024/feature/edit-bookcover-…
Browse files Browse the repository at this point in the history
…in-home

홈 화면의 책 드랍다운 버튼 클릭 시 UIMenu 로직 구현
  • Loading branch information
Kyxxn authored Dec 2, 2024
2 parents bf7fb5e + 8760a3b commit a505387
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
UpdateBookCoverUseCase.self,
object: DefaultUpdateBookCoverUseCase(repository: bookCoverRepository)
)
DIContainer.shared.register(
DeleteBookCoverUseCase.self,
object: DefaultDeleteBookCoverUseCase(repository: bookCoverRepository)
)
}

private func registerViewModelFactoryDependency() throws {
Expand All @@ -201,12 +205,14 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let fetchMemorialHouseNameUseCase = try DIContainer.shared.resolve(FetchMemorialHouseNameUseCase.self)
let fetchAllBookCoverUseCase = try DIContainer.shared.resolve(FetchAllBookCoverUseCase.self)
let updateBookCoverUseCase = try DIContainer.shared.resolve(UpdateBookCoverUseCase.self)
let deleteBookCoverUseCase = try DIContainer.shared.resolve(DeleteBookCoverUseCase.self)
DIContainer.shared.register(
HomeViewModelFactory.self,
object: HomeViewModelFactory(
fetchMemorialHouseNameUseCase: fetchMemorialHouseNameUseCase,
fetchAllBookCoverUseCase: fetchAllBookCoverUseCase,
updateBookCoverUseCase: updateBookCoverUseCase
updateBookCoverUseCase: updateBookCoverUseCase,
deleteBookCoverUseCase: deleteBookCoverUseCase
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ extension CoreDataBookCoverStorage: BookCoverStorage {
}
}

// TODO: 책 커버 삭제 시, 책 내용 모두 삭제되게끔 수정 필요
public func delete(with id: UUID) async -> Result<Void, MHDataError> {
return await coreDataStorage.performDatabaseTask { [weak self] context in
guard let entity = try self?.getEntityByIdentifier(in: context, with: id) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class BookCreationViewController: UIViewController {
}()
private let bookColorButtons: [UIButton] = zip(
["분 홍", "초 록", "파 랑", "주 황", "베이지", ""],
[UIColor.mhPink, .mhGreen, .mhBlue, .mhOrange, .mhBeige, .clear]
[.mhPink, .mhGreen, .mhBlue, .mhOrange, .mhBeige, .clear]
).map { (title: String, color: UIColor) in
let button = UIButton(frame: CGRect(origin: .zero, size: .init(width: 66, height: 30)))
var attributedTitle = AttributedString(stringLiteral: title)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import UIKit
final class MHRegisterView: UIView {
// MARK: UI Components
let registerTextField: UITextField = {
let registerFont = UIFont.ownglyphBerry(size: 12)
let registerFont = UIFont.ownglyphBerry(size: 20)

let textField = UITextField()
textField.font = registerFont
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ final class BookCollectionViewCell: UICollectionViewCell {
}

// MARK: - Configuration
func configure(
func configureCell(
id: UUID,
title: String,
bookCoverImage: UIImage,
targetImage: UIImage,
isLike: Bool,
houseName: String,
bookCoverAction: @escaping () -> Void,
likeButtonAction: @escaping () -> Void
houseName: String
) {
self.isLike = isLike
bookCoverView.configure(
Expand All @@ -49,15 +47,13 @@ final class BookCollectionViewCell: UICollectionViewCell {
)
changeLikeButtonImage(isLike: isLike)
dropDownButton.setImage(.dotHorizontal, for: .normal)
configureAction(
bookCoverAction: bookCoverAction,
likeButtonAction: likeButtonAction
)
}

private func configureAction(
func configureButtonAction(
bookCoverAction: @escaping () -> Void,
likeButtonAction: @escaping () -> Void
likeButtonAction: @escaping () -> Void,
dropDownButtonEditAction: @escaping () -> Void,
dropDownButtonDeleteAction: @escaping () -> Void
) {
bookCoverView.addAction(UIAction { _ in
bookCoverAction()
Expand All @@ -70,9 +66,23 @@ final class BookCollectionViewCell: UICollectionViewCell {
self.changeLikeButtonImage(isLike: self.isLike)
}, for: .touchUpInside)

dropDownButton.addAction(UIAction { _ in
// TODO: 드랍다운 액션 추가
}, for: .touchUpInside)
dropDownButton.showsMenuAsPrimaryAction = true
dropDownButton.menu = UIMenu(
title: "",
children: [
UIAction(
title: "책 커버 수정",
image: UIImage(systemName: "pencil"),
handler: { _ in dropDownButtonEditAction() }
),
UIAction(
title: "책 커버 삭제",
image: UIImage(systemName: "trash"),
attributes: .destructive,
handler: { _ in dropDownButtonDeleteAction() }
)
]
)
}

private func changeLikeButtonImage(isLike: Bool) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public final class HomeViewController: UIViewController {
let flowLayout = UICollectionViewFlowLayout()
let cellWidth = (self.view.bounds.inset(by: self.view.safeAreaInsets).width - 80) / 2
flowLayout.itemSize = .init(width: cellWidth, height: cellWidth * 1.5)
flowLayout.minimumLineSpacing = 40
flowLayout.minimumLineSpacing = 25
flowLayout.minimumInteritemSpacing = 20
flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 30, right: 20)
flowLayout.scrollDirection = .vertical
Expand Down Expand Up @@ -158,9 +158,7 @@ public final class HomeViewController: UIViewController {
}, for: .touchUpInside)

makingBookFloatingButton.addAction(UIAction { [weak self] _ in
guard let self else { return }
let bookCreationViewController = BookCreationViewController(viewModel: BookCreationViewModel())
self.navigationController?.pushViewController(bookCreationViewController, animated: true)
self?.moveMakingBookViewController()
}, for: .touchUpInside)

navigationBar.configureSettingAction(action: UIAction { [weak self] _ in
Expand All @@ -171,6 +169,11 @@ public final class HomeViewController: UIViewController {
})
}

private func moveMakingBookViewController() {
let bookCreationViewController = BookCreationViewController(viewModel: BookCreationViewModel())
navigationController?.pushViewController(bookCreationViewController, animated: true)
}

private func configureConstraints() {
navigationBar.setAnchor(
top: view.safeAreaLayoutGuide.topAnchor, constantTop: 20,
Expand Down Expand Up @@ -262,18 +265,26 @@ extension HomeViewController: UICollectionViewDataSource {
// TODO: Image Loader 필요 & 메모리 캐싱 필요

let bookCover = viewModel.currentBookCovers[indexPath.item]
cell.configure(
cell.configureCell(
id: bookCover.id,
title: bookCover.title,
bookCoverImage: bookCover.color.image,
targetImage: UIImage(systemName: "person")!,
isLike: bookCover.favorite,
houseName: viewModel.houseName,
houseName: viewModel.houseName
)
cell.configureButtonAction(
bookCoverAction: { [weak self] in
self?.bookCoverTapped(indexPath: indexPath)
},
likeButtonAction: { [weak self] in
self?.input.send(.likeButtonTapped(bookId: bookCover.id))
},
dropDownButtonEditAction: { [weak self] in
self?.moveMakingBookViewController()
},
dropDownButtonDeleteAction: { [weak self] in
self?.input.send(.deleteBookCover(bookId: bookCover.id))
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public final class HomeViewModel: ViewModelType {
case selectedCategory(category: String)
case dragAndDropBookCover(currentIndex: Int, destinationIndex: Int)
case likeButtonTapped(bookId: UUID)
case deleteBookCover(bookId: UUID)
}

public enum Output: Equatable {
Expand All @@ -23,6 +24,7 @@ public final class HomeViewModel: ViewModelType {
private let fetchMemorialHouseNameUseCase: FetchMemorialHouseNameUseCase
private let fetchAllBookCoverUseCase: FetchAllBookCoverUseCase
private let updateBookCoverUseCase: UpdateBookCoverUseCase
private let deleteBookCoverUseCase: DeleteBookCoverUseCase
private var cancellables = Set<AnyCancellable>()
private(set) var houseName = ""
private(set) var bookCovers = [BookCover]()
Expand All @@ -31,11 +33,13 @@ public final class HomeViewModel: ViewModelType {
public init(
fetchMemorialHouseUseCase: FetchMemorialHouseNameUseCase,
fetchAllBookCoverUseCase: FetchAllBookCoverUseCase,
updateBookCoverUseCase: UpdateBookCoverUseCase
updateBookCoverUseCase: UpdateBookCoverUseCase,
deleteBookCoverUseCase: DeleteBookCoverUseCase
) {
self.fetchMemorialHouseNameUseCase = fetchMemorialHouseUseCase
self.fetchAllBookCoverUseCase = fetchAllBookCoverUseCase
self.updateBookCoverUseCase = updateBookCoverUseCase
self.deleteBookCoverUseCase = deleteBookCoverUseCase
}

@MainActor
Expand All @@ -44,46 +48,91 @@ public final class HomeViewModel: ViewModelType {
switch event {
case .viewDidLoad:
Task {
do {
try await self?.fetchMemorialHouse()
try await self?.fetchAllBookCover()
} catch {
self?.output.send(.fetchedFailure("데이터 로드 중 에러가 발생했습니다."))
MHLogger.error("데이터 로드 에러 발생: \(error.localizedDescription)")
}
await self?.fetchMemorialHouse()
await self?.fetchAllBookCover()
}
case .selectedCategory(let category):
self?.filterBooks(by: category)
case .dragAndDropBookCover(let currentIndex, let destinationIndex):
self?.dragAndDropBookCover(from: currentIndex, to: destinationIndex)
case .likeButtonTapped(let bookId):
Task {
do {
try await self?.likeButtonTapped(bookId: bookId)
} catch {
self?.output.send(.fetchedFailure("좋아요에 실패했습니다."))
MHLogger.error("좋아요 에러 발생: \(error.localizedDescription)")
}
}
Task { await self?.likeButtonTapped(bookId: bookId) }
case .deleteBookCover(let bookId):
Task { await self?.deleteBookCover(bookId: bookId) }
}
}.store(in: &cancellables)

return output.eraseToAnyPublisher()
}

@MainActor
private func fetchMemorialHouse() async throws {
let memorialHouseName = try await fetchMemorialHouseNameUseCase.execute()
houseName = memorialHouseName
output.send(.fetchedMemorialHouseName)
private func fetchMemorialHouse() async {
do {
let memorialHouseName = try await fetchMemorialHouseNameUseCase.execute()
houseName = memorialHouseName
output.send(.fetchedMemorialHouseName)
} catch {
output.send(.fetchedFailure("MemorialHouseName 로드 중 에러가 발생했습니다."))
MHLogger.error("MemorialHouseName 로드 에러 발생: \(error.localizedDescription)")
}
}

@MainActor
private func fetchAllBookCover() async throws {
let bookCovers = try await fetchAllBookCoverUseCase.execute()
self.bookCovers = bookCovers
self.currentBookCovers = bookCovers
output.send(.fetchedAllBookCover)
private func fetchAllBookCover() async {
do {
let bookCovers = try await fetchAllBookCoverUseCase.execute()
self.bookCovers = bookCovers
self.currentBookCovers = bookCovers
output.send(.fetchedAllBookCover)
} catch {
output.send(.fetchedFailure("책들을 불러오는 중에 에러가 발생했습니다."))
MHLogger.error("책들을 불러오는 중에 에러 발생: \(error.localizedDescription)")
}
}

@MainActor
private func likeButtonTapped(bookId: UUID) async {
guard
let bookCoverIndex = bookCovers.firstIndex(where: { $0.id == bookId }),
let currentBookCoverindex = currentBookCovers.firstIndex(where: { $0.id == bookId })
else { return }

let currentBookCover = currentBookCovers[currentBookCoverindex]
let bookCover = BookCover(
id: currentBookCover.id,
order: currentBookCover.order,
title: currentBookCover.title,
imageURL: currentBookCover.category,
color: currentBookCover.color,
category: currentBookCover.imageURL,
favorite: !currentBookCover.favorite
)

do {
try await updateBookCoverUseCase.execute(id: bookId, with: bookCover)
bookCovers[bookCoverIndex] = bookCover
currentBookCovers[currentBookCoverindex] = bookCover
} catch {
output.send(.fetchedFailure("좋아요에 실패했습니다."))
MHLogger.error("좋아요 에러 발생: \(error.localizedDescription)")
}
}

@MainActor
private func deleteBookCover(bookId: UUID) async {
do {
try await deleteBookCoverUseCase.execute(id: bookId)
guard
let bookCoverIndex = bookCovers.firstIndex(where: { $0.id == bookId }),
let currentBookCoverIndex = currentBookCovers.firstIndex(where: { $0.id == bookId })
else { return }

bookCovers.remove(at: bookCoverIndex)
currentBookCovers.remove(at: currentBookCoverIndex)
} catch {
MHLogger.error("삭제 에러 발생: \(error.localizedDescription)")
output.send(.fetchedFailure("삭제에 실패했습니다."))
}
}

private func filterBooks(by category: String) {
Expand All @@ -108,25 +157,4 @@ public final class HomeViewModel: ViewModelType {
currentBookCovers.insert(currentBookCover, at: destinationIndex)
output.send(.dragAndDropFinished)
}

private func likeButtonTapped(bookId: UUID) async throws {
guard
let bookCoverIndex = bookCovers.firstIndex(where: { $0.id == bookId }),
let currentBookCoverindex = currentBookCovers.firstIndex(where: { $0.id == bookId })
else { return }

let currentBookCover = currentBookCovers[currentBookCoverindex]
let bookCover = BookCover(
id: currentBookCover.id,
order: currentBookCover.order,
title: currentBookCover.title,
imageURL: currentBookCover.category,
color: currentBookCover.color,
category: currentBookCover.imageURL,
favorite: !currentBookCover.favorite
)
try await updateBookCoverUseCase.execute(id: bookId, with: bookCover)
bookCovers[bookCoverIndex] = bookCover
currentBookCovers[currentBookCoverindex] = bookCover
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@ public struct HomeViewModelFactory {
let fetchMemorialHouseNameUseCase: FetchMemorialHouseNameUseCase
let fetchAllBookCoverUseCase: FetchAllBookCoverUseCase
let updateBookCoverUseCase: UpdateBookCoverUseCase
let deleteBookCoverUseCase: DeleteBookCoverUseCase

public init(
fetchMemorialHouseNameUseCase: FetchMemorialHouseNameUseCase,
fetchAllBookCoverUseCase: FetchAllBookCoverUseCase,
updateBookCoverUseCase: UpdateBookCoverUseCase
updateBookCoverUseCase: UpdateBookCoverUseCase,
deleteBookCoverUseCase: DeleteBookCoverUseCase
) {
self.fetchMemorialHouseNameUseCase = fetchMemorialHouseNameUseCase
self.fetchAllBookCoverUseCase = fetchAllBookCoverUseCase
self.updateBookCoverUseCase = updateBookCoverUseCase
self.deleteBookCoverUseCase = deleteBookCoverUseCase
}

public func make() -> HomeViewModel {
HomeViewModel(
fetchMemorialHouseUseCase: fetchMemorialHouseNameUseCase,
fetchAllBookCoverUseCase: fetchAllBookCoverUseCase,
updateBookCoverUseCase: updateBookCoverUseCase
updateBookCoverUseCase: updateBookCoverUseCase,
deleteBookCoverUseCase: deleteBookCoverUseCase
)
}
}
Loading

0 comments on commit a505387

Please sign in to comment.