(null);
// Clear the form each time the dialog opens, during render (not an
@@ -79,22 +101,43 @@ export function SuggestPlaceDialog({ open, onOpenChange }: SuggestPlaceDialogPro
setAddress("");
setGmapsLink("");
setNote("");
+ setPopupBlocked(false);
}
}
- const isValid = name.trim() && city.trim() && gmapsLink.trim();
+ const trimmedName = name.trim();
+ const trimmedCity = city.trim();
+ const trimmedGmapsLink = gmapsLink.trim();
+ const isGmapsLinkValid = isGoogleMapsUrl(trimmedGmapsLink);
+ const showGmapsError = trimmedGmapsLink.length > 0 && !isGmapsLinkValid;
+ const isValid = Boolean(trimmedName && trimmedCity && isGmapsLinkValid);
+ const issueUrl = isValid
+ ? buildIssueUrl({
+ name: trimmedName,
+ type,
+ city: trimmedCity,
+ address: address.trim(),
+ gmapsLink: trimmedGmapsLink,
+ note,
+ })
+ : null;
function handleSubmit() {
- if (!isValid) return;
- const url = buildIssueUrl({
- name: name.trim(),
- type,
- city: city.trim(),
- address: address.trim(),
- gmapsLink: gmapsLink.trim(),
- note,
- });
- window.open(url, "_blank", "noopener,noreferrer");
+ if (!issueUrl) return;
+
+ // Open a blank tab first so a non-null handle genuinely means the browser
+ // allowed the popup. Passing "noopener" to window.open intentionally
+ // returns null even when the tab opens. Sever opener before navigating.
+ const opened = window.open("", "_blank");
+ if (!opened) {
+ setPopupBlocked(true);
+ toast.error("Pop-up blocked. Your suggestion is still here.");
+ return;
+ }
+
+ opened.opener = null;
+ opened.location.replace(issueUrl);
+ setPopupBlocked(false);
onOpenChange(false);
}
@@ -166,7 +209,14 @@ export function SuggestPlaceDialog({ open, onOpenChange }: SuggestPlaceDialogPro
value={gmapsLink}
onChange={(e) => setGmapsLink(e.target.value)}
placeholder="https://maps.app.goo.gl/..."
+ aria-invalid={showGmapsError}
+ aria-describedby={showGmapsError ? "suggest-place-gmaps-error" : undefined}
/>
+ {showGmapsError ? (
+
+ Enter a valid Google Maps link.
+
+ ) : null}
@@ -181,6 +231,21 @@ export function SuggestPlaceDialog({ open, onOpenChange }: SuggestPlaceDialogPro
placeholder="Optional - why it belongs on the map, rating, review count..."
/>
+
+ {popupBlocked && issueUrl ? (
+
+ Pop-up blocked. Your suggestion is still here.{" "}
+
+ Open GitHub here
+
+ .
+
+ ) : null}