|
|
@@ -14,8 +14,9 @@ import StoreKit
|
|
14
|
14
|
private enum SidebarPage: Int {
|
|
15
|
15
|
case joinMeetings = 0
|
|
16
|
16
|
case photo = 1
|
|
17
|
|
- case video = 2
|
|
18
|
|
- case settings = 3
|
|
|
17
|
+ case enrolled = 2
|
|
|
18
|
+ case video = 3
|
|
|
19
|
+ case settings = 4
|
|
19
|
20
|
}
|
|
20
|
21
|
|
|
21
|
22
|
private enum ZoomJoinMode: Int {
|
|
|
@@ -287,6 +288,7 @@ final class ViewController: NSViewController {
|
|
287
|
288
|
private var hasViewAppearedOnce = false
|
|
288
|
289
|
private var lastKnownPremiumAccess = false
|
|
289
|
290
|
private var displayedScheduleTodos: [ClassroomTodoItem] = []
|
|
|
291
|
+ private var enrolledCachedCourses: [ClassroomCourse] = []
|
|
290
|
292
|
private var appUsageSessionStartDate: Date?
|
|
291
|
293
|
private var hasObservedAppLifecycleForUsage = false
|
|
292
|
294
|
private var premiumUpgradeRatingPromptWorkItem: DispatchWorkItem?
|
|
|
@@ -356,6 +358,8 @@ final class ViewController: NSViewController {
|
|
356
|
358
|
private weak var schedulePageRangeErrorLabel: NSTextField?
|
|
357
|
359
|
private weak var schedulePageCardsStack: NSStackView?
|
|
358
|
360
|
private weak var schedulePageCardsScrollView: NSScrollView?
|
|
|
361
|
+ private weak var enrolledPageHeadingLabel: NSTextField?
|
|
|
362
|
+ private weak var enrolledPageCardsStack: NSStackView?
|
|
359
|
363
|
|
|
360
|
364
|
// MARK: - Calendar page (custom month UI)
|
|
361
|
365
|
private var calendarPageMonthAnchor: Date = Calendar.current.startOfDay(for: Date())
|
|
|
@@ -1262,6 +1266,7 @@ private extension ViewController {
|
|
1262
|
1266
|
private func refreshPagesAfterPremiumStateUpdate() {
|
|
1263
|
1267
|
pageCache[.joinMeetings] = nil
|
|
1264
|
1268
|
pageCache[.photo] = nil
|
|
|
1269
|
+ pageCache[.enrolled] = nil
|
|
1265
|
1270
|
pageCache[.video] = nil
|
|
1266
|
1271
|
pageCache[.settings] = nil
|
|
1267
|
1272
|
showSidebarPage(selectedSidebarPage)
|
|
|
@@ -1508,6 +1513,8 @@ private extension ViewController {
|
|
1508
|
1513
|
built = makeJoinMeetingsContent()
|
|
1509
|
1514
|
case .photo:
|
|
1510
|
1515
|
built = makeSchedulePageContent()
|
|
|
1516
|
+ case .enrolled:
|
|
|
1517
|
+ built = makeEnrolledPageContent()
|
|
1511
|
1518
|
case .video:
|
|
1512
|
1519
|
built = makeCalendarPageContent()
|
|
1513
|
1520
|
case .settings:
|
|
|
@@ -1866,6 +1873,8 @@ private extension ViewController {
|
|
1866
|
1873
|
title = "App for Google Classroom"
|
|
1867
|
1874
|
case .photo:
|
|
1868
|
1875
|
title = "Schedule"
|
|
|
1876
|
+ case .enrolled:
|
|
|
1877
|
+ title = "Enrolled"
|
|
1869
|
1878
|
case .video:
|
|
1870
|
1879
|
title = "Calendar"
|
|
1871
|
1880
|
case .settings:
|
|
|
@@ -1909,7 +1918,7 @@ private extension ViewController {
|
|
1909
|
1918
|
private func logoTemplateForSidebarPage(_ page: SidebarPage) -> Bool {
|
|
1910
|
1919
|
switch page {
|
|
1911
|
1920
|
case .photo: return false
|
|
1912
|
|
- case .joinMeetings, .video, .settings: return true
|
|
|
1921
|
+ case .joinMeetings, .enrolled, .video, .settings: return true
|
|
1913
|
1922
|
}
|
|
1914
|
1923
|
}
|
|
1915
|
1924
|
|
|
|
@@ -1964,6 +1973,9 @@ private extension ViewController {
|
|
1964
|
1973
|
let photoRow = sidebarItem("Schedule", icon: "", page: .photo, systemSymbolName: "clock.badge.checkmark")
|
|
1965
|
1974
|
menuStack.addArrangedSubview(photoRow)
|
|
1966
|
1975
|
sidebarRowViews[.photo] = photoRow
|
|
|
1976
|
+ let enrolledRow = sidebarItem("Enrolled", icon: "", page: .enrolled, systemSymbolName: "person.3.sequence.fill")
|
|
|
1977
|
+ menuStack.addArrangedSubview(enrolledRow)
|
|
|
1978
|
+ sidebarRowViews[.enrolled] = enrolledRow
|
|
1967
|
1979
|
let videoRow = sidebarItem("Calendar", icon: "", page: .video, systemSymbolName: "calendar")
|
|
1968
|
1980
|
menuStack.addArrangedSubview(videoRow)
|
|
1969
|
1981
|
sidebarRowViews[.video] = videoRow
|
|
|
@@ -2201,6 +2213,109 @@ private extension ViewController {
|
|
2201
|
2213
|
return panel
|
|
2202
|
2214
|
}
|
|
2203
|
2215
|
|
|
|
2216
|
+ func makeEnrolledPageContent() -> NSView {
|
|
|
2217
|
+ let panel = NSView()
|
|
|
2218
|
+ panel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2219
|
+ panel.userInterfaceLayoutDirection = .leftToRight
|
|
|
2220
|
+
|
|
|
2221
|
+ let contentStack = NSStackView()
|
|
|
2222
|
+ contentStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2223
|
+ contentStack.userInterfaceLayoutDirection = .leftToRight
|
|
|
2224
|
+ contentStack.orientation = .vertical
|
|
|
2225
|
+ contentStack.spacing = 14
|
|
|
2226
|
+ contentStack.alignment = .width
|
|
|
2227
|
+ contentStack.distribution = .fill
|
|
|
2228
|
+
|
|
|
2229
|
+ let titleRow = NSStackView()
|
|
|
2230
|
+ titleRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2231
|
+ titleRow.userInterfaceLayoutDirection = .leftToRight
|
|
|
2232
|
+ titleRow.orientation = .horizontal
|
|
|
2233
|
+ titleRow.alignment = .centerY
|
|
|
2234
|
+ titleRow.distribution = .fill
|
|
|
2235
|
+ titleRow.spacing = 10
|
|
|
2236
|
+
|
|
|
2237
|
+ let titleLabel = textLabel("Enrolled Classes", font: typography.pageTitle, color: palette.textPrimary)
|
|
|
2238
|
+ titleLabel.alignment = .left
|
|
|
2239
|
+ titleLabel.maximumNumberOfLines = 1
|
|
|
2240
|
+ titleLabel.lineBreakMode = .byTruncatingTail
|
|
|
2241
|
+ titleLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
2242
|
+ titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
2243
|
+
|
|
|
2244
|
+ let spacer = NSView()
|
|
|
2245
|
+ spacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2246
|
+ spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
2247
|
+ spacer.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
2248
|
+
|
|
|
2249
|
+ let refreshButton = makeScheduleRefreshButton()
|
|
|
2250
|
+ refreshButton.target = self
|
|
|
2251
|
+ refreshButton.action = #selector(enrolledPageRefreshPressed(_:))
|
|
|
2252
|
+
|
|
|
2253
|
+ titleRow.addArrangedSubview(titleLabel)
|
|
|
2254
|
+ titleRow.addArrangedSubview(spacer)
|
|
|
2255
|
+ titleRow.addArrangedSubview(refreshButton)
|
|
|
2256
|
+
|
|
|
2257
|
+ let heading = textLabel(enrolledPageInitialHeadingText(), font: typography.dateHeading, color: palette.textSecondary)
|
|
|
2258
|
+ heading.alignment = .left
|
|
|
2259
|
+ heading.maximumNumberOfLines = 2
|
|
|
2260
|
+ heading.lineBreakMode = .byWordWrapping
|
|
|
2261
|
+ enrolledPageHeadingLabel = heading
|
|
|
2262
|
+
|
|
|
2263
|
+ let scroll = NSScrollView()
|
|
|
2264
|
+ scroll.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2265
|
+ scroll.userInterfaceLayoutDirection = .leftToRight
|
|
|
2266
|
+ scroll.drawsBackground = false
|
|
|
2267
|
+ scroll.hasHorizontalScroller = false
|
|
|
2268
|
+ scroll.hasVerticalScroller = true
|
|
|
2269
|
+ scroll.autohidesScrollers = true
|
|
|
2270
|
+ scroll.borderType = .noBorder
|
|
|
2271
|
+ scroll.scrollerStyle = .overlay
|
|
|
2272
|
+ scroll.automaticallyAdjustsContentInsets = false
|
|
|
2273
|
+ let clip = TopAlignedClipView()
|
|
|
2274
|
+ clip.drawsBackground = false
|
|
|
2275
|
+ scroll.contentView = clip
|
|
|
2276
|
+
|
|
|
2277
|
+ let stack = NSStackView()
|
|
|
2278
|
+ stack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
2279
|
+ stack.userInterfaceLayoutDirection = .leftToRight
|
|
|
2280
|
+ stack.orientation = .vertical
|
|
|
2281
|
+ stack.spacing = 14
|
|
|
2282
|
+ stack.alignment = .width
|
|
|
2283
|
+ enrolledPageCardsStack = stack
|
|
|
2284
|
+ scroll.documentView = stack
|
|
|
2285
|
+
|
|
|
2286
|
+ NSLayoutConstraint.activate([
|
|
|
2287
|
+ stack.leadingAnchor.constraint(equalTo: scroll.contentView.leadingAnchor),
|
|
|
2288
|
+ stack.trailingAnchor.constraint(equalTo: scroll.contentView.trailingAnchor),
|
|
|
2289
|
+ stack.topAnchor.constraint(equalTo: scroll.contentView.topAnchor),
|
|
|
2290
|
+ stack.widthAnchor.constraint(equalTo: scroll.contentView.widthAnchor)
|
|
|
2291
|
+ ])
|
|
|
2292
|
+
|
|
|
2293
|
+ contentStack.addArrangedSubview(titleRow)
|
|
|
2294
|
+ contentStack.setCustomSpacing(10, after: titleRow)
|
|
|
2295
|
+ contentStack.addArrangedSubview(heading)
|
|
|
2296
|
+ contentStack.setCustomSpacing(12, after: heading)
|
|
|
2297
|
+ contentStack.addArrangedSubview(scroll)
|
|
|
2298
|
+ panel.addSubview(contentStack)
|
|
|
2299
|
+
|
|
|
2300
|
+ NSLayoutConstraint.activate([
|
|
|
2301
|
+ contentStack.leftAnchor.constraint(equalTo: panel.leftAnchor, constant: 28),
|
|
|
2302
|
+ contentStack.rightAnchor.constraint(equalTo: panel.rightAnchor, constant: -28),
|
|
|
2303
|
+ contentStack.topAnchor.constraint(equalTo: panel.topAnchor),
|
|
|
2304
|
+ contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -16),
|
|
|
2305
|
+ titleRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
2306
|
+ heading.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
2307
|
+ scroll.widthAnchor.constraint(equalTo: contentStack.widthAnchor),
|
|
|
2308
|
+ scroll.heightAnchor.constraint(greaterThanOrEqualToConstant: 420)
|
|
|
2309
|
+ ])
|
|
|
2310
|
+
|
|
|
2311
|
+ renderEnrolledClassCards([])
|
|
|
2312
|
+ Task { [weak self] in
|
|
|
2313
|
+ await self?.loadEnrolledClasses()
|
|
|
2314
|
+ }
|
|
|
2315
|
+
|
|
|
2316
|
+ return panel
|
|
|
2317
|
+ }
|
|
|
2318
|
+
|
|
2204
|
2319
|
func makeCalendarPageContent() -> NSView {
|
|
2205
|
2320
|
let panel = NSView()
|
|
2206
|
2321
|
panel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -5845,6 +5960,16 @@ private extension ViewController {
|
|
5845
|
5960
|
googleOAuth.loadTokens() == nil ? "Connect Google to see your to-do" : "Loading to-do…"
|
|
5846
|
5961
|
}
|
|
5847
|
5962
|
|
|
|
5963
|
+ private func enrolledPageInitialHeadingText() -> String {
|
|
|
5964
|
+ googleOAuth.loadTokens() == nil ? "Connect Google to see enrolled classes" : "Loading classes…"
|
|
|
5965
|
+ }
|
|
|
5966
|
+
|
|
|
5967
|
+ @objc func enrolledPageRefreshPressed(_ sender: NSButton) {
|
|
|
5968
|
+ Task { [weak self] in
|
|
|
5969
|
+ await self?.loadEnrolledClasses()
|
|
|
5970
|
+ }
|
|
|
5971
|
+ }
|
|
|
5972
|
+
|
|
5848
|
5973
|
@objc func scheduleFilterDropdownChanged(_ sender: NSPopUpButton) {
|
|
5849
|
5974
|
guard let selectedItem = sender.selectedItem,
|
|
5850
|
5975
|
let filter = ScheduleFilter(rawValue: selectedItem.tag) else { return }
|
|
|
@@ -5950,10 +6075,102 @@ private extension ViewController {
|
|
5950
|
6075
|
return f.string(from: day)
|
|
5951
|
6076
|
}
|
|
5952
|
6077
|
|
|
|
6078
|
+ private func enrolledPageHeadingText(for courses: [ClassroomCourse]) -> String {
|
|
|
6079
|
+ if googleOAuth.loadTokens() == nil { return "Connect Google to see enrolled classes" }
|
|
|
6080
|
+ if courses.isEmpty { return "No active enrolled classes" }
|
|
|
6081
|
+ return "\(courses.count) enrolled class\(courses.count == 1 ? "" : "es")"
|
|
|
6082
|
+ }
|
|
|
6083
|
+
|
|
5953
|
6084
|
private func openURL(_ url: URL) {
|
|
5954
|
6085
|
NSWorkspace.shared.open(url)
|
|
5955
|
6086
|
}
|
|
5956
|
6087
|
|
|
|
6088
|
+ private func renderEnrolledClassCards(_ courses: [ClassroomCourse]) {
|
|
|
6089
|
+ guard let stack = enrolledPageCardsStack else { return }
|
|
|
6090
|
+
|
|
|
6091
|
+ stack.arrangedSubviews.forEach { view in
|
|
|
6092
|
+ stack.removeArrangedSubview(view)
|
|
|
6093
|
+ view.removeFromSuperview()
|
|
|
6094
|
+ }
|
|
|
6095
|
+
|
|
|
6096
|
+ if courses.isEmpty {
|
|
|
6097
|
+ let empty = roundedContainer(cornerRadius: 12, color: palette.sectionCard)
|
|
|
6098
|
+ empty.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6099
|
+ empty.heightAnchor.constraint(equalToConstant: 128).isActive = true
|
|
|
6100
|
+ styleSurface(empty, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
6101
|
+
|
|
|
6102
|
+ let emptyLabel = textLabel(
|
|
|
6103
|
+ googleOAuth.loadTokens() == nil ? "Connect to load classes" : "No enrolled classes found",
|
|
|
6104
|
+ font: typography.cardSubtitle,
|
|
|
6105
|
+ color: palette.textSecondary
|
|
|
6106
|
+ )
|
|
|
6107
|
+ emptyLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6108
|
+ emptyLabel.alignment = .left
|
|
|
6109
|
+ empty.addSubview(emptyLabel)
|
|
|
6110
|
+
|
|
|
6111
|
+ NSLayoutConstraint.activate([
|
|
|
6112
|
+ emptyLabel.leadingAnchor.constraint(equalTo: empty.leadingAnchor, constant: 18),
|
|
|
6113
|
+ emptyLabel.trailingAnchor.constraint(equalTo: empty.trailingAnchor, constant: -18),
|
|
|
6114
|
+ emptyLabel.centerYAnchor.constraint(equalTo: empty.centerYAnchor)
|
|
|
6115
|
+ ])
|
|
|
6116
|
+ stack.addArrangedSubview(empty)
|
|
|
6117
|
+ return
|
|
|
6118
|
+ }
|
|
|
6119
|
+
|
|
|
6120
|
+ for course in courses {
|
|
|
6121
|
+ stack.addArrangedSubview(enrolledClassCard(course: course))
|
|
|
6122
|
+ }
|
|
|
6123
|
+ }
|
|
|
6124
|
+
|
|
|
6125
|
+ private func enrolledClassCard(course: ClassroomCourse) -> NSView {
|
|
|
6126
|
+ let card = roundedContainer(cornerRadius: 14, color: palette.sectionCard)
|
|
|
6127
|
+ card.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6128
|
+ styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
6129
|
+ card.heightAnchor.constraint(greaterThanOrEqualToConstant: 124).isActive = true
|
|
|
6130
|
+
|
|
|
6131
|
+ let title = textLabel(course.name, font: NSFont.systemFont(ofSize: 17, weight: .semibold), color: palette.textPrimary)
|
|
|
6132
|
+ title.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6133
|
+ title.alignment = .left
|
|
|
6134
|
+ title.maximumNumberOfLines = 2
|
|
|
6135
|
+ title.lineBreakMode = .byWordWrapping
|
|
|
6136
|
+
|
|
|
6137
|
+ let sectionText = [course.section, course.room]
|
|
|
6138
|
+ .compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
|
6139
|
+ .filter { !$0.isEmpty }
|
|
|
6140
|
+ .joined(separator: " • ")
|
|
|
6141
|
+ let sectionLabel = textLabel(sectionText.isEmpty ? "Section details unavailable" : sectionText, font: typography.cardSubtitle, color: palette.textSecondary)
|
|
|
6142
|
+ sectionLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6143
|
+ sectionLabel.alignment = .left
|
|
|
6144
|
+ sectionLabel.maximumNumberOfLines = 1
|
|
|
6145
|
+ sectionLabel.lineBreakMode = .byTruncatingTail
|
|
|
6146
|
+
|
|
|
6147
|
+ let teachers = course.teacherNames.isEmpty ? "Teacher unavailable" : course.teacherNames.joined(separator: ", ")
|
|
|
6148
|
+ let teacherLabel = textLabel("Teacher: \(teachers)", font: NSFont.systemFont(ofSize: 12, weight: .medium), color: palette.textMuted)
|
|
|
6149
|
+ teacherLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
6150
|
+ teacherLabel.alignment = .left
|
|
|
6151
|
+ teacherLabel.maximumNumberOfLines = 2
|
|
|
6152
|
+ teacherLabel.lineBreakMode = .byWordWrapping
|
|
|
6153
|
+
|
|
|
6154
|
+ card.addSubview(title)
|
|
|
6155
|
+ card.addSubview(sectionLabel)
|
|
|
6156
|
+ card.addSubview(teacherLabel)
|
|
|
6157
|
+ NSLayoutConstraint.activate([
|
|
|
6158
|
+ title.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 18),
|
|
|
6159
|
+ title.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -18),
|
|
|
6160
|
+ title.topAnchor.constraint(equalTo: card.topAnchor, constant: 16),
|
|
|
6161
|
+
|
|
|
6162
|
+ sectionLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
6163
|
+ sectionLabel.trailingAnchor.constraint(equalTo: title.trailingAnchor),
|
|
|
6164
|
+ sectionLabel.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 8),
|
|
|
6165
|
+
|
|
|
6166
|
+ teacherLabel.leadingAnchor.constraint(equalTo: title.leadingAnchor),
|
|
|
6167
|
+ teacherLabel.trailingAnchor.constraint(equalTo: title.trailingAnchor),
|
|
|
6168
|
+ teacherLabel.topAnchor.constraint(equalTo: sectionLabel.bottomAnchor, constant: 10),
|
|
|
6169
|
+ teacherLabel.bottomAnchor.constraint(lessThanOrEqualTo: card.bottomAnchor, constant: -16)
|
|
|
6170
|
+ ])
|
|
|
6171
|
+ return card
|
|
|
6172
|
+ }
|
|
|
6173
|
+
|
|
5957
|
6174
|
private func renderScheduleCards(into stack: NSStackView, todos: [ClassroomTodoItem]) {
|
|
5958
|
6175
|
displayedScheduleTodos = todos
|
|
5959
|
6176
|
let shouldShowScrollControls = todos.count > 3
|
|
|
@@ -6293,6 +6510,48 @@ private extension ViewController {
|
|
6293
|
6510
|
}
|
|
6294
|
6511
|
}
|
|
6295
|
6512
|
|
|
|
6513
|
+ private func loadEnrolledClasses() async {
|
|
|
6514
|
+ do {
|
|
|
6515
|
+ if googleOAuth.loadTokens() == nil {
|
|
|
6516
|
+ await MainActor.run {
|
|
|
6517
|
+ enrolledCachedCourses = []
|
|
|
6518
|
+ enrolledPageHeadingLabel?.stringValue = "Connect Google to see enrolled classes"
|
|
|
6519
|
+ renderEnrolledClassCards([])
|
|
|
6520
|
+ }
|
|
|
6521
|
+ return
|
|
|
6522
|
+ }
|
|
|
6523
|
+
|
|
|
6524
|
+ let token = try await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
6525
|
+ let courses = try await classroomClient.fetchEnrolledCourses(accessToken: token)
|
|
|
6526
|
+
|
|
|
6527
|
+ await MainActor.run {
|
|
|
6528
|
+ enrolledCachedCourses = courses
|
|
|
6529
|
+ enrolledPageHeadingLabel?.stringValue = enrolledPageHeadingText(for: courses)
|
|
|
6530
|
+ renderEnrolledClassCards(courses)
|
|
|
6531
|
+ }
|
|
|
6532
|
+ } catch {
|
|
|
6533
|
+ await MainActor.run {
|
|
|
6534
|
+ if errorRequiresReconsentForClassroomScopes(error) {
|
|
|
6535
|
+ _ = try? googleOAuth.signOut()
|
|
|
6536
|
+ applyGoogleProfile(nil)
|
|
|
6537
|
+ updateGoogleAuthButtonTitle()
|
|
|
6538
|
+ enrolledCachedCourses = []
|
|
|
6539
|
+ enrolledPageHeadingLabel?.stringValue = "Reconnect Google to enable Classroom permissions"
|
|
|
6540
|
+ renderEnrolledClassCards([])
|
|
|
6541
|
+ showSimpleAlert(
|
|
|
6542
|
+ title: "Reconnect Google",
|
|
|
6543
|
+ message: "We added Google Classroom permissions. Please connect your Google account again so Google can grant access to your classes."
|
|
|
6544
|
+ )
|
|
|
6545
|
+ return
|
|
|
6546
|
+ }
|
|
|
6547
|
+ enrolledCachedCourses = []
|
|
|
6548
|
+ enrolledPageHeadingLabel?.stringValue = "Couldn’t load enrolled classes"
|
|
|
6549
|
+ renderEnrolledClassCards([])
|
|
|
6550
|
+ showSimpleError("Couldn’t load enrolled classes.", error: error)
|
|
|
6551
|
+ }
|
|
|
6552
|
+ }
|
|
|
6553
|
+ }
|
|
|
6554
|
+
|
|
6296
|
6555
|
func showScheduleHelp() {
|
|
6297
|
6556
|
let alert = NSAlert()
|
|
6298
|
6557
|
alert.messageText = "Google Classroom to-do"
|
|
|
@@ -6311,6 +6570,7 @@ private extension ViewController {
|
|
6311
|
6570
|
self.scheduleDateHeadingLabel?.stringValue = "Refreshing…"
|
|
6312
|
6571
|
self.pageCache[.joinMeetings] = nil
|
|
6313
|
6572
|
self.pageCache[.photo] = nil
|
|
|
6573
|
+ self.pageCache[.enrolled] = nil
|
|
6314
|
6574
|
self.showSidebarPage(self.selectedSidebarPage)
|
|
6315
|
6575
|
}
|
|
6316
|
6576
|
await self.loadSchedule()
|
|
|
@@ -6339,6 +6599,7 @@ private extension ViewController {
|
|
6339
|
6599
|
self.applyGoogleProfile(profile.map { self.makeGoogleProfileDisplay(from: $0) })
|
|
6340
|
6600
|
self.pageCache[.joinMeetings] = nil
|
|
6341
|
6601
|
self.pageCache[.photo] = nil
|
|
|
6602
|
+ self.pageCache[.enrolled] = nil
|
|
6342
|
6603
|
self.pageCache[.video] = nil
|
|
6343
|
6604
|
self.pageCache[.settings] = nil
|
|
6344
|
6605
|
self.showSidebarPage(self.selectedSidebarPage)
|
|
|
@@ -6390,6 +6651,7 @@ private extension ViewController {
|
|
6390
|
6651
|
updateGoogleAuthButtonTitle()
|
|
6391
|
6652
|
pageCache[.joinMeetings] = nil
|
|
6392
|
6653
|
pageCache[.photo] = nil
|
|
|
6654
|
+ pageCache[.enrolled] = nil
|
|
6393
|
6655
|
pageCache[.video] = nil
|
|
6394
|
6656
|
pageCache[.settings] = nil
|
|
6395
|
6657
|
showSidebarPage(selectedSidebarPage)
|