Răsfoiți Sursa

Improve meetings refresh with auto-polling and scroll edge triggers.

Refresh scheduled meetings periodically, trigger reloads when users scroll to the top or bottom of the list, and guard against overlapping refresh calls.

Made-with: Cursor
huzaifahayat12 3 luni în urmă
părinte
comite
4ed0c95739
1 a modificat fișierele cu 87 adăugiri și 1 ștergeri
  1. 87 1
      zoom_app/ViewController.swift

+ 87 - 1
zoom_app/ViewController.swift

@@ -37,9 +37,17 @@ class ViewController: NSViewController {
     private weak var emptyMeetingLabel: NSTextField?
     private weak var meetingsListStack: NSStackView?
     private weak var meetingsStatusLabel: NSTextField?
+    private weak var meetingsScrollView: NSScrollView?
     private var clockTimer: Timer?
+    private var meetingsRefreshTimer: Timer?
     private var isSigningIn = false
     private var isPromptingZoomCredentials = false
+    private var isLoadingMeetings = false
+    private var meetingsScrollObserver: NSObjectProtocol?
+    private var lastMeetingsRefreshAt = Date.distantPast
+    private var lastScrollEdgeRefreshAt = Date.distantPast
+    private let meetingsRefreshInterval: TimeInterval = 8
+    private let scrollRefreshCooldown: TimeInterval = 3
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -73,6 +81,9 @@ class ViewController: NSViewController {
 
     private func showLoginView() {
         clockTimer?.invalidate()
+        meetingsRefreshTimer?.invalidate()
+        meetingsRefreshTimer = nil
+        clearMeetingsScrollObserver()
         homeView?.removeFromSuperview()
         homeView = nil
         isSigningIn = false
@@ -89,12 +100,14 @@ class ViewController: NSViewController {
 
     private func showHomeView(profile: GoogleUserProfile?) {
         loginView?.removeFromSuperview()
+        clearMeetingsScrollObserver()
         homeView?.removeFromSuperview()
         homeView = makeHomeView(profile: profile)
         if let homeView { attachToRoot(homeView) }
         persistLoggedInState(true)
         startClock()
-        Task { await loadScheduledMeetings() }
+        startMeetingsAutoRefresh()
+        triggerMeetingsRefresh(force: true)
     }
 
     private func isUserLoggedIn() -> Bool {
@@ -188,12 +201,81 @@ class ViewController: NSViewController {
     }
 
     @objc private func logoutTapped() {
+        meetingsRefreshTimer?.invalidate()
+        meetingsRefreshTimer = nil
+        clearMeetingsScrollObserver()
         googleOAuth.clearSavedTokens()
         zoomOAuth.clearSavedTokens()
         persistLoggedInState(false)
         showLoginView()
     }
 
+    private func startMeetingsAutoRefresh() {
+        meetingsRefreshTimer?.invalidate()
+        // Poll Zoom meetings periodically so newly scheduled meetings appear automatically.
+        meetingsRefreshTimer = Timer.scheduledTimer(withTimeInterval: meetingsRefreshInterval, repeats: true) { [weak self] _ in
+            guard let self else { return }
+            self.triggerMeetingsRefresh()
+        }
+    }
+
+    private func triggerMeetingsRefresh(force: Bool = false) {
+        let now = Date()
+        if force == false, now.timeIntervalSince(lastMeetingsRefreshAt) < meetingsRefreshInterval {
+            return
+        }
+        lastMeetingsRefreshAt = now
+        Task { await self.loadScheduledMeetings() }
+    }
+
+    private func clearMeetingsScrollObserver() {
+        if let meetingsScrollObserver {
+            NotificationCenter.default.removeObserver(meetingsScrollObserver)
+        }
+        meetingsScrollObserver = nil
+        meetingsScrollView?.contentView.postsBoundsChangedNotifications = false
+        meetingsScrollView = nil
+    }
+
+    private func observeMeetingsScrollEdges(in scrollView: NSScrollView) {
+        clearMeetingsScrollObserver()
+        meetingsScrollView = scrollView
+        scrollView.contentView.postsBoundsChangedNotifications = true
+        meetingsScrollObserver = NotificationCenter.default.addObserver(
+            forName: NSView.boundsDidChangeNotification,
+            object: scrollView.contentView,
+            queue: .main
+        ) { [weak self, weak scrollView] _ in
+            guard let self, let scrollView else { return }
+            self.refreshMeetingsIfScrolledToEdge(scrollView)
+        }
+    }
+
+    private func refreshMeetingsIfScrolledToEdge(_ scrollView: NSScrollView) {
+        guard let documentView = scrollView.documentView else { return }
+
+        let visibleRect = scrollView.contentView.bounds
+        let contentHeight = documentView.bounds.height
+        let viewportHeight = visibleRect.height
+        if contentHeight <= viewportHeight + 2 {
+            return
+        }
+
+        let maxOffset = max(contentHeight - viewportHeight, 0)
+        let y = visibleRect.origin.y
+        let threshold: CGFloat = 12
+        let reachedTop = y <= threshold
+        let reachedBottom = y >= (maxOffset - threshold)
+        guard reachedTop || reachedBottom else { return }
+
+        let now = Date()
+        if now.timeIntervalSince(lastScrollEdgeRefreshAt) < scrollRefreshCooldown {
+            return
+        }
+        lastScrollEdgeRefreshAt = now
+        triggerMeetingsRefresh(force: true)
+    }
+
     @MainActor
     private func resetLoginSigningInState() {
         isSigningIn = false
@@ -251,6 +333,9 @@ class ViewController: NSViewController {
     }
 
     private func loadScheduledMeetings() async {
+        if isLoadingMeetings { return }
+        isLoadingMeetings = true
+        defer { isLoadingMeetings = false }
         do {
             let zoomToken = try await zoomOAuth.validAccessToken(presentingWindow: view.window)
             let zoomMeetings = try await fetchZoomScheduledMeetings(accessToken: zoomToken)
@@ -729,6 +814,7 @@ class ViewController: NSViewController {
         meetingsListStack = meetingsStack
         meetingsStatusLabel = meetingsStatus
         emptyMeetingLabel = noMeeting
+        observeMeetingsScrollEdges(in: meetingsScrollView)
         updateClock()
         return root
     }