|
|
@@ -186,11 +186,8 @@ class ViewController: NSViewController {
|
|
|
private var mutedText: NSColor { palette.mutedText }
|
|
|
|
|
|
private let rootContainer = NSView()
|
|
|
- private var loginView: NSView?
|
|
|
private var homeView: NSView?
|
|
|
private weak var googleButton: NSButton?
|
|
|
- private weak var nextSignInButton: NSButton?
|
|
|
- private weak var zoomSocialButton: NSButton?
|
|
|
private weak var timeLabel: NSTextField?
|
|
|
private weak var dateLabel: NSTextField?
|
|
|
private weak var meetingsDayHeaderLabel: NSTextField?
|
|
|
@@ -242,6 +239,8 @@ class ViewController: NSViewController {
|
|
|
private weak var settingsRestoreButton: NSButton?
|
|
|
private weak var settingsGoogleActionButton: NSButton?
|
|
|
private weak var topBarPremiumButton: NSButton?
|
|
|
+ private weak var topBarAuthButton: NSButton?
|
|
|
+ private var topBarAuthButtonWidthConstraint: NSLayoutConstraint?
|
|
|
private var paywallWindow: NSWindow?
|
|
|
private var joinMeetingWindow: NSWindow?
|
|
|
private var scheduleMeetingWindow: NSWindow?
|
|
|
@@ -285,6 +284,7 @@ class ViewController: NSViewController {
|
|
|
private var searchOutsideClickMonitor: Any?
|
|
|
private var meetingSearchTextObserver: NSObjectProtocol?
|
|
|
private var globalSearchResults: [ScheduledMeeting] = []
|
|
|
+ private var topBarProfileInitial = "H"
|
|
|
private var clockTimer: Timer?
|
|
|
private var meetingsRefreshTimer: Timer?
|
|
|
private var isSigningIn = false
|
|
|
@@ -308,7 +308,6 @@ class ViewController: NSViewController {
|
|
|
private let meetingsPageSize: Int = 8
|
|
|
|
|
|
private enum SidebarStyle {
|
|
|
- case login
|
|
|
case home
|
|
|
}
|
|
|
|
|
|
@@ -477,11 +476,7 @@ class ViewController: NSViewController {
|
|
|
window.styleMask.insert(.fullSizeContentView)
|
|
|
}
|
|
|
alignNativeTrafficLights()
|
|
|
- if isUserLoggedIn() {
|
|
|
- showHomeView(profile: nil)
|
|
|
- } else {
|
|
|
- showLoginView()
|
|
|
- }
|
|
|
+ showHomeView(profile: nil)
|
|
|
}
|
|
|
|
|
|
override func viewDidLayout() {
|
|
|
@@ -503,54 +498,7 @@ class ViewController: NSViewController {
|
|
|
])
|
|
|
}
|
|
|
|
|
|
- private func showLoginView() {
|
|
|
- clockTimer?.invalidate()
|
|
|
- meetingsRefreshTimer?.invalidate()
|
|
|
- meetingsRefreshTimer = nil
|
|
|
- clearMeetingsScrollObserver()
|
|
|
- clearMeetingsPageScrollObserver()
|
|
|
- removeSearchFieldObserver()
|
|
|
- removeMeetingSearchObserver()
|
|
|
- removeSearchShortcutMonitor()
|
|
|
- homeSearchField = nil
|
|
|
- homeSearchPill = nil
|
|
|
- meetingSearchOverlayView = nil
|
|
|
- meetingSearchPanelView = nil
|
|
|
- meetingSearchField = nil
|
|
|
- meetingSearchResultsStack = nil
|
|
|
- meetingSearchEmptyLabel = nil
|
|
|
- meetingsPageRootView = nil
|
|
|
- meetingsPageCategoryFilter = nil
|
|
|
- meetingsPageFromDatePicker = nil
|
|
|
- meetingsPageToDatePicker = nil
|
|
|
- meetingsPageRefreshButton = nil
|
|
|
- meetingsPageCountLabel = nil
|
|
|
- meetingsPageStatusLabel = nil
|
|
|
- meetingsPageListStack = nil
|
|
|
- meetingsPageEmptyLabel = nil
|
|
|
- meetingsPageLoadMoreButton = nil
|
|
|
- meetingsPageFilteredMeetings = []
|
|
|
- meetingsPageSelectedRangePreset = "All"
|
|
|
- meetingsPageFromDateFilter = nil
|
|
|
- meetingsPageToDateFilter = nil
|
|
|
- meetingsPageVisibleCount = 0
|
|
|
- allScheduledMeetings = []
|
|
|
- homeView?.removeFromSuperview()
|
|
|
- homeView = nil
|
|
|
- isSigningIn = false
|
|
|
- nextSignInButton?.title = "Next"
|
|
|
- nextSignInButton?.isEnabled = true
|
|
|
- zoomSocialButton?.isEnabled = true
|
|
|
-
|
|
|
- if loginView == nil {
|
|
|
- loginView = makeLoginView()
|
|
|
- }
|
|
|
- guard let loginView else { return }
|
|
|
- attachToRoot(loginView)
|
|
|
- }
|
|
|
-
|
|
|
private func showHomeView(profile: GoogleUserProfile?) {
|
|
|
- loginView?.removeFromSuperview()
|
|
|
clearMeetingsScrollObserver()
|
|
|
clearMeetingsPageScrollObserver()
|
|
|
removeSearchFieldObserver()
|
|
|
@@ -565,14 +513,15 @@ class ViewController: NSViewController {
|
|
|
meetingSearchEmptyLabel = nil
|
|
|
meetingsPageRootView = nil
|
|
|
homeView?.removeFromSuperview()
|
|
|
+ if let profileName = profile?.name?.trimmingCharacters(in: .whitespacesAndNewlines), profileName.isEmpty == false {
|
|
|
+ topBarProfileInitial = String(profileName.prefix(1)).uppercased()
|
|
|
+ }
|
|
|
selectedMeetingsDayStart = Calendar.current.startOfDay(for: Date())
|
|
|
homeView = makeHomeView(profile: profile)
|
|
|
if let homeView { attachToRoot(homeView) }
|
|
|
installSearchShortcutMonitor()
|
|
|
- persistLoggedInState(true)
|
|
|
startClock()
|
|
|
- startMeetingsAutoRefresh()
|
|
|
- triggerMeetingsRefresh(force: true)
|
|
|
+ applyAuthenticationUIState(triggerInitialRefresh: true)
|
|
|
updateHomeSidebarHighlight()
|
|
|
updateSelectedHomeSectionUI()
|
|
|
}
|
|
|
@@ -609,10 +558,9 @@ class ViewController: NSViewController {
|
|
|
let token = try await googleOAuth.validAccessToken(presentingWindow: view.window)
|
|
|
let profile = try? await googleOAuth.fetchUserProfile(accessToken: token)
|
|
|
await MainActor.run {
|
|
|
- self.isSigningIn = false
|
|
|
self.googleButton?.title = "G"
|
|
|
- self.googleButton?.isEnabled = true
|
|
|
- self.showHomeView(profile: profile)
|
|
|
+ self.resetLoginSigningInState()
|
|
|
+ self.completeLoginAndRestart(profile: profile)
|
|
|
}
|
|
|
} catch {
|
|
|
await MainActor.run {
|
|
|
@@ -629,9 +577,8 @@ class ViewController: NSViewController {
|
|
|
@objc private func zoomPrimarySignInTapped() {
|
|
|
guard isSigningIn == false else { return }
|
|
|
isSigningIn = true
|
|
|
- nextSignInButton?.title = "Signing in…"
|
|
|
- nextSignInButton?.isEnabled = false
|
|
|
- zoomSocialButton?.isEnabled = false
|
|
|
+ topBarAuthButton?.title = "Signing in..."
|
|
|
+ topBarAuthButton?.isEnabled = false
|
|
|
googleButton?.isEnabled = false
|
|
|
|
|
|
Task {
|
|
|
@@ -648,7 +595,7 @@ class ViewController: NSViewController {
|
|
|
|
|
|
await MainActor.run {
|
|
|
self.resetLoginSigningInState()
|
|
|
- self.showHomeView(profile: profile)
|
|
|
+ self.completeLoginAndRestart(profile: profile)
|
|
|
}
|
|
|
} catch {
|
|
|
await MainActor.run {
|
|
|
@@ -660,6 +607,10 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
@objc private func scheduleMeetingTapped() {
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ showSimpleAlert(title: "Sign in required", message: "Please sign in with Zoom to schedule a meeting.")
|
|
|
+ return
|
|
|
+ }
|
|
|
if let existing = scheduleMeetingWindow {
|
|
|
existing.makeKeyAndOrderFront(nil)
|
|
|
NSApp.activate(ignoringOtherApps: true)
|
|
|
@@ -1385,7 +1336,33 @@ class ViewController: NSViewController {
|
|
|
googleOAuth.clearSavedTokens()
|
|
|
zoomOAuth.clearSavedTokens()
|
|
|
persistLoggedInState(false)
|
|
|
- showLoginView()
|
|
|
+ allScheduledMeetings = []
|
|
|
+ applyAuthenticationUIState(triggerInitialRefresh: false)
|
|
|
+ Task { @MainActor in
|
|
|
+ self.applyMeetings([])
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func topBarAuthButtonTapped() {
|
|
|
+ if isUserLoggedIn() {
|
|
|
+ showProfileMenu()
|
|
|
+ } else {
|
|
|
+ zoomPrimarySignInTapped()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func profileMenuLogoutTapped() {
|
|
|
+ logoutTapped()
|
|
|
+ }
|
|
|
+
|
|
|
+ private func showProfileMenu() {
|
|
|
+ guard let button = topBarAuthButton else { return }
|
|
|
+ let menu = NSMenu()
|
|
|
+ let logoutItem = NSMenuItem(title: "Logout", action: #selector(profileMenuLogoutTapped), keyEquivalent: "")
|
|
|
+ logoutItem.target = self
|
|
|
+ menu.addItem(logoutItem)
|
|
|
+ let menuOrigin = NSPoint(x: button.bounds.minX, y: button.bounds.minY - 6)
|
|
|
+ menu.popUp(positioning: nil, at: menuOrigin, in: button)
|
|
|
}
|
|
|
|
|
|
@objc private func topBarPlaceholderTapped() {
|
|
|
@@ -1407,6 +1384,10 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
@objc private func refreshMeetingsTapped() {
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ applyGuestLoginMessages()
|
|
|
+ return
|
|
|
+ }
|
|
|
Task { @MainActor in
|
|
|
self.animateMeetingsRefresh()
|
|
|
self.setMeetingsLoadingUI(true)
|
|
|
@@ -1443,6 +1424,7 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
private func startMeetingsAutoRefresh() {
|
|
|
+ guard isUserLoggedIn() else { return }
|
|
|
meetingsRefreshTimer?.invalidate()
|
|
|
// Poll Zoom meetings periodically so newly scheduled meetings appear automatically.
|
|
|
meetingsRefreshTimer = Timer.scheduledTimer(withTimeInterval: meetingsRefreshInterval, repeats: true) { [weak self] _ in
|
|
|
@@ -1452,6 +1434,7 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
private func triggerMeetingsRefresh(force: Bool = false) {
|
|
|
+ guard isUserLoggedIn() else { return }
|
|
|
let now = Date()
|
|
|
if let until = meetingsRateLimitedUntil, now < until {
|
|
|
return
|
|
|
@@ -1782,12 +1765,77 @@ class ViewController: NSViewController {
|
|
|
@MainActor
|
|
|
private func resetLoginSigningInState() {
|
|
|
isSigningIn = false
|
|
|
- nextSignInButton?.title = "Next"
|
|
|
- nextSignInButton?.isEnabled = true
|
|
|
- zoomSocialButton?.isEnabled = true
|
|
|
+ updateTopBarAuthButton()
|
|
|
googleButton?.isEnabled = true
|
|
|
}
|
|
|
|
|
|
+ @MainActor
|
|
|
+ private func completeLoginAndRestart(profile: GoogleUserProfile?) {
|
|
|
+ persistLoggedInState(true)
|
|
|
+ showHomeView(profile: profile)
|
|
|
+ if let window = view.window {
|
|
|
+ window.makeKeyAndOrderFront(nil)
|
|
|
+ }
|
|
|
+ NSApp.activate(ignoringOtherApps: true)
|
|
|
+ }
|
|
|
+
|
|
|
+ @MainActor
|
|
|
+ private func updateTopBarAuthButton() {
|
|
|
+ guard let button = topBarAuthButton else { return }
|
|
|
+ let loggedIn = isUserLoggedIn()
|
|
|
+ if loggedIn {
|
|
|
+ button.title = topBarProfileInitial
|
|
|
+ button.toolTip = "Profile"
|
|
|
+ button.font = .systemFont(ofSize: 14, weight: .bold)
|
|
|
+ button.contentTintColor = primaryText
|
|
|
+ button.layer?.backgroundColor = accentBlue.withAlphaComponent(0.75).cgColor
|
|
|
+ button.layer?.cornerRadius = 10
|
|
|
+ topBarAuthButtonWidthConstraint?.constant = 34
|
|
|
+ } else {
|
|
|
+ button.title = "Sign in with Zoom"
|
|
|
+ button.toolTip = "Sign in with Zoom"
|
|
|
+ button.font = .systemFont(ofSize: 12, weight: .semibold)
|
|
|
+ button.contentTintColor = .white
|
|
|
+ button.layer?.backgroundColor = accentBlue.cgColor
|
|
|
+ button.layer?.cornerRadius = 11
|
|
|
+ topBarAuthButtonWidthConstraint?.constant = 120
|
|
|
+ }
|
|
|
+ button.isEnabled = isSigningIn == false
|
|
|
+ }
|
|
|
+
|
|
|
+ @MainActor
|
|
|
+ private func applyGuestLoginMessages() {
|
|
|
+ meetingsStatusLabel?.stringValue = "Sign in with Zoom to load meetings."
|
|
|
+ emptyMeetingLabel?.isHidden = false
|
|
|
+ emptyMeetingLabel?.stringValue = "Please sign in with Zoom to view your scheduled meetings."
|
|
|
+ meetingsPageStatusLabel?.stringValue = "Sign in with Zoom to view meetings."
|
|
|
+ meetingsPageEmptyLabel?.isHidden = false
|
|
|
+ meetingsPageEmptyLabel?.stringValue = "Please sign in with Zoom to view your meetings."
|
|
|
+ meetingsPageCountLabel?.stringValue = "0 scheduled"
|
|
|
+ schedulerMeetingsEmptyLabel?.isHidden = false
|
|
|
+ schedulerMeetingsEmptyLabel?.stringValue = "Please sign in with Zoom to use Scheduler."
|
|
|
+ }
|
|
|
+
|
|
|
+ @MainActor
|
|
|
+ private func applyAuthenticationUIState(triggerInitialRefresh: Bool) {
|
|
|
+ let loggedIn = isUserLoggedIn()
|
|
|
+ updateTopBarAuthButton()
|
|
|
+ settingsGoogleActionButton?.title = loggedIn ? "Sign Out" : "Sign in with Zoom"
|
|
|
+ settingsGoogleActionButton?.isEnabled = true
|
|
|
+
|
|
|
+ meetingsRefreshTimer?.invalidate()
|
|
|
+ meetingsRefreshTimer = nil
|
|
|
+ meetingsRateLimitedUntil = nil
|
|
|
+ if loggedIn {
|
|
|
+ startMeetingsAutoRefresh()
|
|
|
+ if triggerInitialRefresh {
|
|
|
+ triggerMeetingsRefresh(force: true)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ applyGuestLoginMessages()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// Returns false if the user cancelled or left credentials empty.
|
|
|
@MainActor
|
|
|
private func ensureZoomOAuthClientConfigured() -> Bool {
|
|
|
@@ -1840,6 +1888,15 @@ class ViewController: NSViewController {
|
|
|
view.removeFromSuperview()
|
|
|
}
|
|
|
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ meetingsStatusLabel?.stringValue = "Sign in with Zoom to load meetings."
|
|
|
+ emptyMeetingLabel?.isHidden = false
|
|
|
+ emptyMeetingLabel?.stringValue = "Please sign in with Zoom to view your scheduled meetings."
|
|
|
+ applySchedulerMeetingsForSelectedDate()
|
|
|
+ renderSchedulerCalendarGrid()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
let query = (homeSearchField?.stringValue ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
|
let calendar = Calendar.current
|
|
|
let dayStart = selectedMeetingsDayStart
|
|
|
@@ -1889,6 +1946,12 @@ class ViewController: NSViewController {
|
|
|
|
|
|
@MainActor
|
|
|
private func applyMeetingsPageFilters(resetVisible: Bool) {
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ meetingsPageFilteredMeetings = []
|
|
|
+ meetingsPageVisibleCount = 0
|
|
|
+ renderMeetingsPageList()
|
|
|
+ return
|
|
|
+ }
|
|
|
let calendar = Calendar.current
|
|
|
let now = Date()
|
|
|
let activePreset = meetingsPageCategoryFilter?.titleOfSelectedItem ?? meetingsPageSelectedRangePreset
|
|
|
@@ -1965,6 +2028,15 @@ class ViewController: NSViewController {
|
|
|
view.removeFromSuperview()
|
|
|
}
|
|
|
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ meetingsPageCountLabel?.stringValue = "0 scheduled"
|
|
|
+ meetingsPageStatusLabel?.stringValue = "Sign in with Zoom to view meetings."
|
|
|
+ meetingsPageEmptyLabel?.isHidden = false
|
|
|
+ meetingsPageEmptyLabel?.stringValue = "Please sign in with Zoom to view your meetings."
|
|
|
+ meetingsPageLoadMoreButton?.isHidden = true
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
let totalCount = meetingsPageFilteredMeetings.count
|
|
|
let visibleCount = min(meetingsPageVisibleCount, totalCount)
|
|
|
meetingsPageCountLabel?.stringValue = "\(totalCount) scheduled"
|
|
|
@@ -2133,6 +2205,12 @@ class ViewController: NSViewController {
|
|
|
view.removeFromSuperview()
|
|
|
}
|
|
|
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ schedulerMeetingsEmptyLabel?.isHidden = false
|
|
|
+ schedulerMeetingsEmptyLabel?.stringValue = "Please sign in with Zoom to use Scheduler."
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
let meetings = meetingsForDay(selectedMeetingsDayStart)
|
|
|
if meetings.isEmpty {
|
|
|
schedulerMeetingsEmptyLabel?.isHidden = false
|
|
|
@@ -2183,7 +2261,7 @@ class ViewController: NSViewController {
|
|
|
let dayDate = calendar.date(byAdding: .day, value: dayOffset, to: monthStart) ?? monthStart
|
|
|
let dayNumber = calendar.component(.day, from: dayDate)
|
|
|
let inCurrentMonth = monthRange.contains(dayNumber) && calendar.isDate(dayDate, equalTo: monthStart, toGranularity: .month)
|
|
|
- let hasMeetings = meetingsForDay(dayDate).isEmpty == false
|
|
|
+ let hasMeetings = isUserLoggedIn() && meetingsForDay(dayDate).isEmpty == false
|
|
|
let button = makeSchedulerDayCell(
|
|
|
day: dayNumber,
|
|
|
date: dayDate,
|
|
|
@@ -2301,6 +2379,13 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
private func loadScheduledMeetings() async {
|
|
|
+ guard isUserLoggedIn() else {
|
|
|
+ await MainActor.run {
|
|
|
+ self.applyMeetings([])
|
|
|
+ self.applyGuestLoginMessages()
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
if isLoadingMeetings { return }
|
|
|
isLoadingMeetings = true
|
|
|
defer {
|
|
|
@@ -2596,111 +2681,6 @@ class ViewController: NSViewController {
|
|
|
return mapMeetings(allMeetings)
|
|
|
}
|
|
|
|
|
|
- // MARK: - Login UI
|
|
|
-
|
|
|
- private func makeLoginView() -> NSView {
|
|
|
- let root = NSView()
|
|
|
- let sidebar = makeSidebar(items: ["Home", "Chat", "Phone", "Docs", "Whiteboards", "Clips", "More"], selected: "Home", style: .login)
|
|
|
- let content = NSView()
|
|
|
-
|
|
|
- root.addSubview(sidebar)
|
|
|
- root.addSubview(content)
|
|
|
- sidebar.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- content.translatesAutoresizingMaskIntoConstraints = false
|
|
|
-
|
|
|
- NSLayoutConstraint.activate([
|
|
|
- sidebar.leadingAnchor.constraint(equalTo: root.leadingAnchor),
|
|
|
- sidebar.topAnchor.constraint(equalTo: root.topAnchor),
|
|
|
- sidebar.bottomAnchor.constraint(equalTo: root.bottomAnchor),
|
|
|
- sidebar.widthAnchor.constraint(equalToConstant: sidebarWidth),
|
|
|
- content.leadingAnchor.constraint(equalTo: sidebar.trailingAnchor),
|
|
|
- content.trailingAnchor.constraint(equalTo: root.trailingAnchor),
|
|
|
- content.topAnchor.constraint(equalTo: root.topAnchor),
|
|
|
- content.bottomAnchor.constraint(equalTo: root.bottomAnchor)
|
|
|
- ])
|
|
|
-
|
|
|
- let back = makeLabel("‹ Back", size: 32, color: accentBlue, weight: .regular, centered: false)
|
|
|
- let logo = makeLabel("zoom\nWorkplace", size: 24, color: primaryText, weight: .bold, centered: true)
|
|
|
- logo.maximumNumberOfLines = 2
|
|
|
- let domain = makeLabel("us05web.zoom.us", size: 16, color: primaryText, weight: .semibold, centered: true)
|
|
|
-
|
|
|
- let emailField = NSTextField()
|
|
|
- emailField.placeholderString = "Email or phone number"
|
|
|
- emailField.font = .systemFont(ofSize: 20, weight: .regular)
|
|
|
- emailField.textColor = .white
|
|
|
- emailField.wantsLayer = true
|
|
|
- emailField.layer?.cornerRadius = 10
|
|
|
- emailField.layer?.borderWidth = 1.5
|
|
|
- emailField.layer?.borderColor = accentBlue.cgColor
|
|
|
- emailField.layer?.backgroundColor = cardBackground.cgColor
|
|
|
- emailField.focusRingType = .none
|
|
|
-
|
|
|
- let nextButton = NSButton(title: "Next", target: self, action: #selector(zoomPrimarySignInTapped))
|
|
|
- nextButton.font = .systemFont(ofSize: 20, weight: .semibold)
|
|
|
- nextButton.isBordered = false
|
|
|
- nextButton.wantsLayer = true
|
|
|
- nextButton.layer?.cornerRadius = 10
|
|
|
- nextButton.layer?.backgroundColor = cardBackground.cgColor
|
|
|
- nextButton.contentTintColor = primaryText
|
|
|
-
|
|
|
- let divider = NSBox()
|
|
|
- divider.boxType = .separator
|
|
|
- let socialText = makeLabel("or sign in with", size: 14, color: secondaryText, weight: .regular, centered: true)
|
|
|
-
|
|
|
- let sso = makeSocialButton(icon: "🔑", text: "SSO")
|
|
|
- let google = makeSocialButton(icon: "G", text: "Google", action: #selector(googleLoginTapped))
|
|
|
- let apple = makeSocialButton(icon: "", text: "Apple")
|
|
|
- let facebook = makeSocialButton(icon: "f", text: "Facebook")
|
|
|
- let zoomSocial = makeSocialButton(icon: "Z", text: "Zoom", action: #selector(zoomPrimarySignInTapped))
|
|
|
- self.googleButton = google.button
|
|
|
- self.nextSignInButton = nextButton
|
|
|
- self.zoomSocialButton = zoomSocial.button
|
|
|
-
|
|
|
- let social = NSStackView(views: [sso.container, google.container, apple.container, facebook.container, zoomSocial.container])
|
|
|
- social.orientation = .horizontal
|
|
|
- social.spacing = 14
|
|
|
- social.distribution = .fillEqually
|
|
|
-
|
|
|
- let signup = makeLabel("Don't have an account? Sign up", size: 15, color: primaryText, weight: .regular, centered: true)
|
|
|
- let footer = makeLabel("Help Terms Privacy", size: 14, color: accentBlue, weight: .regular, centered: true)
|
|
|
-
|
|
|
- [back, logo, domain, emailField, nextButton, divider, socialText, social, signup, footer].forEach {
|
|
|
- $0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- content.addSubview($0)
|
|
|
- }
|
|
|
-
|
|
|
- NSLayoutConstraint.activate([
|
|
|
- back.topAnchor.constraint(equalTo: content.topAnchor, constant: 24),
|
|
|
- back.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 34),
|
|
|
- logo.topAnchor.constraint(equalTo: content.topAnchor, constant: 118),
|
|
|
- logo.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- domain.topAnchor.constraint(equalTo: logo.bottomAnchor, constant: 12),
|
|
|
- domain.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- emailField.topAnchor.constraint(equalTo: domain.bottomAnchor, constant: 30),
|
|
|
- emailField.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- emailField.widthAnchor.constraint(equalToConstant: 520),
|
|
|
- emailField.heightAnchor.constraint(equalToConstant: 52),
|
|
|
- nextButton.topAnchor.constraint(equalTo: emailField.bottomAnchor, constant: 20),
|
|
|
- nextButton.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- nextButton.widthAnchor.constraint(equalTo: emailField.widthAnchor),
|
|
|
- nextButton.heightAnchor.constraint(equalToConstant: 52),
|
|
|
- divider.topAnchor.constraint(equalTo: nextButton.bottomAnchor, constant: 28),
|
|
|
- divider.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- divider.widthAnchor.constraint(equalTo: emailField.widthAnchor),
|
|
|
- socialText.centerYAnchor.constraint(equalTo: divider.centerYAnchor),
|
|
|
- socialText.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- social.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 18),
|
|
|
- social.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- social.widthAnchor.constraint(equalTo: emailField.widthAnchor),
|
|
|
- signup.topAnchor.constraint(equalTo: social.bottomAnchor, constant: 14),
|
|
|
- signup.centerXAnchor.constraint(equalTo: content.centerXAnchor),
|
|
|
- footer.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -16),
|
|
|
- footer.centerXAnchor.constraint(equalTo: content.centerXAnchor)
|
|
|
- ])
|
|
|
-
|
|
|
- return root
|
|
|
- }
|
|
|
-
|
|
|
private func makeSettingsView() -> NSView {
|
|
|
let panel = NSView()
|
|
|
panel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -2937,7 +2917,7 @@ class ViewController: NSViewController {
|
|
|
logoutTapped()
|
|
|
return
|
|
|
}
|
|
|
- showSimpleAlert(title: "Sign in", message: "Please sign in from the login screen to connect your account.")
|
|
|
+ zoomPrimarySignInTapped()
|
|
|
}
|
|
|
|
|
|
private func roundedContainer(cornerRadius: CGFloat, color: NSColor) -> NSView {
|
|
|
@@ -3056,13 +3036,9 @@ class ViewController: NSViewController {
|
|
|
applyWindowBackgroundForCurrentTheme(window)
|
|
|
}
|
|
|
|
|
|
- if isUserLoggedIn() {
|
|
|
- let keepSelected = selectedHomeSidebarItem
|
|
|
- selectedHomeSidebarItem = keepSelected
|
|
|
- showHomeView(profile: nil)
|
|
|
- } else {
|
|
|
- showLoginView()
|
|
|
- }
|
|
|
+ let keepSelected = selectedHomeSidebarItem
|
|
|
+ selectedHomeSidebarItem = keepSelected
|
|
|
+ showHomeView(profile: nil)
|
|
|
}
|
|
|
|
|
|
private func applyWindowBackgroundForCurrentTheme(_ window: NSWindow) {
|
|
|
@@ -3737,15 +3713,15 @@ class ViewController: NSViewController {
|
|
|
topBarPremiumButton = upgradeToProButton
|
|
|
updateTopBarPremiumButton()
|
|
|
|
|
|
- let profileChip = NSButton(title: String((profile?.name ?? "H").prefix(1)).uppercased(), target: self, action: #selector(logoutTapped))
|
|
|
- profileChip.isBordered = false
|
|
|
- profileChip.wantsLayer = true
|
|
|
- profileChip.layer?.backgroundColor = accentBlue.withAlphaComponent(0.75).cgColor
|
|
|
- profileChip.layer?.cornerRadius = 10
|
|
|
- profileChip.contentTintColor = primaryText
|
|
|
- profileChip.font = .systemFont(ofSize: 14, weight: .bold)
|
|
|
- profileChip.toolTip = "Profile (click to logout)"
|
|
|
- [upgradeToProButton, profileChip].forEach { rightTopBarCluster.addArrangedSubview($0) }
|
|
|
+ let authButton = NSButton(title: "", target: self, action: #selector(topBarAuthButtonTapped))
|
|
|
+ authButton.isBordered = false
|
|
|
+ authButton.wantsLayer = true
|
|
|
+ authButton.layer?.cornerRadius = 11
|
|
|
+ authButton.contentTintColor = .white
|
|
|
+ authButton.font = .systemFont(ofSize: 12, weight: .semibold)
|
|
|
+ authButton.bezelStyle = .shadowlessSquare
|
|
|
+ self.topBarAuthButton = authButton
|
|
|
+ [upgradeToProButton, authButton].forEach { rightTopBarCluster.addArrangedSubview($0) }
|
|
|
|
|
|
let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
|
|
|
let timeTitle = makeLabel("--:--", size: 56, color: primaryText, weight: .bold, centered: true)
|
|
|
@@ -4088,8 +4064,7 @@ class ViewController: NSViewController {
|
|
|
searchField.leadingAnchor.constraint(equalTo: searchIcon.trailingAnchor, constant: 8),
|
|
|
searchField.trailingAnchor.constraint(equalTo: searchPill.trailingAnchor, constant: -10),
|
|
|
searchField.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
|
|
|
- profileChip.widthAnchor.constraint(equalToConstant: 34),
|
|
|
- profileChip.heightAnchor.constraint(equalToConstant: 34),
|
|
|
+ authButton.heightAnchor.constraint(equalToConstant: 30),
|
|
|
|
|
|
welcome.topAnchor.constraint(equalTo: contentColumn.topAnchor, constant: 18),
|
|
|
welcome.centerXAnchor.constraint(equalTo: contentColumn.centerXAnchor),
|
|
|
@@ -4300,6 +4275,10 @@ class ViewController: NSViewController {
|
|
|
self.applyGlobalMeetingSearch(query: popupSearchField.stringValue)
|
|
|
}
|
|
|
applyGlobalMeetingSearch(query: "")
|
|
|
+ topBarAuthButtonWidthConstraint = authButton.widthAnchor.constraint(equalToConstant: 120)
|
|
|
+ topBarAuthButtonWidthConstraint?.isActive = true
|
|
|
+ updateTopBarAuthButton()
|
|
|
+ applyAuthenticationUIState(triggerInitialRefresh: false)
|
|
|
|
|
|
return root
|
|
|
}
|
|
|
@@ -4522,7 +4501,7 @@ class ViewController: NSViewController {
|
|
|
|
|
|
// MARK: - Shared UI
|
|
|
|
|
|
- private func makeSidebar(items: [String], selected: String, style: SidebarStyle = .login) -> NSView {
|
|
|
+ private func makeSidebar(items: [String], selected: String, style: SidebarStyle = .home) -> NSView {
|
|
|
let sidebar = NSView()
|
|
|
sidebar.wantsLayer = true
|
|
|
sidebar.layer?.backgroundColor = (style == .home ? chromeUnifiedBackground : sidebarBackground).cgColor
|
|
|
@@ -4724,11 +4703,8 @@ class ViewController: NSViewController {
|
|
|
NSLayoutConstraint.activate([
|
|
|
stack.topAnchor.constraint(equalTo: sidebar.topAnchor, constant: 10)
|
|
|
])
|
|
|
- stack.bottomAnchor.constraint(lessThanOrEqualTo: sidebar.bottomAnchor, constant: -18).isActive = true
|
|
|
- } else {
|
|
|
- stack.topAnchor.constraint(equalTo: sidebar.topAnchor, constant: 18).isActive = true
|
|
|
- stack.bottomAnchor.constraint(lessThanOrEqualTo: sidebar.bottomAnchor, constant: -18).isActive = true
|
|
|
}
|
|
|
+ stack.bottomAnchor.constraint(lessThanOrEqualTo: sidebar.bottomAnchor, constant: -18).isActive = true
|
|
|
|
|
|
return sidebar
|
|
|
}
|