Przeglądaj źródła

Improve Google Meet join link handling and browser launch reliability.

Made-with: Cursor
huzaifahayat12 3 miesięcy temu
rodzic
commit
b7cc5ff60f
1 zmienionych plików z 158 dodań i 5 usunięć
  1. 158 5
      meetings_app/ViewController.swift

+ 158 - 5
meetings_app/ViewController.swift

@@ -6,6 +6,7 @@
 //
 
 import Cocoa
+import WebKit
 
 private enum SidebarPage: Int {
     case joinMeetings = 0
@@ -57,6 +58,8 @@ final class ViewController: NSViewController {
     private var paywallPlanViews: [PremiumPlan: NSView] = [:]
     private var premiumPlanByView = [ObjectIdentifier: PremiumPlan]()
     private weak var paywallOfferLabel: NSTextField?
+    private weak var meetLinkField: NSTextField?
+    private var inAppBrowserWindowController: InAppBrowserWindowController?
 
     private let darkModeDefaultsKey = "settings.darkModeEnabled"
     private var darkModeEnabled: Bool {
@@ -179,6 +182,65 @@ private extension ViewController {
         showPaywall()
     }
 
+    @objc private func joinMeetClicked(_ sender: Any?) {
+        let rawInput = meetLinkField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+        guard rawInput.isEmpty == false else {
+            showSimpleAlert(title: "Join Meeting", message: "Please enter a Google Meet link.")
+            return
+        }
+
+        let normalized = normalizedURLString(from: rawInput)
+        guard let url = URL(string: normalized),
+              let host = url.host?.lowercased(),
+              host.contains("meet.google.com") else {
+            showSimpleAlert(title: "Invalid Link", message: "Please enter a valid Google Meet link.")
+            return
+        }
+
+        openInSafari(url: url)
+    }
+
+    @objc private func cancelMeetJoinClicked(_ sender: Any?) {
+        meetLinkField?.stringValue = ""
+    }
+
+    private func normalizedURLString(from value: String) -> String {
+        if value.lowercased().hasPrefix("http://") || value.lowercased().hasPrefix("https://") {
+            return value
+        }
+        return "https://\(value)"
+    }
+
+    private func openInAppBrowser(with url: URL) {
+        let browserController: InAppBrowserWindowController
+        if let existing = inAppBrowserWindowController {
+            browserController = existing
+        } else {
+            browserController = InAppBrowserWindowController()
+            inAppBrowserWindowController = browserController
+        }
+
+        browserController.load(url: url)
+        browserController.showWindow(nil)
+        browserController.window?.makeKeyAndOrderFront(nil)
+        browserController.window?.orderFrontRegardless()
+        NSApp.activate(ignoringOtherApps: true)
+    }
+
+    private func openInSafari(url: URL) {
+        guard let safariAppURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: "com.apple.Safari") else {
+            NSWorkspace.shared.open(url)
+            return
+        }
+
+        let configuration = NSWorkspace.OpenConfiguration()
+        NSWorkspace.shared.open([url], withApplicationAt: safariAppURL, configuration: configuration) { _, error in
+            if let error {
+                self.showSimpleAlert(title: "Unable to Open Safari", message: error.localizedDescription)
+            }
+        }
+    }
+
     private func showSidebarPage(_ page: SidebarPage) {
         selectedSidebarPage = page
         updateSidebarAppearance()
@@ -663,6 +725,7 @@ private extension ViewController {
         codeField.textColor = palette.textPrimary
         codeField.placeholderString = "Enter Link"
         codeInputShell.addSubview(codeField)
+        meetLinkField = codeField
         codeCard.addSubview(codeTitle)
         codeCard.addSubview(codeInputShell)
 
@@ -718,11 +781,40 @@ private extension ViewController {
         let spacer = NSView()
         spacer.translatesAutoresizingMaskIntoConstraints = false
         row.addArrangedSubview(spacer)
-        row.addArrangedSubview(actionButton(title: "Cancel", color: palette.cancelButton, textColor: palette.textSecondary, width: 110))
-        row.addArrangedSubview(actionButton(title: "Join", color: palette.primaryBlue, textColor: .white, width: 116))
+        row.addArrangedSubview(meetActionButton(
+            title: "Cancel",
+            color: palette.cancelButton,
+            textColor: palette.textSecondary,
+            width: 110,
+            action: #selector(cancelMeetJoinClicked(_:))
+        ))
+        row.addArrangedSubview(meetActionButton(
+            title: "Join",
+            color: palette.primaryBlue,
+            textColor: .white,
+            width: 116,
+            action: #selector(joinMeetClicked(_:))
+        ))
         return row
     }
 
+    func meetActionButton(title: String, color: NSColor, textColor: NSColor, width: CGFloat, action: Selector) -> NSButton {
+        let button = NSButton(title: title, target: self, action: action)
+        button.translatesAutoresizingMaskIntoConstraints = false
+        button.isBordered = false
+        button.bezelStyle = .regularSquare
+        button.wantsLayer = true
+        button.layer?.cornerRadius = 9
+        button.layer?.backgroundColor = color.cgColor
+        button.layer?.borderColor = (title == "Cancel" ? palette.inputBorder : palette.primaryBlueBorder).cgColor
+        button.layer?.borderWidth = 1
+        button.font = typography.buttonText
+        button.contentTintColor = textColor
+        button.widthAnchor.constraint(equalToConstant: width).isActive = true
+        button.heightAnchor.constraint(equalToConstant: 36).isActive = true
+        return button
+    }
+
     func makePaywallContent() -> NSView {
         paywallPlanViews.removeAll()
         premiumPlanByView.removeAll()
@@ -1451,9 +1543,7 @@ private extension ViewController {
 /// Ensures `NSClickGestureRecognizer` on the row receives clicks instead of child label/image views swallowing them.
 private class RowHitTestView: NSView {
     override func hitTest(_ point: NSPoint) -> NSView? {
-        guard let superview else { return nil }
-        let local = convert(point, from: superview)
-        return bounds.contains(local) ? self : nil
+        return bounds.contains(point) ? self : nil
     }
 }
 
@@ -2000,3 +2090,66 @@ private struct Typography {
     let cardTime = NSFont.systemFont(ofSize: 12, weight: .regular)
 }
 
+private final class InAppBrowserWindowController: NSWindowController {
+    private let browserViewController = InAppBrowserViewController()
+
+    init() {
+        let browserWindow = NSWindow(
+            contentRect: NSRect(x: 0, y: 0, width: 1100, height: 760),
+            styleMask: [.titled, .closable, .miniaturizable, .resizable],
+            backing: .buffered,
+            defer: false
+        )
+        browserWindow.title = "Google Meet"
+        browserWindow.center()
+        browserWindow.contentViewController = browserViewController
+        super.init(window: browserWindow)
+    }
+
+    @available(*, unavailable)
+    required init?(coder: NSCoder) {
+        nil
+    }
+
+    func load(url: URL) {
+        browserViewController.load(url: url)
+    }
+}
+
+private final class InAppBrowserViewController: NSViewController, WKNavigationDelegate {
+    private let webView = WKWebView(frame: .zero)
+    private var lastLoadedURL: URL?
+
+    override func loadView() {
+        webView.navigationDelegate = self
+        webView.translatesAutoresizingMaskIntoConstraints = false
+        view = webView
+    }
+
+    func load(url: URL) {
+        lastLoadedURL = url
+        webView.load(URLRequest(url: url))
+    }
+
+    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
+        let nsError = error as NSError
+        if nsError.code == NSURLErrorCancelled {
+            return
+        }
+        let alert = NSAlert()
+        alert.messageText = "Unable to Open Link"
+        alert.informativeText = "Could not load this page in the in-app browser.\n\n\(error.localizedDescription)\n\nOpen in default browser instead?"
+        alert.addButton(withTitle: "Open in Browser")
+        alert.addButton(withTitle: "Cancel")
+        let result = alert.runModal()
+        if result == .alertFirstButtonReturn, let url = lastLoadedURL {
+            NSWorkspace.shared.open(url)
+        }
+    }
+
+    func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
+        // WebKit process can be terminated by the OS; retry current page once.
+        webView.reload()
+    }
+}
+