浏览代码

Validate Google Meet links on Join before opening browser

Require standard xxx-yyyy-zzz meeting codes. Accept full https URL, meet.google.com/… without scheme, or bare code; normalize to https://meet.google.com/{code}. Reject empty or invalid input with a clear alert. Update join field placeholder.

Made-with: Cursor
huzaifahayat12 1 周之前
父节点
当前提交
2ed0ebcd55
共有 1 个文件被更改,包括 49 次插入12 次删除
  1. 49 12
      meetings_app/ViewController.swift

+ 49 - 12
meetings_app/ViewController.swift

@@ -190,17 +190,11 @@ private extension ViewController {
190 190
     @objc private func joinMeetClicked(_ sender: Any?) {
191 191
         let rawInput = meetLinkField?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
192 192
 
193
-        if rawInput.isEmpty {
194
-            guard let url = URL(string: "https://meet.google.com/") else { return }
195
-            openInAppBrowser(with: url, policy: inAppBrowserDefaultPolicy)
196
-            return
197
-        }
198
-
199
-        let normalized = normalizedURLString(from: rawInput)
200
-        guard let url = URL(string: normalized),
201
-              let host = url.host?.lowercased(),
202
-              host.contains("meet.google.com") else {
203
-            showSimpleAlert(title: "Invalid Link", message: "Please enter a valid Google Meet link.")
193
+        guard let url = normalizedMeetJoinURL(from: rawInput) else {
194
+            showSimpleAlert(
195
+                title: "Invalid Meet link",
196
+                message: "Enter a valid Google Meet link or meeting code (for example nkd-grps-duv, meet.google.com/nkd-grps-duv, or https://meet.google.com/nkd-grps-duv)."
197
+            )
204 198
             return
205 199
         }
206 200
 
@@ -247,6 +241,49 @@ private extension ViewController {
247 241
         return "https://\(value)"
248 242
     }
249 243
 
244
+    /// Typical Meet meeting code shape: three hyphen-separated groups (e.g. `nkd-grps-duv`).
245
+    private func isValidMeetMeetingCode(_ code: String) -> Bool {
246
+        let trimmed = code.trimmingCharacters(in: .whitespacesAndNewlines)
247
+        guard trimmed.isEmpty == false else { return false }
248
+        let pattern = "^[a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{3}$"
249
+        return trimmed.range(of: pattern, options: [.regularExpression, .caseInsensitive]) != nil
250
+    }
251
+
252
+    /// Accepts `https://meet.google.com/...`, `meet.google.com/...`, or a bare code; returns canonical Meet URL or `nil`.
253
+    private func normalizedMeetJoinURL(from rawInput: String) -> URL? {
254
+        let trimmed = rawInput.trimmingCharacters(in: .whitespacesAndNewlines)
255
+        guard trimmed.isEmpty == false else { return nil }
256
+
257
+        let lower = trimmed.lowercased()
258
+
259
+        if lower.hasPrefix("http://") || lower.hasPrefix("https://") {
260
+            guard let url = URL(string: trimmed),
261
+                  let host = url.host?.lowercased(),
262
+                  host == "meet.google.com" || host.hasSuffix(".meet.google.com") else {
263
+                return nil
264
+            }
265
+            let path = url.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
266
+            guard path.isEmpty == false else { return nil }
267
+            let firstSegment = path.split(separator: "/").first.map(String.init) ?? path
268
+            guard isValidMeetMeetingCode(firstSegment) else { return nil }
269
+            return URL(string: "https://meet.google.com/\(firstSegment.lowercased())")
270
+        }
271
+
272
+        if lower.hasPrefix("meet.google.com/") {
273
+            let afterHost = trimmed.dropFirst("meet.google.com/".count)
274
+            let beforeQuery = String(afterHost).split(separator: "?").first.map(String.init) ?? String(afterHost)
275
+            let firstSegment = beforeQuery.split(separator: "/").first.map(String.init) ?? beforeQuery
276
+            guard isValidMeetMeetingCode(firstSegment) else { return nil }
277
+            return URL(string: "https://meet.google.com/\(firstSegment.lowercased())")
278
+        }
279
+
280
+        if isValidMeetMeetingCode(trimmed) {
281
+            return URL(string: "https://meet.google.com/\(trimmed.lowercased())")
282
+        }
283
+
284
+        return nil
285
+    }
286
+
250 287
     private func openInAppBrowser(with url: URL, policy: InAppBrowserURLPolicy = .allowAll) {
251 288
         let browserController: InAppBrowserWindowController
252 289
         if let existing = inAppBrowserWindowController {
@@ -887,7 +924,7 @@ private extension ViewController {
887 924
         codeField.focusRingType = .none
888 925
         codeField.font = NSFont.systemFont(ofSize: 36 / 2, weight: .regular)
889 926
         codeField.textColor = palette.textPrimary
890
-        codeField.placeholderString = "Enter Link"
927
+        codeField.placeholderString = "Code or meet.google.com/…"
891 928
         codeInputShell.addSubview(codeField)
892 929
         meetLinkField = codeField
893 930
         codeCard.addSubview(codeTitle)