ソースを参照

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 日 前
コミット
fd311d1562
共有1 個のファイルを変更した43 個の追加2 個の削除を含む
  1. 43 2
      zoom_app/ViewController.swift

+ 43 - 2
zoom_app/ViewController.swift

@@ -13,6 +13,7 @@ import WebKit
13 13
 class ViewController: NSViewController {
14 14
     private let googleOAuth = GoogleOAuthService.shared
15 15
     private let zoomOAuth = ZoomOAuthService.shared
16
+    private let loginStateKey = "zoom_app.isLoggedIn"
16 17
     private let sidebarWidth: CGFloat = 78
17 18
     private let appBackground = NSColor(calibratedRed: 10 / 255, green: 11 / 255, blue: 12 / 255, alpha: 1)
18 19
     private let sidebarBackground = NSColor(calibratedRed: 16 / 255, green: 17 / 255, blue: 19 / 255, alpha: 1)
@@ -49,7 +50,11 @@ class ViewController: NSViewController {
49 50
         super.viewDidAppear()
50 51
         view.window?.setContentSize(NSSize(width: 1020, height: 690))
51 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 60
     private func setupUI() {
@@ -87,10 +92,19 @@ class ViewController: NSViewController {
87 92
         homeView?.removeFromSuperview()
88 93
         homeView = makeHomeView(profile: profile)
89 94
         if let homeView { attachToRoot(homeView) }
95
+        persistLoggedInState(true)
90 96
         startClock()
91 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 108
     private func attachToRoot(_ subview: NSView) {
95 109
         subview.translatesAutoresizingMaskIntoConstraints = false
96 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 197
     @MainActor
177 198
     private func resetLoginSigningInState() {
178 199
         isSigningIn = false
@@ -556,6 +577,13 @@ class ViewController: NSViewController {
556 577
         profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.22).cgColor
557 578
         profileChip.layer?.cornerRadius = 9
558 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 588
         let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
561 589
         let timeTitle = makeLabel("--:--", size: 50, color: primaryText, weight: .bold, centered: true)
@@ -610,7 +638,7 @@ class ViewController: NSViewController {
610 638
         contentColumn.translatesAutoresizingMaskIntoConstraints = false
611 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 642
             $0.translatesAutoresizingMaskIntoConstraints = false
615 643
             contentColumn.addSubview($0)
616 644
         }
@@ -646,6 +674,11 @@ class ViewController: NSViewController {
646 674
             name.trailingAnchor.constraint(equalTo: profileChip.trailingAnchor, constant: -12),
647 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 682
             welcome.topAnchor.constraint(equalTo: topBar.bottomAnchor, constant: 18),
650 683
             welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
651 684
 
@@ -1154,6 +1187,10 @@ final class GoogleOAuthService: NSObject {
1154 1187
 
1155 1188
     func loadTokens() -> GoogleOAuthTokens? { try? tokenStore.readTokens() }
1156 1189
 
1190
+    func clearSavedTokens() {
1191
+        tokenStore.clearTokens()
1192
+    }
1193
+
1157 1194
     func fetchUserProfile(accessToken: String) async throws -> GoogleUserProfile {
1158 1195
         var request = URLRequest(url: URL(string: "https://openidconnect.googleapis.com/v1/userinfo")!)
1159 1196
         request.httpMethod = "GET"
@@ -1299,6 +1336,10 @@ final class KeychainTokenStore {
1299 1336
         let data = try JSONEncoder().encode(tokens)
1300 1337
         defaults.set(data, forKey: defaultsKey)
1301 1338
     }
1339
+
1340
+    func clearTokens() {
1341
+        defaults.removeObject(forKey: defaultsKey)
1342
+    }
1302 1343
 }
1303 1344
 
1304 1345
 private final class OAuthLoopbackServer {