|
|
@@ -287,6 +287,12 @@ final class ViewController: NSViewController {
|
|
287
|
287
|
private weak var meetLinkField: NSTextField?
|
|
288
|
288
|
private weak var browseAddressField: NSTextField?
|
|
289
|
289
|
private var inAppBrowserWindowController: InAppBrowserWindowController?
|
|
|
290
|
+ private var embeddedBrowserViewController: InAppBrowserContainerViewController?
|
|
|
291
|
+ private var embeddedBrowserURL: URL?
|
|
|
292
|
+ private var embeddedBrowserPolicy: InAppBrowserURLPolicy = .allowAll
|
|
|
293
|
+ private weak var mainPanelAuthBar: NSView?
|
|
|
294
|
+ private var mainContentHostTopToAuthConstraint: NSLayoutConstraint?
|
|
|
295
|
+ private var mainContentHostTopToPanelConstraint: NSLayoutConstraint?
|
|
290
|
296
|
private let googleOAuth = GoogleOAuthService.shared
|
|
291
|
297
|
private let calendarClient = GoogleCalendarClient()
|
|
292
|
298
|
private let storeKitCoordinator = StoreKitCoordinator()
|
|
|
@@ -712,7 +718,7 @@ private extension ViewController {
|
|
712
|
718
|
showSimpleAlert(title: "Invalid address", message: "Enter a valid http or https URL.")
|
|
713
|
719
|
return
|
|
714
|
720
|
}
|
|
715
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
721
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
716
|
722
|
}
|
|
717
|
723
|
|
|
718
|
724
|
@objc private func browseQuickLinkMeetClicked(_ sender: Any?) {
|
|
|
@@ -722,12 +728,12 @@ private extension ViewController {
|
|
722
|
728
|
|
|
723
|
729
|
@objc private func browseQuickLinkMeetHelpClicked(_ sender: Any?) {
|
|
724
|
730
|
guard let url = URL(string: "https://support.google.com/meet") else { return }
|
|
725
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
731
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
726
|
732
|
}
|
|
727
|
733
|
|
|
728
|
734
|
@objc private func browseQuickLinkZoomHelpClicked(_ sender: Any?) {
|
|
729
|
735
|
guard let url = URL(string: "https://support.zoom.us") else { return }
|
|
730
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
736
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
731
|
737
|
}
|
|
732
|
738
|
|
|
733
|
739
|
@objc private func instantMeetClicked(_ sender: NSClickGestureRecognizer) {
|
|
|
@@ -812,6 +818,77 @@ private extension ViewController {
|
|
812
|
818
|
}
|
|
813
|
819
|
}
|
|
814
|
820
|
|
|
|
821
|
+ private func openURLWithRouting(_ url: URL, policy: InAppBrowserURLPolicy = .allowAll) {
|
|
|
822
|
+ let scheme = (url.scheme ?? "").lowercased()
|
|
|
823
|
+ if scheme == "http" || scheme == "https" {
|
|
|
824
|
+ showEmbeddedWebPage(url, policy: policy)
|
|
|
825
|
+ return
|
|
|
826
|
+ }
|
|
|
827
|
+ openInDefaultBrowser(url: url)
|
|
|
828
|
+ }
|
|
|
829
|
+
|
|
|
830
|
+ private func embeddedBrowserController() -> InAppBrowserContainerViewController {
|
|
|
831
|
+ if let existing = embeddedBrowserViewController {
|
|
|
832
|
+ return existing
|
|
|
833
|
+ }
|
|
|
834
|
+ let controller = InAppBrowserContainerViewController()
|
|
|
835
|
+ embeddedBrowserViewController = controller
|
|
|
836
|
+ return controller
|
|
|
837
|
+ }
|
|
|
838
|
+
|
|
|
839
|
+ private func detachEmbeddedBrowserIfNeeded() {
|
|
|
840
|
+ guard let controller = embeddedBrowserViewController, controller.parent === self else { return }
|
|
|
841
|
+ controller.view.removeFromSuperview()
|
|
|
842
|
+ controller.removeFromParent()
|
|
|
843
|
+ }
|
|
|
844
|
+
|
|
|
845
|
+ private func mountMainContentView(_ child: NSView) {
|
|
|
846
|
+ guard let host = mainContentHost else { return }
|
|
|
847
|
+ NSLayoutConstraint.deactivate(mainContentHostPinConstraints)
|
|
|
848
|
+ mainContentHostPinConstraints.removeAll()
|
|
|
849
|
+ host.subviews.forEach { $0.removeFromSuperview() }
|
|
|
850
|
+ child.translatesAutoresizingMaskIntoConstraints = false
|
|
|
851
|
+ host.addSubview(child)
|
|
|
852
|
+ mainContentHostPinConstraints = [
|
|
|
853
|
+ child.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
|
854
|
+ child.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
|
855
|
+ child.topAnchor.constraint(equalTo: host.topAnchor),
|
|
|
856
|
+ child.bottomAnchor.constraint(equalTo: host.bottomAnchor)
|
|
|
857
|
+ ]
|
|
|
858
|
+ NSLayoutConstraint.activate(mainContentHostPinConstraints)
|
|
|
859
|
+ }
|
|
|
860
|
+
|
|
|
861
|
+ private func showEmbeddedWebPage(_ url: URL, policy: InAppBrowserURLPolicy = .allowAll) {
|
|
|
862
|
+ embeddedBrowserURL = url
|
|
|
863
|
+ embeddedBrowserPolicy = policy
|
|
|
864
|
+
|
|
|
865
|
+ let controller = embeddedBrowserController()
|
|
|
866
|
+ _ = controller.view
|
|
|
867
|
+ if controller.parent !== self {
|
|
|
868
|
+ addChild(controller)
|
|
|
869
|
+ }
|
|
|
870
|
+ controller.setNavigationPolicy(policy)
|
|
|
871
|
+ controller.load(url: url)
|
|
|
872
|
+
|
|
|
873
|
+ setEmbeddedBrowserLayoutMode(isEmbedded: true)
|
|
|
874
|
+ applyEmbeddedBrowserWindowTitle(url: url)
|
|
|
875
|
+ mountMainContentView(controller.view)
|
|
|
876
|
+ }
|
|
|
877
|
+
|
|
|
878
|
+ private func setEmbeddedBrowserLayoutMode(isEmbedded: Bool) {
|
|
|
879
|
+ mainPanelAuthBar?.isHidden = isEmbedded
|
|
|
880
|
+ mainContentHostTopToAuthConstraint?.isActive = !isEmbedded
|
|
|
881
|
+ mainContentHostTopToPanelConstraint?.isActive = isEmbedded
|
|
|
882
|
+ }
|
|
|
883
|
+
|
|
|
884
|
+ private func applyEmbeddedBrowserWindowTitle(url: URL) {
|
|
|
885
|
+ if let host = url.host, host.isEmpty == false {
|
|
|
886
|
+ view.window?.title = host
|
|
|
887
|
+ } else {
|
|
|
888
|
+ view.window?.title = "Browser"
|
|
|
889
|
+ }
|
|
|
890
|
+ }
|
|
|
891
|
+
|
|
815
|
892
|
private func openRateUsDestination() {
|
|
816
|
893
|
let configured = (Bundle.main.object(forInfoDictionaryKey: "RateUsURL") as? String)?
|
|
817
|
894
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
@@ -938,21 +1015,10 @@ private extension ViewController {
|
|
938
|
1015
|
selectedSidebarPage = page
|
|
939
|
1016
|
updateSidebarAppearance()
|
|
940
|
1017
|
applyWindowTitle(for: page)
|
|
941
|
|
-
|
|
942
|
|
- guard let host = mainContentHost else { return }
|
|
943
|
|
- NSLayoutConstraint.deactivate(mainContentHostPinConstraints)
|
|
944
|
|
- mainContentHostPinConstraints.removeAll()
|
|
945
|
|
- host.subviews.forEach { $0.removeFromSuperview() }
|
|
|
1018
|
+ setEmbeddedBrowserLayoutMode(isEmbedded: false)
|
|
|
1019
|
+ detachEmbeddedBrowserIfNeeded()
|
|
946
|
1020
|
let child = viewForPage(page)
|
|
947
|
|
- child.translatesAutoresizingMaskIntoConstraints = false
|
|
948
|
|
- host.addSubview(child)
|
|
949
|
|
- mainContentHostPinConstraints = [
|
|
950
|
|
- child.leadingAnchor.constraint(equalTo: host.leadingAnchor),
|
|
951
|
|
- child.trailingAnchor.constraint(equalTo: host.trailingAnchor),
|
|
952
|
|
- child.topAnchor.constraint(equalTo: host.topAnchor),
|
|
953
|
|
- child.bottomAnchor.constraint(equalTo: host.bottomAnchor)
|
|
954
|
|
- ]
|
|
955
|
|
- NSLayoutConstraint.activate(mainContentHostPinConstraints)
|
|
|
1021
|
+ mountMainContentView(child)
|
|
956
|
1022
|
}
|
|
957
|
1023
|
|
|
958
|
1024
|
private func showSettingsPopover() {
|
|
|
@@ -999,6 +1065,14 @@ private extension ViewController {
|
|
999
|
1065
|
paywallContinueButton = nil
|
|
1000
|
1066
|
paywallContinueEnabled = true
|
|
1001
|
1067
|
|
|
|
1068
|
+ detachEmbeddedBrowserIfNeeded()
|
|
|
1069
|
+ embeddedBrowserViewController = nil
|
|
|
1070
|
+ embeddedBrowserURL = nil
|
|
|
1071
|
+ embeddedBrowserPolicy = .allowAll
|
|
|
1072
|
+ mainPanelAuthBar = nil
|
|
|
1073
|
+ mainContentHostTopToAuthConstraint = nil
|
|
|
1074
|
+ mainContentHostTopToPanelConstraint = nil
|
|
|
1075
|
+
|
|
1002
|
1076
|
googleAccountPopover?.performClose(nil)
|
|
1003
|
1077
|
googleAccountPopover = nil
|
|
1004
|
1078
|
|
|
|
@@ -1026,7 +1100,7 @@ private extension ViewController {
|
|
1026
|
1100
|
case .moreApps:
|
|
1027
|
1101
|
if let moreAppsURL = Bundle.main.object(forInfoDictionaryKey: "MoreAppsURL") as? String,
|
|
1028
|
1102
|
let url = URL(string: moreAppsURL) {
|
|
1029
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
1103
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
1030
|
1104
|
}
|
|
1031
|
1105
|
case .shareApp:
|
|
1032
|
1106
|
shareAppFromSettingsMenu(sourceView: sourceView, clickLocationInSourceView: clickLocationInSourceView)
|
|
|
@@ -1039,7 +1113,7 @@ private extension ViewController {
|
|
1039
|
1113
|
let defaultURL = (Bundle.main.object(forInfoDictionaryKey: "AppLaunchPlaceholderURL") as? String) ?? "https://example.com/app-link-coming-soon"
|
|
1040
|
1114
|
let urlString = (Bundle.main.object(forInfoDictionaryKey: infoKey) as? String) ?? defaultURL
|
|
1041
|
1115
|
guard let url = URL(string: urlString) else { return }
|
|
1042
|
|
- openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
|
|
|
1116
|
+ openURLWithRouting(url, policy: inAppBrowserDefaultPolicy)
|
|
1043
|
1117
|
}
|
|
1044
|
1118
|
|
|
1045
|
1119
|
private func showSimpleAlert(title: String, message: String) {
|
|
|
@@ -2427,17 +2501,23 @@ private extension ViewController {
|
|
2427
|
2501
|
let host = NSView()
|
|
2428
|
2502
|
host.translatesAutoresizingMaskIntoConstraints = false
|
|
2429
|
2503
|
panel.addSubview(host)
|
|
|
2504
|
+ let hostTopToAuth = host.topAnchor.constraint(equalTo: authBar.bottomAnchor, constant: 20)
|
|
|
2505
|
+ let hostTopToPanel = host.topAnchor.constraint(equalTo: panel.topAnchor)
|
|
|
2506
|
+ hostTopToPanel.isActive = false
|
|
2430
|
2507
|
NSLayoutConstraint.activate([
|
|
2431
|
2508
|
authBar.leadingAnchor.constraint(equalTo: panel.leadingAnchor, constant: 28),
|
|
2432
|
2509
|
authBar.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -28),
|
|
2433
|
|
- authBar.topAnchor.constraint(equalTo: panel.topAnchor, constant: 26),
|
|
|
2510
|
+ authBar.topAnchor.constraint(equalTo: panel.safeAreaLayoutGuide.topAnchor, constant: 26),
|
|
2434
|
2511
|
|
|
2435
|
2512
|
host.leadingAnchor.constraint(equalTo: panel.leadingAnchor),
|
|
2436
|
2513
|
host.trailingAnchor.constraint(equalTo: panel.trailingAnchor),
|
|
2437
|
|
- host.topAnchor.constraint(equalTo: authBar.bottomAnchor, constant: 20),
|
|
|
2514
|
+ hostTopToAuth,
|
|
2438
|
2515
|
host.bottomAnchor.constraint(equalTo: panel.bottomAnchor)
|
|
2439
|
2516
|
])
|
|
2440
|
2517
|
mainContentHost = host
|
|
|
2518
|
+ mainPanelAuthBar = authBar
|
|
|
2519
|
+ mainContentHostTopToAuthConstraint = hostTopToAuth
|
|
|
2520
|
+ mainContentHostTopToPanelConstraint = hostTopToPanel
|
|
2441
|
2521
|
|
|
2442
|
2522
|
if hasGoogleSessionAvailable(), let profile = scheduleCurrentProfile {
|
|
2443
|
2523
|
applyGoogleProfile(profile)
|