|
@@ -33,8 +33,6 @@ private let embeddedZoomWebViewUserAgent =
|
|
|
|
|
|
|
|
/// Shared cookie jar for embedded Zoom meetings and Zoom OAuth so the in-app browser reuses the same Zoom web session.
|
|
/// Shared cookie jar for embedded Zoom meetings and Zoom OAuth so the in-app browser reuses the same Zoom web session.
|
|
|
private let zoomEmbeddedWebsiteDataStore = WKWebsiteDataStore.default()
|
|
private let zoomEmbeddedWebsiteDataStore = WKWebsiteDataStore.default()
|
|
|
-/// Shared renderer process so OAuth cookies are visible to the embedded meeting web client.
|
|
|
|
|
-private let zoomEmbeddedWebProcessPool = WKProcessPool()
|
|
|
|
|
|
|
|
|
|
/// Errors from the main-window embedded OAuth web session (right panel).
|
|
/// Errors from the main-window embedded OAuth web session (right panel).
|
|
|
private enum EmbeddedOAuthFlowError: Error {
|
|
private enum EmbeddedOAuthFlowError: Error {
|
|
@@ -454,8 +452,11 @@ class ViewController: NSViewController {
|
|
|
private weak var homeMainContentColumnView: NSView?
|
|
private weak var homeMainContentColumnView: NSView?
|
|
|
private weak var embeddedBrowserBackButton: NSButton?
|
|
private weak var embeddedBrowserBackButton: NSButton?
|
|
|
private weak var embeddedBrowserForwardButton: NSButton?
|
|
private weak var embeddedBrowserForwardButton: NSButton?
|
|
|
- private var embeddedBrowserPanelWidthConstraint: NSLayoutConstraint?
|
|
|
|
|
- private var embeddedBrowserMainColumnCollapsedWidthConstraint: NSLayoutConstraint?
|
|
|
|
|
|
|
+ private var embeddedBrowserPanelWidthConstraint: NSLayoutConstraint? // unused; panel visibility is stack-driven
|
|
|
|
|
+ private var mainColumnVerticalFillConstraints: [NSLayoutConstraint] = []
|
|
|
|
|
+ private var webPanelVerticalFillConstraints: [NSLayoutConstraint] = []
|
|
|
|
|
+ /// Tracks expanded layout mode (NSStackView hides the inactive column out of Auto Layout).
|
|
|
|
|
+ private var isEmbeddedBrowserPanelExpanded = false
|
|
|
private var oauthSessionContinuation: CheckedContinuation<URL, Error>?
|
|
private var oauthSessionContinuation: CheckedContinuation<URL, Error>?
|
|
|
private var oauthSessionCallbackPrefix: String?
|
|
private var oauthSessionCallbackPrefix: String?
|
|
|
/// ZAK + display name for the next embedded join; used to re-auth the web client if sign-in or guest join appears.
|
|
/// ZAK + display name for the next embedded join; used to re-auth the web client if sign-in or guest join appears.
|
|
@@ -889,7 +890,7 @@ class ViewController: NSViewController {
|
|
|
updatePaywallPanelSize()
|
|
updatePaywallPanelSize()
|
|
|
updatePaywallResponsiveLayout()
|
|
updatePaywallResponsiveLayout()
|
|
|
}
|
|
}
|
|
|
- if (embeddedBrowserPanelWidthConstraint?.constant ?? 0) > 0.5 {
|
|
|
|
|
|
|
+ if isEmbeddedBrowserPanelExpanded {
|
|
|
updateEmbeddedRightPanelWidthForOpenState()
|
|
updateEmbeddedRightPanelWidthForOpenState()
|
|
|
}
|
|
}
|
|
|
if aiCompanionPageRootView?.isHidden == false {
|
|
if aiCompanionPageRootView?.isHidden == false {
|
|
@@ -920,7 +921,9 @@ class ViewController: NSViewController {
|
|
|
object: window,
|
|
object: window,
|
|
|
queue: .main
|
|
queue: .main
|
|
|
) { [weak self] _ in
|
|
) { [weak self] _ in
|
|
|
- self?.syncBrandPositionForCurrentWindowState(animated: true)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated {
|
|
|
|
|
+ self?.syncBrandPositionForCurrentWindowState(animated: true)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let exitObserver = center.addObserver(
|
|
let exitObserver = center.addObserver(
|
|
@@ -928,7 +931,9 @@ class ViewController: NSViewController {
|
|
|
object: window,
|
|
object: window,
|
|
|
queue: .main
|
|
queue: .main
|
|
|
) { [weak self] _ in
|
|
) { [weak self] _ in
|
|
|
- self?.syncBrandPositionForCurrentWindowState(animated: true)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated {
|
|
|
|
|
+ self?.syncBrandPositionForCurrentWindowState(animated: true)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
windowFullScreenObservers = [enterObserver, exitObserver]
|
|
windowFullScreenObservers = [enterObserver, exitObserver]
|
|
@@ -2600,9 +2605,11 @@ class ViewController: NSViewController {
|
|
|
embeddedMeetingReachedInCallPage = false
|
|
embeddedMeetingReachedInCallPage = false
|
|
|
embeddedMeetingAbsentCallConsecutiveMisses = 0
|
|
embeddedMeetingAbsentCallConsecutiveMisses = 0
|
|
|
let timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
|
|
let timer = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
|
|
|
- guard let self else { return }
|
|
|
|
|
- let webView = self.embeddedBrowserWebView
|
|
|
|
|
- self.updateEmbeddedMeetingRecordingPresence(for: webView?.url, webViewTitle: webView?.title)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated {
|
|
|
|
|
+ guard let self else { return }
|
|
|
|
|
+ let webView = self.embeddedBrowserWebView
|
|
|
|
|
+ self.updateEmbeddedMeetingRecordingPresence(for: webView?.url, webViewTitle: webView?.title)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
RunLoop.main.add(timer, forMode: .common)
|
|
RunLoop.main.add(timer, forMode: .common)
|
|
|
embeddedMeetingRecordingMonitorTimer = timer
|
|
embeddedMeetingRecordingMonitorTimer = timer
|
|
@@ -2639,8 +2646,7 @@ class ViewController: NSViewController {
|
|
|
guard MeetingRecordingManager.shared.isRecording == false else { return }
|
|
guard MeetingRecordingManager.shared.isRecording == false else { return }
|
|
|
pendingZoomMeetingRecording = (normalizedEmbeddedJoinURL(for: sourceURL), meetingDisplayName)
|
|
pendingZoomMeetingRecording = (normalizedEmbeddedJoinURL(for: sourceURL), meetingDisplayName)
|
|
|
updateAiCompanionRecordingUI()
|
|
updateAiCompanionRecordingUI()
|
|
|
- if embeddedBrowserWebView != nil,
|
|
|
|
|
- (embeddedBrowserPanelWidthConstraint?.constant ?? 0) > 0.5 {
|
|
|
|
|
|
|
+ if embeddedBrowserWebView != nil, isEmbeddedBrowserPanelExpanded {
|
|
|
startEmbeddedMeetingRecordingMonitorIfNeeded()
|
|
startEmbeddedMeetingRecordingMonitorIfNeeded()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -2689,7 +2695,7 @@ class ViewController: NSViewController {
|
|
|
|
|
|
|
|
@MainActor
|
|
@MainActor
|
|
|
private var isEmbeddedBrowserPanelOpen: Bool {
|
|
private var isEmbeddedBrowserPanelOpen: Bool {
|
|
|
- (embeddedBrowserPanelWidthConstraint?.constant ?? 0) > 0.5
|
|
|
|
|
|
|
+ isEmbeddedBrowserPanelExpanded
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@MainActor
|
|
@MainActor
|
|
@@ -2745,24 +2751,28 @@ class ViewController: NSViewController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// Makes the embedded web panel fill the entire right-side content area (to match the reference behavior).
|
|
|
|
|
|
|
+ /// Expands the embedded web panel to fill the body row via stack arranged-subview hiding.
|
|
|
|
|
+ /// Never force `width == 0` — that fights WKWebView/toolbar intrinsic sizes (see console conflict log).
|
|
|
@MainActor
|
|
@MainActor
|
|
|
private func updateEmbeddedRightPanelWidthForOpenState() {
|
|
private func updateEmbeddedRightPanelWidthForOpenState() {
|
|
|
- guard let widthConstraint = embeddedBrowserPanelWidthConstraint else { return }
|
|
|
|
|
|
|
+ if isEmbeddedBrowserPanelExpanded == false {
|
|
|
|
|
+ isEmbeddedBrowserPanelExpanded = true
|
|
|
|
|
+ NSLayoutConstraint.deactivate(mainColumnVerticalFillConstraints)
|
|
|
|
|
+ NSLayoutConstraint.activate(webPanelVerticalFillConstraints)
|
|
|
|
|
+ homeMainContentColumnView?.isHidden = true
|
|
|
|
|
+ embeddedBrowserPanel?.isHidden = false
|
|
|
|
|
+ }
|
|
|
view.layoutSubtreeIfNeeded()
|
|
view.layoutSubtreeIfNeeded()
|
|
|
view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
|
- var rowW = homeBodyRowView?.bounds.width ?? 0
|
|
|
|
|
- if rowW < 1 {
|
|
|
|
|
- rowW = view.bounds.width
|
|
|
|
|
- }
|
|
|
|
|
- // Right panel spans from (bodyRow.leading + gap) to bodyRow.trailing when the main column is collapsed (width 0).
|
|
|
|
|
- // Using the full row width here over-constrains by the column gap and nudges the window wider.
|
|
|
|
|
- let available = max(0, rowW - homeEmbeddedBrowserColumnGap)
|
|
|
|
|
- widthConstraint.constant = max(320, floor(available))
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Collapse the left main content column while web is visible.
|
|
|
|
|
- homeMainContentColumnView?.isHidden = true
|
|
|
|
|
- embeddedBrowserMainColumnCollapsedWidthConstraint?.isActive = true
|
|
|
|
|
|
|
+ @MainActor
|
|
|
|
|
+ private func restoreEmbeddedBrowserClosedLayout() {
|
|
|
|
|
+ isEmbeddedBrowserPanelExpanded = false
|
|
|
|
|
+ NSLayoutConstraint.deactivate(webPanelVerticalFillConstraints)
|
|
|
|
|
+ NSLayoutConstraint.activate(mainColumnVerticalFillConstraints)
|
|
|
|
|
+ embeddedBrowserPanel?.isHidden = true
|
|
|
|
|
+ homeMainContentColumnView?.isHidden = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@MainActor
|
|
@MainActor
|
|
@@ -2823,9 +2833,7 @@ class ViewController: NSViewController {
|
|
|
}
|
|
}
|
|
|
embeddedZoomPendingWebAuth = nil
|
|
embeddedZoomPendingWebAuth = nil
|
|
|
embeddedZoomPendingJoinURL = nil
|
|
embeddedZoomPendingJoinURL = nil
|
|
|
- embeddedBrowserPanelWidthConstraint?.constant = 0
|
|
|
|
|
- embeddedBrowserMainColumnCollapsedWidthConstraint?.isActive = false
|
|
|
|
|
- homeMainContentColumnView?.isHidden = false
|
|
|
|
|
|
|
+ restoreEmbeddedBrowserClosedLayout()
|
|
|
refreshEmbeddedBrowserNavButtons()
|
|
refreshEmbeddedBrowserNavButtons()
|
|
|
view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
|
}
|
|
}
|
|
@@ -2874,7 +2882,7 @@ class ViewController: NSViewController {
|
|
|
self.oauthSessionContinuation = nil
|
|
self.oauthSessionContinuation = nil
|
|
|
self.oauthSessionCallbackPrefix = nil
|
|
self.oauthSessionCallbackPrefix = nil
|
|
|
webView.stopLoading()
|
|
webView.stopLoading()
|
|
|
- self.embeddedBrowserPanelWidthConstraint?.constant = 0
|
|
|
|
|
|
|
+ self.restoreEmbeddedBrowserClosedLayout()
|
|
|
self.refreshEmbeddedBrowserNavButtons()
|
|
self.refreshEmbeddedBrowserNavButtons()
|
|
|
webView.load(URLRequest(url: URL(string: "about:blank")!))
|
|
webView.load(URLRequest(url: URL(string: "about:blank")!))
|
|
|
self.view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
self.view.window?.contentView?.layoutSubtreeIfNeeded()
|
|
@@ -6146,12 +6154,14 @@ class ViewController: NSViewController {
|
|
|
continueButtonWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
continueButtonWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
continueButtonWrap.addSubview(continueButton)
|
|
continueButtonWrap.addSubview(continueButton)
|
|
|
planColumn.addArrangedSubview(continueButtonWrap)
|
|
planColumn.addArrangedSubview(continueButtonWrap)
|
|
|
|
|
+ let continuePreferredWidth = continueButton.widthAnchor.constraint(equalToConstant: 400)
|
|
|
|
|
+ continuePreferredWidth.priority = .defaultHigh
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
continueButtonWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor),
|
|
continueButtonWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor),
|
|
|
continueButton.topAnchor.constraint(equalTo: continueButtonWrap.topAnchor),
|
|
continueButton.topAnchor.constraint(equalTo: continueButtonWrap.topAnchor),
|
|
|
continueButton.bottomAnchor.constraint(equalTo: continueButtonWrap.bottomAnchor),
|
|
continueButton.bottomAnchor.constraint(equalTo: continueButtonWrap.bottomAnchor),
|
|
|
continueButton.centerXAnchor.constraint(equalTo: continueButtonWrap.centerXAnchor),
|
|
continueButton.centerXAnchor.constraint(equalTo: continueButtonWrap.centerXAnchor),
|
|
|
- continueButton.widthAnchor.constraint(equalToConstant: 400),
|
|
|
|
|
|
|
+ continuePreferredWidth,
|
|
|
continueButton.widthAnchor.constraint(lessThanOrEqualTo: continueButtonWrap.widthAnchor, constant: -40)
|
|
continueButton.widthAnchor.constraint(lessThanOrEqualTo: continueButtonWrap.widthAnchor, constant: -40)
|
|
|
])
|
|
])
|
|
|
planColumn.setCustomSpacing(16, after: continueButtonWrap)
|
|
planColumn.setCustomSpacing(16, after: continueButtonWrap)
|
|
@@ -6225,21 +6235,18 @@ class ViewController: NSViewController {
|
|
|
planColumn.addArrangedSubview(footerWrap)
|
|
planColumn.addArrangedSubview(footerWrap)
|
|
|
footerWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
footerWrap.widthAnchor.constraint(equalTo: planColumn.widthAnchor).isActive = true
|
|
|
|
|
|
|
|
- // Keep a comfortable top margin, but center the paywall content vertically when there's room.
|
|
|
|
|
|
|
+ // Keep a comfortable top margin; center when there's room. Avoid dual required-style
|
|
|
|
|
+ // bottom anchors (centerY + bottomEq) which fight when content is taller than the panel.
|
|
|
let contentTopMin = contentStack.topAnchor.constraint(greaterThanOrEqualTo: panel.topAnchor, constant: 84)
|
|
let contentTopMin = contentStack.topAnchor.constraint(greaterThanOrEqualTo: panel.topAnchor, constant: 84)
|
|
|
let contentBottomMax = contentStack.bottomAnchor.constraint(lessThanOrEqualTo: panel.bottomAnchor, constant: -18)
|
|
let contentBottomMax = contentStack.bottomAnchor.constraint(lessThanOrEqualTo: panel.bottomAnchor, constant: -18)
|
|
|
let contentCenterY = contentStack.centerYAnchor.constraint(equalTo: panel.centerYAnchor)
|
|
let contentCenterY = contentStack.centerYAnchor.constraint(equalTo: panel.centerYAnchor)
|
|
|
contentCenterY.priority = .defaultLow
|
|
contentCenterY.priority = .defaultLow
|
|
|
-
|
|
|
|
|
- let contentBottomEq = contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -18)
|
|
|
|
|
- contentBottomEq.priority = .defaultLow
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 70),
|
|
contentStack.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 70),
|
|
|
contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -70),
|
|
contentStack.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -70),
|
|
|
contentTopMin,
|
|
contentTopMin,
|
|
|
contentBottomMax,
|
|
contentBottomMax,
|
|
|
- contentCenterY,
|
|
|
|
|
- contentBottomEq
|
|
|
|
|
|
|
+ contentCenterY
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
refreshPaywallStoreUI()
|
|
refreshPaywallStoreUI()
|
|
@@ -7207,11 +7214,16 @@ class ViewController: NSViewController {
|
|
|
let schedulerEmptyLabel = makeLabel("No meetings scheduled for this date.", size: 15, color: secondaryText, weight: .regular, centered: true)
|
|
let schedulerEmptyLabel = makeLabel("No meetings scheduled for this date.", size: 15, color: secondaryText, weight: .regular, centered: true)
|
|
|
schedulerEmptyLabel.isHidden = true
|
|
schedulerEmptyLabel.isHidden = true
|
|
|
|
|
|
|
|
- let bodyRow = NSView()
|
|
|
|
|
|
|
+ let bodyRow = NSStackView()
|
|
|
|
|
+ bodyRow.orientation = .horizontal
|
|
|
|
|
+ bodyRow.alignment = .top
|
|
|
|
|
+ bodyRow.distribution = .fill
|
|
|
|
|
+ bodyRow.spacing = homeEmbeddedBrowserColumnGap
|
|
|
bodyRow.translatesAutoresizingMaskIntoConstraints = false
|
|
bodyRow.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
|
|
let mainContentColumn = NSView()
|
|
let mainContentColumn = NSView()
|
|
|
mainContentColumn.translatesAutoresizingMaskIntoConstraints = false
|
|
mainContentColumn.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
+ mainContentColumn.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
mainContentColumn.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
mainContentColumn.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
|
|
|
let rightWebPanel = NSView()
|
|
let rightWebPanel = NSView()
|
|
@@ -7222,6 +7234,10 @@ class ViewController: NSViewController {
|
|
|
rightWebPanel.layer?.backgroundColor = secondaryCardBackground.withAlphaComponent(0.94).cgColor
|
|
rightWebPanel.layer?.backgroundColor = secondaryCardBackground.withAlphaComponent(0.94).cgColor
|
|
|
rightWebPanel.layer?.borderWidth = 1
|
|
rightWebPanel.layer?.borderWidth = 1
|
|
|
rightWebPanel.layer?.borderColor = palette.inputBorder.cgColor
|
|
rightWebPanel.layer?.borderColor = palette.inputBorder.cgColor
|
|
|
|
|
+ rightWebPanel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ rightWebPanel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
|
|
|
+ // Closed by default: stack hiding removes this column from Auto Layout (no width==0).
|
|
|
|
|
+ rightWebPanel.isHidden = true
|
|
|
|
|
|
|
|
let webToolbar = NSView()
|
|
let webToolbar = NSView()
|
|
|
webToolbar.translatesAutoresizingMaskIntoConstraints = false
|
|
webToolbar.translatesAutoresizingMaskIntoConstraints = false
|
|
@@ -7267,7 +7283,6 @@ class ViewController: NSViewController {
|
|
|
|
|
|
|
|
let embeddedBrowserConfiguration = WKWebViewConfiguration()
|
|
let embeddedBrowserConfiguration = WKWebViewConfiguration()
|
|
|
embeddedBrowserConfiguration.websiteDataStore = zoomEmbeddedWebsiteDataStore
|
|
embeddedBrowserConfiguration.websiteDataStore = zoomEmbeddedWebsiteDataStore
|
|
|
- embeddedBrowserConfiguration.processPool = zoomEmbeddedWebProcessPool
|
|
|
|
|
embeddedBrowserConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true
|
|
embeddedBrowserConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true
|
|
|
embeddedBrowserConfiguration.mediaTypesRequiringUserActionForPlayback = []
|
|
embeddedBrowserConfiguration.mediaTypesRequiringUserActionForPlayback = []
|
|
|
let meetingLifecycleController = WKUserContentController()
|
|
let meetingLifecycleController = WKUserContentController()
|
|
@@ -7290,16 +7305,12 @@ class ViewController: NSViewController {
|
|
|
content.addSubview(topBar)
|
|
content.addSubview(topBar)
|
|
|
content.addSubview(topBarDivider)
|
|
content.addSubview(topBarDivider)
|
|
|
content.addSubview(bodyRow)
|
|
content.addSubview(bodyRow)
|
|
|
- bodyRow.addSubview(mainContentColumn)
|
|
|
|
|
- bodyRow.addSubview(rightWebPanel)
|
|
|
|
|
|
|
+ bodyRow.addArrangedSubview(mainContentColumn)
|
|
|
|
|
+ bodyRow.addArrangedSubview(rightWebPanel)
|
|
|
rightWebPanel.addSubview(webToolbar)
|
|
rightWebPanel.addSubview(webToolbar)
|
|
|
webToolbar.addSubview(webToolBarStack)
|
|
webToolbar.addSubview(webToolBarStack)
|
|
|
rightWebPanel.addSubview(embeddedWK)
|
|
rightWebPanel.addSubview(embeddedWK)
|
|
|
|
|
|
|
|
- let rightPanelWidthConstraint = rightWebPanel.widthAnchor.constraint(equalToConstant: 0)
|
|
|
|
|
- let mainColumnCollapsedWidthConstraint = mainContentColumn.widthAnchor.constraint(equalToConstant: 0)
|
|
|
|
|
- mainColumnCollapsedWidthConstraint.isActive = false
|
|
|
|
|
-
|
|
|
|
|
[brandStack, searchRow, rightTopBarCluster, searchPill, searchField, searchIcon, searchHintLabel].forEach {
|
|
[brandStack, searchRow, rightTopBarCluster, searchPill, searchField, searchIcon, searchHintLabel].forEach {
|
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
}
|
|
}
|
|
@@ -7334,14 +7345,23 @@ class ViewController: NSViewController {
|
|
|
meetingsDocument.addSubview(meetingsStack)
|
|
meetingsDocument.addSubview(meetingsStack)
|
|
|
|
|
|
|
|
let searchRowCenterX = searchRow.centerXAnchor.constraint(equalTo: topBar.centerXAnchor)
|
|
let searchRowCenterX = searchRow.centerXAnchor.constraint(equalTo: topBar.centerXAnchor)
|
|
|
- searchRowCenterX.priority = .defaultHigh
|
|
|
|
|
|
|
+ // Prefer centering, but yield when space is tight.
|
|
|
|
|
+ searchRowCenterX.priority = .defaultLow
|
|
|
|
|
|
|
|
let brandLeading = brandStack.leadingAnchor.constraint(equalTo: shell.leadingAnchor, constant: brandLeadingInset)
|
|
let brandLeading = brandStack.leadingAnchor.constraint(equalTo: shell.leadingAnchor, constant: brandLeadingInset)
|
|
|
brandLeadingConstraint = brandLeading
|
|
brandLeadingConstraint = brandLeading
|
|
|
|
|
|
|
|
|
|
+ // Prefer 610pt, but allow shrink.
|
|
|
|
|
+ let homeMeetingsPanelWidth = panel.widthAnchor.constraint(equalToConstant: 610)
|
|
|
|
|
+ homeMeetingsPanelWidth.priority = .defaultHigh
|
|
|
|
|
+
|
|
|
|
|
+ let searchPillWidth = searchPill.widthAnchor.constraint(equalToConstant: 320)
|
|
|
|
|
+ searchPillWidth.priority = .defaultHigh
|
|
|
|
|
+
|
|
|
NSLayoutConstraint.activate([
|
|
NSLayoutConstraint.activate([
|
|
|
|
|
+ // Brand is a shell overlay only — do not pin it to searchRow (cross-subtree constraints
|
|
|
|
|
+ // routinely conflict at launch with the centered search pill + auth cluster).
|
|
|
brandLeading,
|
|
brandLeading,
|
|
|
- brandStack.trailingAnchor.constraint(lessThanOrEqualTo: searchRow.leadingAnchor, constant: -12),
|
|
|
|
|
brandStack.centerYAnchor.constraint(equalTo: chromeHeader.centerYAnchor, constant: -1),
|
|
brandStack.centerYAnchor.constraint(equalTo: chromeHeader.centerYAnchor, constant: -1),
|
|
|
|
|
|
|
|
topBar.topAnchor.constraint(equalTo: content.topAnchor),
|
|
topBar.topAnchor.constraint(equalTo: content.topAnchor),
|
|
@@ -7358,16 +7378,6 @@ class ViewController: NSViewController {
|
|
|
bodyRow.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8),
|
|
bodyRow.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8),
|
|
|
bodyRow.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8),
|
|
bodyRow.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8),
|
|
|
|
|
|
|
|
- mainContentColumn.leadingAnchor.constraint(equalTo: bodyRow.leadingAnchor),
|
|
|
|
|
- mainContentColumn.topAnchor.constraint(equalTo: bodyRow.topAnchor),
|
|
|
|
|
- mainContentColumn.bottomAnchor.constraint(equalTo: bodyRow.bottomAnchor),
|
|
|
|
|
- mainContentColumn.trailingAnchor.constraint(equalTo: rightWebPanel.leadingAnchor, constant: -homeEmbeddedBrowserColumnGap),
|
|
|
|
|
-
|
|
|
|
|
- rightWebPanel.topAnchor.constraint(equalTo: bodyRow.topAnchor),
|
|
|
|
|
- rightWebPanel.bottomAnchor.constraint(equalTo: bodyRow.bottomAnchor),
|
|
|
|
|
- rightWebPanel.trailingAnchor.constraint(equalTo: bodyRow.trailingAnchor),
|
|
|
|
|
- rightPanelWidthConstraint,
|
|
|
|
|
-
|
|
|
|
|
webToolbar.leadingAnchor.constraint(equalTo: rightWebPanel.leadingAnchor),
|
|
webToolbar.leadingAnchor.constraint(equalTo: rightWebPanel.leadingAnchor),
|
|
|
webToolbar.trailingAnchor.constraint(equalTo: rightWebPanel.trailingAnchor),
|
|
webToolbar.trailingAnchor.constraint(equalTo: rightWebPanel.trailingAnchor),
|
|
|
webToolbar.topAnchor.constraint(equalTo: rightWebPanel.topAnchor),
|
|
webToolbar.topAnchor.constraint(equalTo: rightWebPanel.topAnchor),
|
|
@@ -7385,14 +7395,13 @@ class ViewController: NSViewController {
|
|
|
searchRowCenterX,
|
|
searchRowCenterX,
|
|
|
searchRow.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
searchRow.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
searchRow.leadingAnchor.constraint(greaterThanOrEqualTo: topBar.leadingAnchor, constant: 40),
|
|
searchRow.leadingAnchor.constraint(greaterThanOrEqualTo: topBar.leadingAnchor, constant: 40),
|
|
|
- searchRow.leadingAnchor.constraint(greaterThanOrEqualTo: brandStack.trailingAnchor, constant: 16),
|
|
|
|
|
searchRow.trailingAnchor.constraint(lessThanOrEqualTo: rightTopBarCluster.leadingAnchor, constant: -12),
|
|
searchRow.trailingAnchor.constraint(lessThanOrEqualTo: rightTopBarCluster.leadingAnchor, constant: -12),
|
|
|
|
|
|
|
|
rightTopBarCluster.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -12),
|
|
rightTopBarCluster.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -12),
|
|
|
rightTopBarCluster.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
rightTopBarCluster.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
|
|
|
|
|
searchPill.heightAnchor.constraint(equalToConstant: 32),
|
|
searchPill.heightAnchor.constraint(equalToConstant: 32),
|
|
|
- searchPill.widthAnchor.constraint(equalToConstant: 320),
|
|
|
|
|
|
|
+ searchPillWidth,
|
|
|
searchIcon.leadingAnchor.constraint(equalTo: searchPill.leadingAnchor, constant: 12),
|
|
searchIcon.leadingAnchor.constraint(equalTo: searchPill.leadingAnchor, constant: 12),
|
|
|
searchIcon.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
|
|
searchIcon.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
|
|
|
searchIcon.widthAnchor.constraint(equalToConstant: 16),
|
|
searchIcon.widthAnchor.constraint(equalToConstant: 16),
|
|
@@ -7418,7 +7427,7 @@ class ViewController: NSViewController {
|
|
|
|
|
|
|
|
panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 18),
|
|
panel.topAnchor.constraint(equalTo: actions.bottomAnchor, constant: 18),
|
|
|
panel.centerXAnchor.constraint(equalTo: mainContentColumn.centerXAnchor),
|
|
panel.centerXAnchor.constraint(equalTo: mainContentColumn.centerXAnchor),
|
|
|
- panel.widthAnchor.constraint(equalToConstant: 610),
|
|
|
|
|
|
|
+ homeMeetingsPanelWidth,
|
|
|
panel.leadingAnchor.constraint(greaterThanOrEqualTo: mainContentColumn.leadingAnchor, constant: 6),
|
|
panel.leadingAnchor.constraint(greaterThanOrEqualTo: mainContentColumn.leadingAnchor, constant: 6),
|
|
|
panel.trailingAnchor.constraint(lessThanOrEqualTo: mainContentColumn.trailingAnchor, constant: -6),
|
|
panel.trailingAnchor.constraint(lessThanOrEqualTo: mainContentColumn.trailingAnchor, constant: -6),
|
|
|
panel.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
|
|
panel.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
|
|
@@ -7634,8 +7643,16 @@ class ViewController: NSViewController {
|
|
|
embeddedBrowserPanel = rightWebPanel
|
|
embeddedBrowserPanel = rightWebPanel
|
|
|
homeBodyRowView = bodyRow
|
|
homeBodyRowView = bodyRow
|
|
|
homeMainContentColumnView = mainContentColumn
|
|
homeMainContentColumnView = mainContentColumn
|
|
|
- embeddedBrowserPanelWidthConstraint = rightPanelWidthConstraint
|
|
|
|
|
- embeddedBrowserMainColumnCollapsedWidthConstraint = mainColumnCollapsedWidthConstraint
|
|
|
|
|
|
|
+ embeddedBrowserPanelWidthConstraint = nil
|
|
|
|
|
+ mainColumnVerticalFillConstraints = [
|
|
|
|
|
+ mainContentColumn.topAnchor.constraint(equalTo: bodyRow.topAnchor),
|
|
|
|
|
+ mainContentColumn.bottomAnchor.constraint(equalTo: bodyRow.bottomAnchor)
|
|
|
|
|
+ ]
|
|
|
|
|
+ webPanelVerticalFillConstraints = [
|
|
|
|
|
+ rightWebPanel.topAnchor.constraint(equalTo: bodyRow.topAnchor),
|
|
|
|
|
+ rightWebPanel.bottomAnchor.constraint(equalTo: bodyRow.bottomAnchor)
|
|
|
|
|
+ ]
|
|
|
|
|
+ NSLayoutConstraint.activate(mainColumnVerticalFillConstraints)
|
|
|
embeddedBrowserBackButton = browserBack
|
|
embeddedBrowserBackButton = browserBack
|
|
|
embeddedBrowserForwardButton = browserForward
|
|
embeddedBrowserForwardButton = browserForward
|
|
|
refreshEmbeddedBrowserNavButtons()
|
|
refreshEmbeddedBrowserNavButtons()
|
|
@@ -9214,36 +9231,35 @@ class ViewController: NSViewController {
|
|
|
private func setupWidgetBridge() {
|
|
private func setupWidgetBridge() {
|
|
|
let center = NotificationCenter.default
|
|
let center = NotificationCenter.default
|
|
|
center.addObserver(forName: .widgetOpenHomePage, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetOpenHomePage, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarShowSection(.home)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarShowSection(.home) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetOpenMeetingsPage, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetOpenMeetingsPage, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarShowSection(.meetings)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarShowSection(.meetings) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetOpenSchedulerPage, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetOpenSchedulerPage, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarShowSection(.scheduler)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarShowSection(.scheduler) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetOpenWidgetsPage, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetOpenWidgetsPage, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarShowSection(.widgets)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarShowSection(.widgets) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetOpenSettingsPage, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetOpenSettingsPage, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarShowSection(.settings)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarShowSection(.settings) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetStartNewMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetStartNewMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarTriggerNewMeeting()
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarTriggerNewMeeting() }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetJoinMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetJoinMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarTriggerJoinMeeting()
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarTriggerJoinMeeting() }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetScheduleMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetScheduleMeetingRequested, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.menuBarTriggerScheduleMeeting()
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.menuBarTriggerScheduleMeeting() }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetRefreshRequested, object: nil, queue: .main) { [weak self] _ in
|
|
center.addObserver(forName: .widgetRefreshRequested, object: nil, queue: .main) { [weak self] _ in
|
|
|
- self?.triggerMeetingsRefresh(force: true)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.triggerMeetingsRefresh(force: true) }
|
|
|
}
|
|
}
|
|
|
center.addObserver(forName: .widgetOpenMeetingLinkRequested, object: nil, queue: .main) { [weak self] notification in
|
|
center.addObserver(forName: .widgetOpenMeetingLinkRequested, object: nil, queue: .main) { [weak self] notification in
|
|
|
- guard let self else { return }
|
|
|
|
|
guard let link = notification.userInfo?["link"] as? String else { return }
|
|
guard let link = notification.userInfo?["link"] as? String else { return }
|
|
|
- self.joinZoomMeetingFromLink(link, displayName: nil)
|
|
|
|
|
|
|
+ MainActor.assumeIsolated { self?.joinZoomMeetingFromLink(link, displayName: nil) }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -10152,7 +10168,9 @@ class ViewController: NSViewController {
|
|
|
private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
|
|
private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
|
|
|
let root = HoverTileView()
|
|
let root = HoverTileView()
|
|
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- root.widthAnchor.constraint(equalToConstant: 88).isActive = true
|
|
|
|
|
|
|
+ let tileWidth = root.widthAnchor.constraint(equalToConstant: 88)
|
|
|
|
|
+ tileWidth.priority = .defaultHigh
|
|
|
|
|
+ tileWidth.isActive = true
|
|
|
// Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
|
|
// Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
|
|
|
root.heightAnchor.constraint(equalToConstant: 76).isActive = true
|
|
root.heightAnchor.constraint(equalToConstant: 76).isActive = true
|
|
|
|
|
|
|
@@ -10339,20 +10357,24 @@ extension ViewController: WKNavigationDelegate, WKUIDelegate {
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
|
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
|
|
|
guard webView === embeddedBrowserWebView else { return }
|
|
guard webView === embeddedBrowserWebView else { return }
|
|
|
- updateEmbeddedMeetingRecordingPresence(for: webView.url, webViewTitle: webView.title)
|
|
|
|
|
- applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
|
|
- refreshEmbeddedBrowserNavButtons()
|
|
|
|
|
|
|
+ Task { @MainActor in
|
|
|
|
|
+ self.updateEmbeddedMeetingRecordingPresence(for: webView.url, webViewTitle: webView.title)
|
|
|
|
|
+ self.applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
|
|
+ self.refreshEmbeddedBrowserNavButtons()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
|
|
guard webView === embeddedBrowserWebView else { return }
|
|
guard webView === embeddedBrowserWebView else { return }
|
|
|
- updateEmbeddedMeetingRecordingPresence(for: webView.url, webViewTitle: webView.title)
|
|
|
|
|
- applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
|
|
- if let pageURL = webView.url, isZoomEmbeddedWebClientInCallURL(pageURL) {
|
|
|
|
|
- embeddedZoomPendingWebAuth = nil
|
|
|
|
|
- embeddedZoomPendingJoinURL = nil
|
|
|
|
|
|
|
+ Task { @MainActor in
|
|
|
|
|
+ self.updateEmbeddedMeetingRecordingPresence(for: webView.url, webViewTitle: webView.title)
|
|
|
|
|
+ self.applyEmbeddedZoomWebAuthenticationIfNeeded(webView: webView)
|
|
|
|
|
+ if let pageURL = webView.url, self.isZoomEmbeddedWebClientInCallURL(pageURL) {
|
|
|
|
|
+ self.embeddedZoomPendingWebAuth = nil
|
|
|
|
|
+ self.embeddedZoomPendingJoinURL = nil
|
|
|
|
|
+ }
|
|
|
|
|
+ self.refreshEmbeddedBrowserNavButtons()
|
|
|
}
|
|
}
|
|
|
- refreshEmbeddedBrowserNavButtons()
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
|
|
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
|
|
@@ -10998,8 +11020,7 @@ final class ZoomOAuthService: NSObject {
|
|
|
if let existing = inAppOAuthWindowController { return existing }
|
|
if let existing = inAppOAuthWindowController { return existing }
|
|
|
let controller = InAppOAuthWindowController(
|
|
let controller = InAppOAuthWindowController(
|
|
|
title: "Zoom Sign-In",
|
|
title: "Zoom Sign-In",
|
|
|
- websiteDataStore: zoomEmbeddedWebsiteDataStore,
|
|
|
|
|
- processPool: zoomEmbeddedWebProcessPool
|
|
|
|
|
|
|
+ websiteDataStore: zoomEmbeddedWebsiteDataStore
|
|
|
)
|
|
)
|
|
|
inAppOAuthWindowController = controller
|
|
inAppOAuthWindowController = controller
|
|
|
return controller
|
|
return controller
|
|
@@ -11208,14 +11229,10 @@ private final class InAppOAuthWindowController: NSWindowController, WKNavigation
|
|
|
|
|
|
|
|
init(
|
|
init(
|
|
|
title: String,
|
|
title: String,
|
|
|
- websiteDataStore: WKWebsiteDataStore = .nonPersistent(),
|
|
|
|
|
- processPool: WKProcessPool? = nil
|
|
|
|
|
|
|
+ websiteDataStore: WKWebsiteDataStore
|
|
|
) {
|
|
) {
|
|
|
let configuration = WKWebViewConfiguration()
|
|
let configuration = WKWebViewConfiguration()
|
|
|
configuration.websiteDataStore = websiteDataStore
|
|
configuration.websiteDataStore = websiteDataStore
|
|
|
- if let processPool {
|
|
|
|
|
- configuration.processPool = processPool
|
|
|
|
|
- }
|
|
|
|
|
self.webView = WKWebView(frame: .zero, configuration: configuration)
|
|
self.webView = WKWebView(frame: .zero, configuration: configuration)
|
|
|
let container = OAuthWebViewContainerView(webView: webView)
|
|
let container = OAuthWebViewContainerView(webView: webView)
|
|
|
let contentSize = NSSize(width: AppWindowMetrics.defaultContentWidth, height: AppWindowMetrics.defaultContentHeight)
|
|
let contentSize = NSSize(width: AppWindowMetrics.defaultContentWidth, height: AppWindowMetrics.defaultContentHeight)
|