-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathHomeManager.swift
182 lines (132 loc) · 5.65 KB
/
HomeManager.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Kevin Li - 4:13 PM - 7/2/20
import Combine
import ElegantCalendar
import SwiftUI
class HomeManager: ObservableObject {
@Published var scrollState: PageScrollState = .init()
@Published var appTheme: AppTheme = ._white
@Published var calendarTheme: CalendarTheme = ._white
let visitsProvider: VisitsProvider
let listScrollState: ListScrollState
let yearlyCalendarManager: YearlyCalendarManager
let monthlyCalendarManager: MonthlyCalendarManager
private var anyCancellable: AnyCancellable?
init(visits: [Visit]) {
visitsProvider = VisitsProvider(visits: visits)
listScrollState = ListScrollState(
descendingDayComponents: visitsProvider.descendingDayComponents)
let configuration = CalendarConfiguration(
ascending: false,
startDate: visitsProvider.descendingDayComponents.last!.date,
endDate: visitsProvider.descendingDayComponents.first!.date)
yearlyCalendarManager = YearlyCalendarManager(configuration: configuration)
monthlyCalendarManager = MonthlyCalendarManager(configuration: configuration)
configureDelegatesAndPublishers()
}
private func configureDelegatesAndPublishers() {
listScrollState.delegate = self
monthlyCalendarManager.datasource = self
monthlyCalendarManager.delegate = self
monthlyCalendarManager.communicator = self
yearlyCalendarManager.communicator = self
scrollState
.onPageChanged { [weak self] page in
guard let `self` = self else { return }
if page == .yearlyCalendar {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
self.yearlyCalendarManager.scrollToYear(self.monthlyCalendarManager.currentMonth)
}
}
}
anyCancellable = scrollState.objectWillChange.sink { _ in
self.objectWillChange.send()
}
}
}
extension HomeManager: MonthlyCalendarDataSource {
func calendar(backgroundColorOpacityForDate date: Date) -> Double {
let visitCount = visitsProvider.visitsForDayComponents[date.dateComponents]?.count ?? minVisitCount
return Double(visitCount) / Double(maxVisitCount)
}
func calendar(viewForSelectedDate date: Date, dimensions size: CGSize) -> AnyView {
VisitsListView(visits: visitsProvider.visitsForDayComponents[date.dateComponents] ?? [],
height: size.height).erased
}
}
extension HomeManager: MonthlyCalendarDelegate {
func calendar(didSelectDay date: Date) {
listScrollState.scroll(to: date)
}
func calendar(willDisplayMonth date: Date) {
guard listScrollState.currentDayComponent != DateComponents() else { return }
if !appCalendar.isDate(date, equalTo: listScrollState.currentDayComponent.date, toGranularities: [.month, .year]) {
listScrollState.scroll(to: appCalendar.endOfMonth(for: date))
}
}
}
extension HomeManager: ElegantCalendarCommunicator {
func scrollToMonthAndShowMonthlyView(_ month: Date) {
scrollState.scroll(to: .monthlyCalendar)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
self.monthlyCalendarManager.scrollToMonth(month)
}
}
func showYearlyView() {
scrollState.scroll(to: .yearlyCalendar)
}
}
extension HomeManager: VisitsListDelegate {
func listDidBeginScrolling() {
scrollState.canDrag = false
}
func listDidEndScrolling(dayComponent: DateComponents) {
monthlyCalendarManager.scrollToMonth(dayComponent.date, animated: false)
scrollState.canDrag = true
}
func listDidScrollToToday() {
monthlyCalendarManager.scrollToDay(Date(), animated: false)
}
}
protocol HomeManagerDirectAccess: PageScrollStateDirectAccess {
var manager: HomeManager { get }
var scrollState: PageScrollState { get }
}
extension HomeManagerDirectAccess {
var scrollState: PageScrollState {
manager.scrollState
}
var appTheme: AppTheme {
manager.appTheme
}
var calendarTheme: CalendarTheme {
manager.calendarTheme
}
var visitsProvider: VisitsProvider {
manager.visitsProvider
}
var listScrollState: ListScrollState {
manager.listScrollState
}
var yearlyCalendarManager: YearlyCalendarManager {
manager.yearlyCalendarManager
}
var monthlyCalendarManager: MonthlyCalendarManager {
manager.monthlyCalendarManager
}
// I'm guessing this is how TimePage does their theme change as well. It just makes no sense to change the
// theme of the app everytime a new color is selected in the theme picker view. That would cause way too much
// lag in the app. The main problem I encountered was changing the theme of the calendar, which is UI intensive.
// If I change the calendar theme in conjunction with the app's theme, the transition from the theme picker view
// to the menu view becomes stuttery. Thus, the only solution is as you're transitioning from the theme picker to
// the menu, change thhe list's theme, which is pretty fast. Only after that transition is complete then do you
// change the calendar's theme. It's so quick that the user never even notices the slight pause in the main thread.
func changeTheme(to theme: AppTheme) {
manager.appTheme = theme
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.manager.calendarTheme = CalendarTheme(primary: theme.primary)
}
}
}
private extension CalendarTheme {
static let _white = CalendarTheme(primary: Color(._white))
}