|
@@ -53,6 +53,7 @@ class ViewController: NSViewController {
|
|
53
|
private weak var meetingsPrevDayButton: NSButton?
|
53
|
private weak var meetingsPrevDayButton: NSButton?
|
|
54
|
private weak var meetingsNextDayButton: NSButton?
|
54
|
private weak var meetingsNextDayButton: NSButton?
|
|
55
|
private weak var meetingsTodayButton: NSButton?
|
55
|
private weak var meetingsTodayButton: NSButton?
|
|
|
|
56
|
+ private weak var refreshMeetingsButton: NSButton?
|
|
56
|
private weak var homeSearchField: NSTextField?
|
57
|
private weak var homeSearchField: NSTextField?
|
|
57
|
private weak var homeSearchPill: NSView?
|
58
|
private weak var homeSearchPill: NSView?
|
|
58
|
private var allScheduledMeetings: [ScheduledMeeting] = []
|
59
|
private var allScheduledMeetings: [ScheduledMeeting] = []
|
|
@@ -266,9 +267,37 @@ class ViewController: NSViewController {
|
|
266
|
}
|
267
|
}
|
|
267
|
|
268
|
|
|
268
|
@objc private func refreshMeetingsTapped() {
|
269
|
@objc private func refreshMeetingsTapped() {
|
|
|
|
270
|
+ Task { @MainActor in
|
|
|
|
271
|
+ self.animateMeetingsRefresh()
|
|
|
|
272
|
+ self.setMeetingsLoadingUI(true)
|
|
|
|
273
|
+ }
|
|
269
|
triggerMeetingsRefresh(force: true)
|
274
|
triggerMeetingsRefresh(force: true)
|
|
270
|
}
|
275
|
}
|
|
271
|
|
276
|
|
|
|
|
277
|
+ @MainActor
|
|
|
|
278
|
+ private func setMeetingsLoadingUI(_ loading: Bool) {
|
|
|
|
279
|
+ refreshMeetingsButton?.isEnabled = loading == false
|
|
|
|
280
|
+ }
|
|
|
|
281
|
+
|
|
|
|
282
|
+ @MainActor
|
|
|
|
283
|
+ private func animateMeetingsRefresh() {
|
|
|
|
284
|
+ guard let stack = meetingsListStack, stack.arrangedSubviews.isEmpty == false else { return }
|
|
|
|
285
|
+ let views = stack.arrangedSubviews
|
|
|
|
286
|
+ let dimmed: CGFloat = 0.86
|
|
|
|
287
|
+
|
|
|
|
288
|
+ NSAnimationContext.runAnimationGroup { context in
|
|
|
|
289
|
+ context.duration = 0.07
|
|
|
|
290
|
+ context.timingFunction = CAMediaTimingFunction(name: .linear)
|
|
|
|
291
|
+ views.forEach { $0.animator().alphaValue = dimmed }
|
|
|
|
292
|
+ } completionHandler: {
|
|
|
|
293
|
+ NSAnimationContext.runAnimationGroup { context in
|
|
|
|
294
|
+ context.duration = 0.08
|
|
|
|
295
|
+ context.timingFunction = CAMediaTimingFunction(name: .linear)
|
|
|
|
296
|
+ views.forEach { $0.animator().alphaValue = 1.0 }
|
|
|
|
297
|
+ }
|
|
|
|
298
|
+ }
|
|
|
|
299
|
+ }
|
|
|
|
300
|
+
|
|
272
|
private func startMeetingsAutoRefresh() {
|
301
|
private func startMeetingsAutoRefresh() {
|
|
273
|
meetingsRefreshTimer?.invalidate()
|
302
|
meetingsRefreshTimer?.invalidate()
|
|
274
|
// Poll Zoom meetings periodically so newly scheduled meetings appear automatically.
|
303
|
// Poll Zoom meetings periodically so newly scheduled meetings appear automatically.
|
|
@@ -550,7 +579,15 @@ class ViewController: NSViewController {
|
|
550
|
private func loadScheduledMeetings() async {
|
579
|
private func loadScheduledMeetings() async {
|
|
551
|
if isLoadingMeetings { return }
|
580
|
if isLoadingMeetings { return }
|
|
552
|
isLoadingMeetings = true
|
581
|
isLoadingMeetings = true
|
|
553
|
- defer { isLoadingMeetings = false }
|
|
|
|
|
|
582
|
+ defer {
|
|
|
|
583
|
+ isLoadingMeetings = false
|
|
|
|
584
|
+ Task { @MainActor in
|
|
|
|
585
|
+ self.setMeetingsLoadingUI(false)
|
|
|
|
586
|
+ }
|
|
|
|
587
|
+ }
|
|
|
|
588
|
+ await MainActor.run {
|
|
|
|
589
|
+ self.setMeetingsLoadingUI(true)
|
|
|
|
590
|
+ }
|
|
554
|
do {
|
591
|
do {
|
|
555
|
let zoomToken = try await zoomOAuth.validAccessToken(presentingWindow: view.window)
|
592
|
let zoomToken = try await zoomOAuth.validAccessToken(presentingWindow: view.window)
|
|
556
|
let zoomMeetings = try await fetchZoomScheduledMeetings(accessToken: zoomToken)
|
593
|
let zoomMeetings = try await fetchZoomScheduledMeetings(accessToken: zoomToken)
|
|
@@ -1101,6 +1138,7 @@ class ViewController: NSViewController {
|
|
1101
|
refreshMeetingsButton.layer?.cornerRadius = 11
|
1138
|
refreshMeetingsButton.layer?.cornerRadius = 11
|
|
1102
|
refreshMeetingsButton.layer?.borderWidth = 1
|
1139
|
refreshMeetingsButton.layer?.borderWidth = 1
|
|
1103
|
refreshMeetingsButton.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
|
1140
|
refreshMeetingsButton.layer?.borderColor = NSColor.white.withAlphaComponent(0.07).cgColor
|
|
|
|
1141
|
+ self.refreshMeetingsButton = refreshMeetingsButton
|
|
1104
|
|
1142
|
|
|
1105
|
let contentColumn = NSView()
|
1143
|
let contentColumn = NSView()
|
|
1106
|
contentColumn.translatesAutoresizingMaskIntoConstraints = false
|
1144
|
contentColumn.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -1193,7 +1231,7 @@ class ViewController: NSViewController {
|
|
1193
|
|
1231
|
|
|
1194
|
panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 18),
|
1232
|
panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 18),
|
|
1195
|
panel.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
|
1233
|
panel.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
|
|
1196
|
- panel.widthAnchor.constraint(equalToConstant: 640),
|
|
|
|
|
|
1234
|
+ panel.widthAnchor.constraint(equalToConstant: 610),
|
|
1197
|
panel.leadingAnchor.constraint(greaterThanOrEqualTo: contentColumn.leadingAnchor, constant: 6),
|
1235
|
panel.leadingAnchor.constraint(greaterThanOrEqualTo: contentColumn.leadingAnchor, constant: 6),
|
|
1198
|
panel.trailingAnchor.constraint(lessThanOrEqualTo: contentColumn.trailingAnchor, constant: -6),
|
1236
|
panel.trailingAnchor.constraint(lessThanOrEqualTo: contentColumn.trailingAnchor, constant: -6),
|
|
1199
|
panel.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
|
1237
|
panel.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
|