|
@@ -1702,24 +1702,49 @@ class ViewController: NSViewController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Opens non-sign-in web content in the system default browser.
|
|
/// Opens non-sign-in web content in the system default browser.
|
|
|
|
|
+ /// For Zoom meeting join/start links, also begins local audio recording (when not already recording), same as the Join flow.
|
|
|
@MainActor
|
|
@MainActor
|
|
|
- private func openWebURLPreferringInApp(_ url: URL) {
|
|
|
|
|
|
|
+ private func openWebURLPreferringInApp(_ url: URL, zoomMeetingDisplayName: String? = nil) {
|
|
|
_ = openURLInDefaultBrowser(url)
|
|
_ = openURLInDefaultBrowser(url)
|
|
|
|
|
+ beginZoomMeetingRecordingIfAppropriate(sourceURL: url, meetingDisplayName: zoomMeetingDisplayName)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// True when `url` looks like joining or starting a Zoom call (not OAuth, API, or generic marketing pages).
|
|
|
|
|
+ private func isZoomMeetingJoinOrStartURL(_ url: URL) -> Bool {
|
|
|
|
|
+ guard let host = url.host?.lowercased(), host.contains("zoom.") else { return false }
|
|
|
|
|
+ if host.hasPrefix("api.") { return false }
|
|
|
|
|
+ let path = url.path.lowercased()
|
|
|
|
|
+ if path.hasPrefix("/oauth") || path.hasPrefix("/signin") { return false }
|
|
|
|
|
+ if path.contains("/j/") { return true }
|
|
|
|
|
+ if path.contains("/wc/join") { return true }
|
|
|
|
|
+ if path.contains("/join/") { return true }
|
|
|
|
|
+ if path.contains("/my/") { return true }
|
|
|
|
|
+ if path.contains("/w/") { return true }
|
|
|
|
|
+ if path.contains("videomeeting") { return true }
|
|
|
|
|
+ if path.contains("/start") && path.contains("meeting") { return true }
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @MainActor
|
|
|
|
|
+ private func beginZoomMeetingRecordingIfAppropriate(sourceURL: URL, meetingDisplayName: String?) {
|
|
|
|
|
+ guard isZoomMeetingJoinOrStartURL(sourceURL) else { return }
|
|
|
|
|
+ guard MeetingRecordingManager.shared.isRecording == false else { return }
|
|
|
|
|
+ Task { @MainActor in
|
|
|
|
|
+ do {
|
|
|
|
|
+ try await MeetingRecordingManager.shared.startRecording(sourceURL: sourceURL, meetingDisplayName: meetingDisplayName)
|
|
|
|
|
+ self.updateAiCompanionRecordingUI()
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ self.showSimpleAlert(title: "Recording could not start", message: error.localizedDescription)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @MainActor
|
|
|
private func openZoomMeetingInDefaultBrowser(_ url: URL, meetingDisplayName: String? = nil) {
|
|
private func openZoomMeetingInDefaultBrowser(_ url: URL, meetingDisplayName: String? = nil) {
|
|
|
let opened = openURLInDefaultBrowser(url)
|
|
let opened = openURLInDefaultBrowser(url)
|
|
|
if opened {
|
|
if opened {
|
|
|
joinMeetingWindow?.performClose(nil)
|
|
joinMeetingWindow?.performClose(nil)
|
|
|
- // AI Companion mode requirement: automatically record every meeting we start/join.
|
|
|
|
|
- Task { @MainActor in
|
|
|
|
|
- do {
|
|
|
|
|
- try await MeetingRecordingManager.shared.startRecording(sourceURL: url, meetingDisplayName: meetingDisplayName)
|
|
|
|
|
- self.updateAiCompanionRecordingUI()
|
|
|
|
|
- } catch {
|
|
|
|
|
- self.showSimpleAlert(title: "Recording could not start", message: error.localizedDescription)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ beginZoomMeetingRecordingIfAppropriate(sourceURL: url, meetingDisplayName: meetingDisplayName)
|
|
|
} else {
|
|
} else {
|
|
|
showSimpleAlert(title: "Unable to open", message: "Your default browser could not be opened.")
|
|
showSimpleAlert(title: "Unable to open", message: "Your default browser could not be opened.")
|
|
|
}
|
|
}
|
|
@@ -2289,7 +2314,7 @@ class ViewController: NSViewController {
|
|
|
@objc private func meetingSearchResultTapped(_ sender: NSButton) {
|
|
@objc private func meetingSearchResultTapped(_ sender: NSButton) {
|
|
|
guard let meeting = globalSearchResults[safe: sender.tag] else { return }
|
|
guard let meeting = globalSearchResults[safe: sender.tag] else { return }
|
|
|
if let url = meeting.webURL {
|
|
if let url = meeting.webURL {
|
|
|
- openWebURLPreferringInApp(url)
|
|
|
|
|
|
|
+ openWebURLPreferringInApp(url, zoomMeetingDisplayName: meeting.title)
|
|
|
} else {
|
|
} else {
|
|
|
let pasteboard = NSPasteboard.general
|
|
let pasteboard = NSPasteboard.general
|
|
|
pasteboard.clearContents()
|
|
pasteboard.clearContents()
|
|
@@ -7290,7 +7315,7 @@ class ViewController: NSViewController {
|
|
|
guard let self else { return }
|
|
guard let self else { return }
|
|
|
guard let link = notification.userInfo?["link"] as? String,
|
|
guard let link = notification.userInfo?["link"] as? String,
|
|
|
let url = URL(string: link.trimmingCharacters(in: .whitespacesAndNewlines)) else { return }
|
|
let url = URL(string: link.trimmingCharacters(in: .whitespacesAndNewlines)) else { return }
|
|
|
- self.openWebURLPreferringInApp(url)
|
|
|
|
|
|
|
+ self.openWebURLPreferringInApp(url, zoomMeetingDisplayName: nil)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -7759,15 +7784,7 @@ class ViewController: NSViewController {
|
|
|
showSimpleAlert(title: "Unable to open", message: "Could not start a new Zoom meeting right now.")
|
|
showSimpleAlert(title: "Unable to open", message: "Could not start a new Zoom meeting right now.")
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- openWebURLPreferringInApp(url)
|
|
|
|
|
- Task { @MainActor in
|
|
|
|
|
- do {
|
|
|
|
|
- try await MeetingRecordingManager.shared.startRecording(sourceURL: url, meetingDisplayName: "Instant meeting")
|
|
|
|
|
- self.updateAiCompanionRecordingUI()
|
|
|
|
|
- } catch {
|
|
|
|
|
- self.showSimpleAlert(title: "Recording could not start", message: error.localizedDescription)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ openWebURLPreferringInApp(url, zoomMeetingDisplayName: "Instant meeting")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func menuBarTriggerJoinMeeting() {
|
|
func menuBarTriggerJoinMeeting() {
|
|
@@ -8085,7 +8102,7 @@ class ViewController: NSViewController {
|
|
|
hoverBackground: hoverBackground,
|
|
hoverBackground: hoverBackground,
|
|
|
hoverBorder: hoverBorder,
|
|
hoverBorder: hoverBorder,
|
|
|
onTapURL: { [weak self] url in
|
|
onTapURL: { [weak self] url in
|
|
|
- self?.openWebURLPreferringInApp(url)
|
|
|
|
|
|
|
+ self?.openWebURLPreferringInApp(url, zoomMeetingDisplayName: meeting.title)
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
card.wantsLayer = true
|
|
card.wantsLayer = true
|