Validate screenshot URLs with inline hints

This commit is contained in:
2026-02-06 22:52:24 +01:00
parent c64ac7480d
commit ae7cf1ff64
2 changed files with 33 additions and 1 deletions

View File

@@ -520,6 +520,8 @@ function buildSuggestionForm(initial = {}, lockTitle = false) {
<span class="char-counter" data-for="screenshotUrl"></span>
</span>
<input name="screenshotUrl" maxlength="2048" />
<p class="hint" data-i18n="form.screenshotHint">${t("form.screenshotHint")}</p>
<div class="form-error hidden" data-error="screenshot"></div>
</label>
<label class="stack">
<span class="label-row">
@@ -618,6 +620,8 @@ function openSuggestionModal({ title, submitLabel, initial = {}, onSubmit, lockT
const errorBox = form.querySelector('[data-error="players"]');
const minInput = form.querySelector('input[name="minPlayers"]');
const maxInput = form.querySelector('input[name="maxPlayers"]');
const screenshotError = form.querySelector('[data-error="screenshot"]');
const screenshotInput = form.querySelector('input[name="screenshotUrl"]');
const markError = (msg) => {
if (errorBox) {
errorBox.textContent = msg;
@@ -626,6 +630,7 @@ function openSuggestionModal({ title, submitLabel, initial = {}, onSubmit, lockT
};
const clearError = () => {
if (errorBox) errorBox.classList.add("hidden");
if (screenshotError) screenshotError.classList.add("hidden");
};
clearError();
const min = data.minPlayers;
@@ -643,8 +648,14 @@ function openSuggestionModal({ title, submitLabel, initial = {}, onSubmit, lockT
return;
}
if (data.screenshotUrl && !isValidImageUrl(data.screenshotUrl)) {
return toast(t("toast.invalidImageUrl"), true);
if (screenshotError) {
screenshotError.textContent = t("form.screenshotInvalid");
screenshotError.classList.remove("hidden");
}
screenshotInput?.classList.add("input-error");
return;
}
screenshotInput?.classList.remove("input-error");
if (!data.name?.trim()) return toast(t("toast.nameRequired"), true);
try {
await onSubmit(data, close, submitBtn);
@@ -976,6 +987,23 @@ function isValidImageUrl(url) {
const u = new URL(url);
const allowed = ["http:", "https:"];
if (!allowed.includes(u.protocol)) return false;
const host = u.hostname.toLowerCase();
const bannedHosts = [
"bit.ly",
"tinyurl.com",
"t.co",
"goo.gl",
"ow.ly",
"is.gd",
"buff.ly",
"rebrand.ly",
"steamcommunity.com",
"store.steampowered.com",
"imgur.com",
];
if (bannedHosts.some((h) => host === h)) return false;
if (host === "imgur.com" && !u.pathname.startsWith("/a/") && !u.pathname.startsWith("/gallery/")) return false;
if (host === "steamcommunity.com") return false;
const path = u.pathname.toLowerCase();
return [".png", ".jpg", ".jpeg", ".gif", ".webp", ".avif"].some((ext) =>
path.endsWith(ext),