|
|
@@ -287,6 +287,12 @@ final class ViewController: NSViewController {
|
|
|
private weak var meetLinkField: NSTextField?
|
|
|
private weak var browseAddressField: NSTextField?
|
|
|
private var inAppBrowserWindowController: InAppBrowserWindowController?
|
|
|
+ private var embeddedBrowserViewController: InAppBrowserContainerViewController?
|
|
|
+ private var embeddedBrowserURL: URL?
|
|
|
+ private var embeddedBrowserPolicy: InAppBrowserURLPolicy = .allowAll
|
|
|
+ private weak var mainPanelAuthBar: NSView?
|
|
|
+ private var mainContentHostTopToAuthConstraint: NSLayoutConstraint?
|
|
|
+ private var mainContentHostTopToPanelConstraint: NSLayoutConstraint?
|
|
|
private let googleOAuth = GoogleOAuthService.shared
|
|
|
private let calendarClient = GoogleCalendarClient()
|
|
|
private let storeKitCoordinator = StoreKitCoordinator()
|
|
|
@@ -712,7 +718,7 @@ private extension ViewController {
|
|
|
showSimpleAlert(title: "Invalid address", message: "Enter a valid http or https URL.")
|
|
|
return
|
|
|
}
|
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
|
|
|
|
@objc private func browseQuickLinkMeetClicked(_ sender: Any?) {
|
|
|
@@ -722,12 +728,12 @@ private extension ViewController {
|
|
|
|
|
|
@objc private func browseQuickLinkMeetHelpClicked(_ sender: Any?) {
|
|
|
guard let url = URL(string: "https://support.google.com/meet") else { return }
|
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
|
|
|
|
@objc private func browseQuickLinkZoomHelpClicked(_ sender: Any?) {
|
|
|
guard let url = URL(string: "https://support.zoom.us") else { return }
|
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
|
|
|
|
@objc private func instantMeetClicked(_ sender: NSClickGestureRecognizer) {
|
|
|
@@ -812,6 +818,77 @@ private extension ViewController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private func openURLWithRouting(_ url: URL, policy: InAppBrowserURLPolicy = .allowAll) {
|
|
|
+ let scheme = (url.scheme ?? "").lowercased()
|
|
|
+ if scheme == "http" || scheme == "https" {
|
|
|
+ showEmbeddedWebPage(url, policy: policy)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ openInDefaultBrowser(url: url)
|
|
|
+ }
|
|
|
+
|
|
|
+ private func embeddedBrowserController() -> InAppBrowserContainerViewController {
|
|
|
+ if let existing = embeddedBrowserViewController {
|
|
|
+ return existing
|
|
|
+ }
|
|
|
+ let controller = InAppBrowserContainerViewController()
|
|
|
+ embeddedBrowserViewController = controller
|
|
|
+ return controller
|
|
|
+ }
|
|
|
+
|
|
|
+ private func detachEmbeddedBrowserIfNeeded() {
|
|
|
+ guard let controller = embeddedBrowserViewController, controller.parent === self else { return }
|
|
|
+ controller.view.removeFromSuperview()
|
|
|
+ controller.removeFromParent()
|
|
|
+ }
|
|
|
+
|
|
|
+ private func mountMainContentView(_ child: NSView) {
|
|
|
+ guard let host = mainContentHost else { return }
|
|
|
+ NSLayoutConstraint.deactivate(mainContentHostPinConstraints)
|
|
|
+ mainContentHostPinConstraints.removeAll()
|
|
|
+ host.subviews.forEach { $0.removeFromSuperview() }
|
|
|
+ child.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ host.addSubview(child)
|
|
|
+ mainContentHostPinConstraints = [
|
|
|
+ child.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
|
+ child.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
|
+ child.topAnchor.constraint(equalTo: host.topAnchor),
|
|
|
+ child.bottomAnchor.constraint(equalTo: host.bottomAnchor)
|
|
|
+ ]
|
|
|
+ NSLayoutConstraint.activate(mainContentHostPinConstraints)
|
|
|
+ }
|
|
|
+
|
|
|
+ private func showEmbeddedWebPage(_ url: URL, policy: InAppBrowserURLPolicy = .allowAll) {
|
|
|
+ embeddedBrowserURL = url
|
|
|
+ embeddedBrowserPolicy = policy
|
|
|
+
|
|
|
+ let controller = embeddedBrowserController()
|
|
|
+ _ = controller.view
|
|
|
+ if controller.parent !== self {
|
|
|
+ addChild(controller)
|
|
|
+ }
|
|
|
+ controller.setNavigationPolicy(policy)
|
|
|
+ controller.load(url: url)
|
|
|
+
|
|
|
+ setEmbeddedBrowserLayoutMode(isEmbedded: true)
|
|
|
+ applyEmbeddedBrowserWindowTitle(url: url)
|
|
|
+ mountMainContentView(controller.view)
|
|
|
+ }
|
|
|
+
|
|
|
+ private func setEmbeddedBrowserLayoutMode(isEmbedded: Bool) {
|
|
|
+ mainPanelAuthBar?.isHidden = isEmbedded
|
|
|
+ mainContentHostTopToAuthConstraint?.isActive = !isEmbedded
|
|
|
+ mainContentHostTopToPanelConstraint?.isActive = isEmbedded
|
|
|
+ }
|
|
|
+
|
|
|
+ private func applyEmbeddedBrowserWindowTitle(url: URL) {
|
|
|
+ if let host = url.host, host.isEmpty == false {
|
|
|
+ view.window?.title = host
|
|
|
+ } else {
|
|
|
+ view.window?.title = "Browser"
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private func openRateUsDestination() {
|
|
|
let configured = (Bundle.main.object(forInfoDictionaryKey: "RateUsURL") as? String)?
|
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
@@ -938,21 +1015,10 @@ private extension ViewController {
|
|
|
selectedSidebarPage = page
|
|
|
updateSidebarAppearance()
|
|
|
applyWindowTitle(for: page)
|
|
|
-
|
|
|
- guard let host = mainContentHost else { return }
|
|
|
- NSLayoutConstraint.deactivate(mainContentHostPinConstraints)
|
|
|
- mainContentHostPinConstraints.removeAll()
|
|
|
- host.subviews.forEach { $0.removeFromSuperview() }
|
|
|
+ setEmbeddedBrowserLayoutMode(isEmbedded: false)
|
|
|
+ detachEmbeddedBrowserIfNeeded()
|
|
|
let child = viewForPage(page)
|
|
|
- child.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- host.addSubview(child)
|
|
|
- mainContentHostPinConstraints = [
|
|
|
- child.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
|
- child.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
|
- child.topAnchor.constraint(equalTo: host.topAnchor),
|
|
|
- child.bottomAnchor.constraint(equalTo: host.bottomAnchor)
|
|
|
- ]
|
|
|
- NSLayoutConstraint.activate(mainContentHostPinConstraints)
|
|
|
+ mountMainContentView(child)
|
|
|
}
|
|
|
|
|
|
private func showSettingsPopover() {
|
|
|
@@ -999,6 +1065,14 @@ private extension ViewController {
|
|
|
paywallContinueButton = nil
|
|
|
paywallContinueEnabled = true
|
|
|
|
|
|
+ detachEmbeddedBrowserIfNeeded()
|
|
|
+ embeddedBrowserViewController = nil
|
|
|
+ embeddedBrowserURL = nil
|
|
|
+ embeddedBrowserPolicy = .allowAll
|
|
|
+ mainPanelAuthBar = nil
|
|
|
+ mainContentHostTopToAuthConstraint = nil
|
|
|
+ mainContentHostTopToPanelConstraint = nil
|
|
|
+
|
|
|
googleAccountPopover?.performClose(nil)
|
|
|
googleAccountPopover = nil
|
|
|
|
|
|
@@ -1026,7 +1100,7 @@ private extension ViewController {
|
|
|
case .moreApps:
|
|
|
if let moreAppsURL = Bundle.main.object(forInfoDictionaryKey: "MoreAppsURL") as? String,
|
|
|
let url = URL(string: moreAppsURL) {
|
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
|
case .shareApp:
|
|
|
shareAppFromSettingsMenu(sourceView: sourceView, clickLocationInSourceView: clickLocationInSourceView)
|
|
|
@@ -1039,7 +1113,7 @@ private extension ViewController {
|
|
|
let defaultURL = (Bundle.main.object(forInfoDictionaryKey: "AppLaunchPlaceholderURL") as? String) ?? "https://example.com/app-link-coming-soon"
|
|
|
let urlString = (Bundle.main.object(forInfoDictionaryKey: infoKey) as? String) ?? defaultURL
|
|
|
guard let url = URL(string: urlString) else { return }
|
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
|
}
|
|
|
|
|
|
private func showSimpleAlert(title: String, message: String) {
|
|
|
@@ -2427,17 +2501,23 @@ private extension ViewController {
|
|
|
let host = NSView()
|
|
|
host.translatesAutoresizingMaskIntoConstraints = false
|
|
|
panel.addSubview(host)
|
|
|
+ let hostTopToAuth = host.topAnchor.constraint(equalTo: authBar.bottomAnchor, constant: 20)
|
|
|
+ let hostTopToPanel = host.topAnchor.constraint(equalTo: panel.topAnchor)
|
|
|
+ hostTopToPanel.isActive = false
|
|
|
NSLayoutConstraint.activate([
|
|
|
authBar.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 28),
|
|
|
authBar.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -28),
|
|
|
- authBar.topAnchor.constraint(equalTo: panel.topAnchor, constant: 26),
|
|
|
+ authBar.topAnchor.constraint(equalTo: panel.safeAreaLayoutGuide.topAnchor, constant: 26),
|
|
|
|
|
|
host.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
|
|
|
host.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
|
|
|
- host.topAnchor.constraint(equalTo: authBar.bottomAnchor, constant: 20),
|
|
|
+ hostTopToAuth,
|
|
|
host.bottomAnchor.constraint(equalTo: panel.bottomAnchor)
|
|
|
])
|
|
|
mainContentHost = host
|
|
|
+ mainPanelAuthBar = authBar
|
|
|
+ mainContentHostTopToAuthConstraint = hostTopToAuth
|
|
|
+ mainContentHostTopToPanelConstraint = hostTopToPanel
|
|
|
|
|
|
if hasGoogleSessionAvailable(), let profile = scheduleCurrentProfile {
|
|
|
applyGoogleProfile(profile)
|