Skip to content

Commit 2644cea

Browse files
authored
Merge pull request #117 from boostcampwm-2022/feature/CalendarView
Feature/calendar view cell 선택 기능 추가
2 parents 4727a84 + 763f98f commit 2644cea

File tree

8 files changed

+127
-21
lines changed

8 files changed

+127
-21
lines changed

DailyQuest/DailyQuest/Domain/UseCases/Home/DefaultQuestUseCase.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ extension DefaultQuestUseCase: QuestUseCase {
2525
func update(with quest: Quest) -> Observable<Bool> {
2626
return questsRepository
2727
.update(with: quest)
28+
.do(onSuccess: { quest in
29+
if quest.state {
30+
NotificationCenter.default.post(name: .questStateChanged, object: quest.date)
31+
}
32+
})
2833
.map { _ in true }
2934
.catchAndReturn(false)
3035
.asObservable()

DailyQuest/DailyQuest/Domain/UseCases/Home/HomeCalendarUseCase.swift

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ final class HomeCalendarUseCase: CalendarUseCase {
1515

1616
let currentMonth = BehaviorSubject<Date?>(value: Date())
1717
let completionOfMonths = BehaviorSubject<[[DailyQuestCompletion]]>(value: [[], [], []])
18+
let selectedDate = BehaviorSubject<Date>(value: Date())
19+
let selectedDateCompletion = BehaviorSubject<DailyQuestCompletion?>(value: nil)
1820

1921
init(questsRepository: QuestsRepository) {
2022
self.questsRepository = questsRepository
@@ -65,25 +67,6 @@ final class HomeCalendarUseCase: CalendarUseCase {
6567
})
6668
.disposed(by: disposeBag)
6769
}
68-
}
69-
70-
extension HomeCalendarUseCase {
71-
72-
private func calculateDailyState(_ quests: [Quest]) -> DailyQuestCompletion.State {
73-
guard !quests.isEmpty else {
74-
return .normal
75-
}
76-
77-
let result = quests.reduce(0) { partialResult, quest in
78-
partialResult + (quest.state ? 1 : 0)
79-
}
80-
81-
if result == quests.count {
82-
return .done
83-
} else {
84-
return .notDone(result)
85-
}
86-
}
8770

8871
func setupMonths() {
8972
let currentMonth = try? currentMonth.value()
@@ -106,6 +89,57 @@ extension HomeCalendarUseCase {
10689
.disposed(by: disposeBag)
10790
}
10891

92+
func refreshMontlyCompletion(for date: Date) {
93+
guard
94+
let months = try? self.completionOfMonths.value(),
95+
let index = months.firstIndex(where: { month in
96+
month.contains { dailyQuestCompletion in
97+
(dailyQuestCompletion.state != .hidden)
98+
&& (dailyQuestCompletion.day.startOfDay == date.startOfDay)
99+
}
100+
}),
101+
let reloadMonth = months[index].last?.day.startDayOfCurrentMonth
102+
else {
103+
return
104+
}
105+
106+
fetchAMontlyCompletion(reloadMonth)
107+
.subscribe(onNext: { [weak self] monthlyCompletion in
108+
guard
109+
let self,
110+
var values = try? self.completionOfMonths.value()
111+
else {
112+
return
113+
}
114+
115+
values[index] = monthlyCompletion
116+
117+
self.completionOfMonths.onNext(values)
118+
})
119+
.disposed(by: disposeBag)
120+
}
121+
122+
func selectDate(_ date: Date) {
123+
selectedDate.onNext(date)
124+
}
125+
}
126+
127+
extension HomeCalendarUseCase {
128+
129+
private func calculateDailyState(_ quests: [Quest]) -> DailyQuestCompletion.State {
130+
guard !quests.isEmpty else {
131+
return .normal
132+
}
133+
134+
let filteredQuests = quests.filter { !$0.state }
135+
136+
if filteredQuests.isEmpty {
137+
return .done
138+
} else {
139+
return .notDone(filteredQuests.count)
140+
}
141+
}
142+
109143
private func fetchAMontlyCompletion(_ month: Date?) -> Observable<[DailyQuestCompletion]> {
110144
guard let month = month else { return .empty() }
111145

@@ -116,7 +150,14 @@ extension HomeCalendarUseCase {
116150
return self.questsRepository
117151
.fetch(by: date)
118152
.map { quests -> DailyQuestCompletion in
119-
return DailyQuestCompletion(day: date, state: self.calculateDailyState(quests))
153+
let completion = DailyQuestCompletion(day: date, state: self.calculateDailyState(quests))
154+
155+
if let selectedDate = try? self.selectedDate.value(),
156+
selectedDate.startOfDay == date {
157+
self.selectedDateCompletion.onNext(completion)
158+
}
159+
160+
return completion
120161
}
121162
}
122163
.toArray()

DailyQuest/DailyQuest/Domain/UseCases/Home/Protocols/CalendarUseCase.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ protocol CalendarUseCase {
1313

1414
var currentMonth: BehaviorSubject<Date?> { get }
1515
var completionOfMonths: BehaviorSubject<[[DailyQuestCompletion]]> { get }
16+
var selectedDate: BehaviorSubject<Date> { get }
17+
var selectedDateCompletion: BehaviorSubject<DailyQuestCompletion?> { get }
1618

1719
func fetchNextMontlyCompletion()
1820
func fetchLastMontlyCompletion()
1921
func setupMonths()
22+
func refreshMontlyCompletion(for date: Date)
23+
func selectDate(_ date: Date)
2024
}

DailyQuest/DailyQuest/Presentation/Common/Cells/CalendarCell.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ final class CalendarCell: UICollectionViewCell {
2828
return view
2929
}()
3030

31+
override var isSelected: Bool {
32+
didSet {
33+
if isSelected {
34+
self.circleCheckView.layer.borderWidth = 3.0
35+
self.circleCheckView.layer.borderColor = UIColor.maxDarkYellow.cgColor
36+
} else {
37+
self.circleCheckView.layer.borderWidth = 0.0
38+
self.circleCheckView.layer.borderColor = nil
39+
}
40+
}
41+
}
42+
3143
override init(frame: CGRect) {
3244
super.init(frame: frame)
3345

DailyQuest/DailyQuest/Presentation/Home/ViewController/HomeViewController.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,17 @@ final class HomeViewController: UIViewController {
194194
.compactMap({ $0?.toFormat })
195195
.bind(to: calendarView.yearMonthLabel.rx.text)
196196
.disposed(by: disposableBag)
197+
198+
output
199+
.selectedDateCompletion
200+
.drive(onNext: { [weak self] dailyQuestCompletion in
201+
guard let dailyQuestCompletion else { return }
202+
203+
let indexPath = self?.calendarView.dataSource.indexPath(for: dailyQuestCompletion)
204+
self?.calendarView.monthCollectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
205+
})
206+
.disposed(by: disposableBag)
207+
197208
}
198209

199210
private func bindToQuestHeaderButton() {

DailyQuest/DailyQuest/Presentation/Home/ViewModel/HomeViewModel.swift

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ final class HomeViewModel {
3737
let profileTapResult: Observable<Bool>
3838
let currentMonth: Observable<Date?>
3939
let displayDays: Driver<[[DailyQuestCompletion]]>
40+
let selectedDateCompletion: Driver<DailyQuestCompletion?>
4041
}
4142

4243
func transform(input: Input, disposeBag: DisposeBag) -> Output {
@@ -142,6 +143,12 @@ final class HomeViewModel {
142143
})
143144
.disposed(by: disposeBag)
144145

146+
input.daySelected
147+
.bind { [weak self] date in
148+
self?.calendarUseCase.selectDate(date)
149+
}
150+
.disposed(by: disposeBag)
151+
145152
let currentMonth = calendarUseCase
146153
.currentMonth
147154
.asObserver()
@@ -150,11 +157,32 @@ final class HomeViewModel {
150157
.completionOfMonths
151158
.asDriver(onErrorJustReturn: [[], [], []])
152159

160+
let selectedDateCompletion = calendarUseCase
161+
.selectedDateCompletion
162+
.asDriver(onErrorJustReturn: nil)
163+
164+
notification
165+
.subscribe(onNext: { [weak self] date in
166+
self?.calendarUseCase.setupMonths()
167+
})
168+
.disposed(by: disposeBag)
169+
170+
NotificationCenter
171+
.default
172+
.rx
173+
.notification(.questStateChanged)
174+
.compactMap({ $0.object as? Date })
175+
.subscribe(onNext: { [weak self] date in
176+
self?.calendarUseCase.refreshMontlyCompletion(for: date)
177+
})
178+
.disposed(by: disposeBag)
179+
153180
return Output(data: data,
154181
userData: userData,
155182
questStatus: questStatus,
156183
profileTapResult: profileTapResult,
157184
currentMonth: currentMonth,
158-
displayDays: displayDays)
185+
displayDays: displayDays,
186+
selectedDateCompletion: selectedDateCompletion)
159187
}
160188
}

DailyQuest/DailyQuest/Utils/Date+.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ extension Date {
3939
return Calendar.current.component(.weekday, from: self)
4040
}
4141

42+
var startOfDay: Date {
43+
return Calendar.current.startOfDay(for: self)
44+
}
45+
4246
var nextMonthOfCurrentDay: Date? {
4347
return Calendar.current.date(byAdding: .month, value: 1, to: self)
4448
}

DailyQuest/DailyQuest/Utils/Notification+.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ import Foundation
1010
extension Notification.Name {
1111
static let updated = Notification.Name("updated")
1212
static let userUpdated = Notification.Name("userUpdated")
13+
static let questStateChanged = Notification.Name("questStateChanged")
1314
}

0 commit comments

Comments
 (0)