diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultQuestUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultQuestUseCase.swift index 6faa9ac..974e063 100644 --- a/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultQuestUseCase.swift +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultQuestUseCase.swift @@ -25,6 +25,11 @@ extension DefaultQuestUseCase: QuestUseCase { func update(with quest: Quest) -> Observable { return questsRepository .update(with: quest) + .do(onSuccess: { quest in + if quest.state { + NotificationCenter.default.post(name: .questStateChanged, object: quest.date) + } + }) .map { _ in true } .catchAndReturn(false) .asObservable() diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/HomeCalendarUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/HomeCalendarUseCase.swift index dbc9714..d4eb06e 100644 --- a/DailyQuest/DailyQuest/Domain/UseCases/Home/HomeCalendarUseCase.swift +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/HomeCalendarUseCase.swift @@ -15,6 +15,8 @@ final class HomeCalendarUseCase: CalendarUseCase { let currentMonth = BehaviorSubject(value: Date()) let completionOfMonths = BehaviorSubject<[[DailyQuestCompletion]]>(value: [[], [], []]) + let selectedDate = BehaviorSubject(value: Date()) + let selectedDateCompletion = BehaviorSubject(value: nil) init(questsRepository: QuestsRepository) { self.questsRepository = questsRepository @@ -65,25 +67,6 @@ final class HomeCalendarUseCase: CalendarUseCase { }) .disposed(by: disposeBag) } -} - -extension HomeCalendarUseCase { - - private func calculateDailyState(_ quests: [Quest]) -> DailyQuestCompletion.State { - guard !quests.isEmpty else { - return .normal - } - - let result = quests.reduce(0) { partialResult, quest in - partialResult + (quest.state ? 1 : 0) - } - - if result == quests.count { - return .done - } else { - return .notDone(result) - } - } func setupMonths() { let currentMonth = try? currentMonth.value() @@ -106,6 +89,57 @@ extension HomeCalendarUseCase { .disposed(by: disposeBag) } + func refreshMontlyCompletion(for date: Date) { + guard + let months = try? self.completionOfMonths.value(), + let index = months.firstIndex(where: { month in + month.contains { dailyQuestCompletion in + (dailyQuestCompletion.state != .hidden) + && (dailyQuestCompletion.day.startOfDay == date.startOfDay) + } + }), + let reloadMonth = months[index].last?.day.startDayOfCurrentMonth + else { + return + } + + fetchAMontlyCompletion(reloadMonth) + .subscribe(onNext: { [weak self] monthlyCompletion in + guard + let self, + var values = try? self.completionOfMonths.value() + else { + return + } + + values[index] = monthlyCompletion + + self.completionOfMonths.onNext(values) + }) + .disposed(by: disposeBag) + } + + func selectDate(_ date: Date) { + selectedDate.onNext(date) + } +} + +extension HomeCalendarUseCase { + + private func calculateDailyState(_ quests: [Quest]) -> DailyQuestCompletion.State { + guard !quests.isEmpty else { + return .normal + } + + let filteredQuests = quests.filter { !$0.state } + + if filteredQuests.isEmpty { + return .done + } else { + return .notDone(filteredQuests.count) + } + } + private func fetchAMontlyCompletion(_ month: Date?) -> Observable<[DailyQuestCompletion]> { guard let month = month else { return .empty() } @@ -116,7 +150,14 @@ extension HomeCalendarUseCase { return self.questsRepository .fetch(by: date) .map { quests -> DailyQuestCompletion in - return DailyQuestCompletion(day: date, state: self.calculateDailyState(quests)) + let completion = DailyQuestCompletion(day: date, state: self.calculateDailyState(quests)) + + if let selectedDate = try? self.selectedDate.value(), + selectedDate.startOfDay == date { + self.selectedDateCompletion.onNext(completion) + } + + return completion } } .toArray() diff --git a/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/CalendarUseCase.swift b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/CalendarUseCase.swift index a1199d4..5adfdbf 100644 --- a/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/CalendarUseCase.swift +++ b/DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/CalendarUseCase.swift @@ -13,8 +13,12 @@ protocol CalendarUseCase { var currentMonth: BehaviorSubject { get } var completionOfMonths: BehaviorSubject<[[DailyQuestCompletion]]> { get } + var selectedDate: BehaviorSubject { get } + var selectedDateCompletion: BehaviorSubject { get } func fetchNextMontlyCompletion() func fetchLastMontlyCompletion() func setupMonths() + func refreshMontlyCompletion(for date: Date) + func selectDate(_ date: Date) } diff --git a/DailyQuest/DailyQuest/Presentation/Common/Cells/CalendarCell.swift b/DailyQuest/DailyQuest/Presentation/Common/Cells/CalendarCell.swift index 741273b..2969bb6 100644 --- a/DailyQuest/DailyQuest/Presentation/Common/Cells/CalendarCell.swift +++ b/DailyQuest/DailyQuest/Presentation/Common/Cells/CalendarCell.swift @@ -28,6 +28,18 @@ final class CalendarCell: UICollectionViewCell { return view }() + override var isSelected: Bool { + didSet { + if isSelected { + self.circleCheckView.layer.borderWidth = 3.0 + self.circleCheckView.layer.borderColor = UIColor.maxDarkYellow.cgColor + } else { + self.circleCheckView.layer.borderWidth = 0.0 + self.circleCheckView.layer.borderColor = nil + } + } + } + override init(frame: CGRect) { super.init(frame: frame) diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift index 7ba0fcd..da24a1e 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift @@ -194,6 +194,17 @@ final class HomeViewController: UIViewController { .compactMap({ $0?.toFormat }) .bind(to: calendarView.yearMonthLabel.rx.text) .disposed(by: disposableBag) + + output + .selectedDateCompletion + .drive(onNext: { [weak self] dailyQuestCompletion in + guard let dailyQuestCompletion else { return } + + let indexPath = self?.calendarView.dataSource.indexPath(for: dailyQuestCompletion) + self?.calendarView.monthCollectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally) + }) + .disposed(by: disposableBag) + } private func bindToQuestHeaderButton() { diff --git a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift index 83411c3..1af1698 100644 --- a/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift +++ b/DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift @@ -37,6 +37,7 @@ final class HomeViewModel { let profileTapResult: Observable let currentMonth: Observable let displayDays: Driver<[[DailyQuestCompletion]]> + let selectedDateCompletion: Driver } func transform(input: Input, disposeBag: DisposeBag) -> Output { @@ -142,6 +143,12 @@ final class HomeViewModel { }) .disposed(by: disposeBag) + input.daySelected + .bind { [weak self] date in + self?.calendarUseCase.selectDate(date) + } + .disposed(by: disposeBag) + let currentMonth = calendarUseCase .currentMonth .asObserver() @@ -150,11 +157,32 @@ final class HomeViewModel { .completionOfMonths .asDriver(onErrorJustReturn: [[], [], []]) + let selectedDateCompletion = calendarUseCase + .selectedDateCompletion + .asDriver(onErrorJustReturn: nil) + + notification + .subscribe(onNext: { [weak self] date in + self?.calendarUseCase.setupMonths() + }) + .disposed(by: disposeBag) + + NotificationCenter + .default + .rx + .notification(.questStateChanged) + .compactMap({ $0.object as? Date }) + .subscribe(onNext: { [weak self] date in + self?.calendarUseCase.refreshMontlyCompletion(for: date) + }) + .disposed(by: disposeBag) + return Output(data: data, userData: userData, questStatus: questStatus, profileTapResult: profileTapResult, currentMonth: currentMonth, - displayDays: displayDays) + displayDays: displayDays, + selectedDateCompletion: selectedDateCompletion) } } diff --git a/DailyQuest/DailyQuest/Utils/Date+.swift b/DailyQuest/DailyQuest/Utils/Date+.swift index dc783f9..e9afae1 100644 --- a/DailyQuest/DailyQuest/Utils/Date+.swift +++ b/DailyQuest/DailyQuest/Utils/Date+.swift @@ -39,6 +39,10 @@ extension Date { return Calendar.current.component(.weekday, from: self) } + var startOfDay: Date { + return Calendar.current.startOfDay(for: self) + } + var nextMonthOfCurrentDay: Date? { return Calendar.current.date(byAdding: .month, value: 1, to: self) } diff --git a/DailyQuest/DailyQuest/Utils/Notification+.swift b/DailyQuest/DailyQuest/Utils/Notification+.swift index 4de23b2..b1142cd 100644 --- a/DailyQuest/DailyQuest/Utils/Notification+.swift +++ b/DailyQuest/DailyQuest/Utils/Notification+.swift @@ -10,4 +10,5 @@ import Foundation extension Notification.Name { static let updated = Notification.Name("updated") static let userUpdated = Notification.Name("userUpdated") + static let questStateChanged = Notification.Name("questStateChanged") }