Kaynağa Gözat

Show home for logged-in users and add logout flow.

Persist login state to route startup directly to Home, and add a Logout control that clears saved OAuth tokens and returns to Login.

Made-with: Cursor
huzaifahayat12 6 gün önce
ebeveyn
işleme
fd311d1562
1 değiştirilmiş dosya ile 43 ekleme ve 2 silme
  1. 43 2
      zoom_app/ViewController.swift

+ 43 - 2
zoom_app/ViewController.swift

@@ -13,6 +13,7 @@ import WebKit
13
 class ViewController: NSViewController {
13
 class ViewController: NSViewController {
14
     private let googleOAuth = GoogleOAuthService.shared
14
     private let googleOAuth = GoogleOAuthService.shared
15
     private let zoomOAuth = ZoomOAuthService.shared
15
     private let zoomOAuth = ZoomOAuthService.shared
16
+    private let loginStateKey = "zoom_app.isLoggedIn"
16
     private let sidebarWidth: CGFloat = 78
17
     private let sidebarWidth: CGFloat = 78
17
     private let appBackground = NSColor(calibratedRed: 10 / 255, green: 11 / 255, blue: 12 / 255, alpha: 1)
18
     private let appBackground = NSColor(calibratedRed: 10 / 255, green: 11 / 255, blue: 12 / 255, alpha: 1)
18
     private let sidebarBackground = NSColor(calibratedRed: 16 / 255, green: 17 / 255, blue: 19 / 255, alpha: 1)
19
     private let sidebarBackground = NSColor(calibratedRed: 16 / 255, green: 17 / 255, blue: 19 / 255, alpha: 1)
@@ -49,7 +50,11 @@ class ViewController: NSViewController {
49
         super.viewDidAppear()
50
         super.viewDidAppear()
50
         view.window?.setContentSize(NSSize(width: 1020, height: 690))
51
         view.window?.setContentSize(NSSize(width: 1020, height: 690))
51
         view.window?.title = "zoom Workplace"
52
         view.window?.title = "zoom Workplace"
52
-        showLoginView()
53
+        if isUserLoggedIn() {
54
+            showHomeView(profile: nil)
55
+        } else {
56
+            showLoginView()
57
+        }
53
     }
58
     }
54
 
59
 
55
     private func setupUI() {
60
     private func setupUI() {
@@ -87,10 +92,19 @@ class ViewController: NSViewController {
87
         homeView?.removeFromSuperview()
92
         homeView?.removeFromSuperview()
88
         homeView = makeHomeView(profile: profile)
93
         homeView = makeHomeView(profile: profile)
89
         if let homeView { attachToRoot(homeView) }
94
         if let homeView { attachToRoot(homeView) }
95
+        persistLoggedInState(true)
90
         startClock()
96
         startClock()
91
         Task { await loadScheduledMeetings() }
97
         Task { await loadScheduledMeetings() }
92
     }
98
     }
93
 
99
 
100
+    private func isUserLoggedIn() -> Bool {
101
+        UserDefaults.standard.bool(forKey: loginStateKey)
102
+    }
103
+
104
+    private func persistLoggedInState(_ loggedIn: Bool) {
105
+        UserDefaults.standard.set(loggedIn, forKey: loginStateKey)
106
+    }
107
+
94
     private func attachToRoot(_ subview: NSView) {
108
     private func attachToRoot(_ subview: NSView) {
95
         subview.translatesAutoresizingMaskIntoConstraints = false
109
         subview.translatesAutoresizingMaskIntoConstraints = false
96
         if subview.superview != rootContainer {
110
         if subview.superview != rootContainer {
@@ -173,6 +187,13 @@ class ViewController: NSViewController {
173
         }
187
         }
174
     }
188
     }
175
 
189
 
190
+    @objc private func logoutTapped() {
191
+        googleOAuth.clearSavedTokens()
192
+        zoomOAuth.clearSavedTokens()
193
+        persistLoggedInState(false)
194
+        showLoginView()
195
+    }
196
+
176
     @MainActor
197
     @MainActor
177
     private func resetLoginSigningInState() {
198
     private func resetLoginSigningInState() {
178
         isSigningIn = false
199
         isSigningIn = false
@@ -556,6 +577,13 @@ class ViewController: NSViewController {
556
         profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.22).cgColor
577
         profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.22).cgColor
557
         profileChip.layer?.cornerRadius = 9
578
         profileChip.layer?.cornerRadius = 9
558
         let name = makeLabel(profile?.name ?? "User", size: 13, color: primaryText, weight: .semibold, centered: false)
579
         let name = makeLabel(profile?.name ?? "User", size: 13, color: primaryText, weight: .semibold, centered: false)
580
+        let logoutButton = NSButton(title: "Logout", target: self, action: #selector(logoutTapped))
581
+        logoutButton.isBordered = false
582
+        logoutButton.font = .systemFont(ofSize: 13, weight: .semibold)
583
+        logoutButton.contentTintColor = primaryText
584
+        logoutButton.wantsLayer = true
585
+        logoutButton.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.07).cgColor
586
+        logoutButton.layer?.cornerRadius = 8
559
 
587
 
560
         let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
588
         let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
561
         let timeTitle = makeLabel("--:--", size: 50, color: primaryText, weight: .bold, centered: true)
589
         let timeTitle = makeLabel("--:--", size: 50, color: primaryText, weight: .bold, centered: true)
@@ -610,7 +638,7 @@ class ViewController: NSViewController {
610
         contentColumn.translatesAutoresizingMaskIntoConstraints = false
638
         contentColumn.translatesAutoresizingMaskIntoConstraints = false
611
         content.addSubview(contentColumn)
639
         content.addSubview(contentColumn)
612
 
640
 
613
-        [topBar, searchPill, search, profileChip, name, welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
641
+        [topBar, searchPill, search, profileChip, name, logoutButton, welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
614
             $0.translatesAutoresizingMaskIntoConstraints = false
642
             $0.translatesAutoresizingMaskIntoConstraints = false
615
             contentColumn.addSubview($0)
643
             contentColumn.addSubview($0)
616
         }
644
         }
@@ -646,6 +674,11 @@ class ViewController: NSViewController {
646
             name.trailingAnchor.constraint(equalTo: profileChip.trailingAnchor, constant: -12),
674
             name.trailingAnchor.constraint(equalTo: profileChip.trailingAnchor, constant: -12),
647
             name.centerYAnchor.constraint(equalTo: profileChip.centerYAnchor),
675
             name.centerYAnchor.constraint(equalTo: profileChip.centerYAnchor),
648
 
676
 
677
+            logoutButton.trailingAnchor.constraint(equalTo: profileChip.leadingAnchor, constant: -10),
678
+            logoutButton.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
679
+            logoutButton.widthAnchor.constraint(equalToConstant: 76),
680
+            logoutButton.heightAnchor.constraint(equalToConstant: 32),
681
+
649
             welcome.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 18),
682
             welcome.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 18),
650
             welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
683
             welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
651
 
684
 
@@ -1154,6 +1187,10 @@ final class GoogleOAuthService: NSObject {
1154
 
1187
 
1155
     func loadTokens() -> GoogleOAuthTokens? { try? tokenStore.readTokens() }
1188
     func loadTokens() -> GoogleOAuthTokens? { try? tokenStore.readTokens() }
1156
 
1189
 
1190
+    func clearSavedTokens() {
1191
+        tokenStore.clearTokens()
1192
+    }
1193
+
1157
     func fetchUserProfile(accessToken: String) async throws -> GoogleUserProfile {
1194
     func fetchUserProfile(accessToken: String) async throws -> GoogleUserProfile {
1158
         var request = URLRequest(url: URL(string: "https://openidconnect.googleapis.com/v1/userinfo")!)
1195
         var request = URLRequest(url: URL(string: "https://openidconnect.googleapis.com/v1/userinfo")!)
1159
         request.httpMethod = "GET"
1196
         request.httpMethod = "GET"
@@ -1299,6 +1336,10 @@ final class KeychainTokenStore {
1299
         let data = try JSONEncoder().encode(tokens)
1336
         let data = try JSONEncoder().encode(tokens)
1300
         defaults.set(data, forKey: defaultsKey)
1337
         defaults.set(data, forKey: defaultsKey)
1301
     }
1338
     }
1339
+
1340
+    func clearTokens() {
1341
+        defaults.removeObject(forKey: defaultsKey)
1342
+    }
1302
 }
1343
 }
1303
 
1344
 
1304
 private final class OAuthLoopbackServer {
1345
 private final class OAuthLoopbackServer {