diff --git a/html/form.html b/html/form.html index 5d71c79..66d3c38 100644 --- a/html/form.html +++ b/html/form.html @@ -116,13 +116,13 @@ {{ if .telegramEnabled }} - {{ .strings.linkTelegram }} + {{ .strings.linkTelegram }} {{ if .telegramRequired }}({{ .strings.required }}){{ end }} {{ end }} {{ if .discordEnabled }} - {{ .strings.linkDiscord }} + {{ .strings.linkDiscord }} {{ if .discordRequired }}({{ .strings.required }}){{ end }} {{ end }} {{ if .matrixEnabled }} - {{ .strings.linkMatrix }} + {{ .strings.linkMatrix }} {{ if .matrixRequired }}({{ .strings.required }}){{ end }} {{ end }} {{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
diff --git a/lang/common/en-us.json b/lang/common/en-us.json index 322febc..761ff44 100644 --- a/lang/common/en-us.json +++ b/lang/common/en-us.json @@ -23,6 +23,7 @@ "linkMatrix": "Link Matrix", "contactDiscord": "Contact through Discord", "theme": "Theme", - "refresh": "Refresh" + "refresh": "Refresh", + "required": "Required" } } diff --git a/lang/form/en-us.json b/lang/form/en-us.json index e6bfa91..5724a44 100644 --- a/lang/form/en-us.json +++ b/lang/form/en-us.json @@ -31,6 +31,8 @@ "errorUnknown": "Unknown error.", "errorNoEmail": "Email required.", "errorCaptcha": "Captcha incorrect.", + "errorPassword": "Check password requirements.", + "errorNoMatch": "Passwords don't match.", "verified": "Account verified." }, "validationStrings": { diff --git a/ts/form.ts b/ts/form.ts index c0984ae..5693f40 100644 --- a/ts/form.ts +++ b/ts/form.ts @@ -72,6 +72,7 @@ if (window.telegramEnabled) { document.getElementById("contact-via").classList.remove("unfocused"); const radio = document.getElementById("contact-via-telegram") as HTMLInputElement; radio.checked = true; + validatorFunc(); } else if (!modalClosed) { setTimeout(checkVerified, 1500); } @@ -132,6 +133,7 @@ if (window.discordEnabled) { document.getElementById("contact-via").classList.remove("unfocused"); const radio = document.getElementById("contact-via-discord") as HTMLInputElement; radio.checked = true; + validatorFunc(); } else if (!modalClosed) { setTimeout(checkVerified, 1500); } @@ -190,6 +192,7 @@ if (window.matrixEnabled) { document.getElementById("contact-via").classList.remove("unfocused"); const radio = document.getElementById("contact-via-discord") as HTMLInputElement; radio.checked = true; + validatorFunc(); } else { window.notifications.customError("errorInvalidPIN", window.messages["errorInvalidPIN"]); submitButton.classList.add("~critical"); @@ -227,25 +230,85 @@ if (window.userExpiryEnabled) { const form = document.getElementById("form-create") as HTMLFormElement; const submitButton = form.querySelector("input[type=submit]") as HTMLInputElement; const submitSpan = form.querySelector("span.submit") as HTMLSpanElement; +const submitText = submitSpan.textContent; let usernameField = document.getElementById("create-username") as HTMLInputElement; const emailField = document.getElementById("create-email") as HTMLInputElement; if (!window.usernameEnabled) { usernameField.parentElement.remove(); usernameField = emailField; } const passwordField = document.getElementById("create-password") as HTMLInputElement; const rePasswordField = document.getElementById("create-reenter-password") as HTMLInputElement; -if (window.emailRequired) { - emailField.addEventListener("keyup", () => { - if (emailField.value.includes("@")) { - submitButton.disabled = false; - submitSpan.removeAttribute("disabled"); - } else { - submitButton.disabled = true; - submitSpan.setAttribute("disabled", ""); +let captchaVerified = false; +let captchaID = ""; +let captchaInput = document.getElementById("captcha-input") as HTMLInputElement; +const captchaCheckbox = document.getElementById("captcha-success") as HTMLSpanElement; +let prevCaptcha = ""; + +function baseValidator(oncomplete: (valid: boolean) => void): void { + let captchaChecked = false; + let captchaChange = false; + if (window.captcha) { + captchaChange = captchaInput.value != prevCaptcha; + if (captchaChange) { + prevCaptcha = captchaInput.value; + _post("/captcha/verify/" + window.code + "/" + captchaID + "/" + captchaInput.value, null, (req: XMLHttpRequest) => { + if (req.readyState == 4) { + if (req.status == 204) { + captchaCheckbox.innerHTML = ``; + captchaCheckbox.classList.add("~positive"); + captchaCheckbox.classList.remove("~critical"); + captchaVerified = true; + captchaChecked = true; + } else { + captchaCheckbox.innerHTML = ``; + captchaCheckbox.classList.add("~critical"); + captchaCheckbox.classList.remove("~positive"); + captchaVerified = false; + captchaChecked = true; + return; + } + } + }); } - }); + } + if (window.emailRequired) { + if (!emailField.value.includes("@")) { + oncomplete(false); + return; + } + } + if (window.discordEnabled && window.discordRequired && !discordVerified) { + oncomplete(false); + return; + } + if (window.telegramEnabled && window.telegramRequired && !telegramVerified) { + oncomplete(false); + return; + } + if (window.matrixEnabled && window.matrixRequired && !matrixVerified) { + oncomplete(false); + return; + } + if (window.captcha) { + if (!captchaChange) { + oncomplete(captchaVerified); + return; + } + while (!captchaChecked) { + continue; + } + oncomplete(captchaVerified); + } else { + oncomplete(true); + } } -var requirements = initValidator(passwordField, rePasswordField, submitButton, submitSpan) +let r = initValidator(passwordField, rePasswordField, submitButton, submitSpan, baseValidator); +var requirements = r[0]; +var validatorFunc = r[1] as () => void; + +if (window.emailRequired) { + emailField.addEventListener("keyup", validatorFunc) +} interface respDTO { response: boolean; @@ -267,10 +330,6 @@ interface sendDTO { captcha_text?: string; } -let captchaVerified = false; -let captchaID = ""; -let captchaInput = document.getElementById("captcha-input") as HTMLInputElement; - const genCaptcha = () => { _get("/captcha/gen/"+window.code, null, (req: XMLHttpRequest) => { if (req.readyState == 4) { @@ -288,27 +347,7 @@ const genCaptcha = () => { if (window.captcha) { genCaptcha(); (document.getElementById("captcha-regen") as HTMLSpanElement).onclick = genCaptcha; - const input = document.querySelector("input[type=submit]") as HTMLInputElement; - const checkbox = document.getElementById("captcha-success") as HTMLSpanElement; - captchaInput.onkeyup = () => _post("/captcha/verify/" + window.code + "/" + captchaID + "/" + captchaInput.value, null, (req: XMLHttpRequest) => { - if (req.readyState == 4) { - if (req.status == 204) { - input.disabled = false; - input.nextElementSibling.removeAttribute("disabled"); - checkbox.innerHTML = ``; - checkbox.classList.add("~positive"); - checkbox.classList.remove("~critical"); - } else { - input.disabled = true; - input.nextElementSibling.setAttribute("disabled", "true"); - checkbox.innerHTML = ``; - checkbox.classList.add("~critical"); - checkbox.classList.remove("~positive"); - } - } - }, ); - input.disabled = true; - input.nextElementSibling.setAttribute("disabled", "true"); + captchaInput.onkeyup = validatorFunc; } const create = (event: SubmitEvent) => { @@ -361,9 +400,15 @@ const create = (event: SubmitEvent) => { } else { submitSpan.classList.add("~critical"); submitSpan.classList.remove("~urge"); + if (req.response["error"] as string) { + submitSpan.textContent = window.messages[req.response["error"]]; + } else { + submitSpan.textContent = window.messages["errorPassword"]; + } setTimeout(() => { submitSpan.classList.add("~urge"); submitSpan.classList.remove("~critical"); + submitSpan.textContent = submitText; }, 1000); } } @@ -376,17 +421,18 @@ const create = (event: SubmitEvent) => { window.confirmationModal.show(); return; } - const old = submitSpan.textContent; if (req.response["error"] in window.messages) { submitSpan.textContent = window.messages[req.response["error"]]; } else { submitSpan.textContent = req.response["error"]; } - setTimeout(() => { submitSpan.textContent = old; }, 1000); + setTimeout(() => { submitSpan.textContent = submitText; }, 1000); } } } }); }; +validatorFunc(); + form.onsubmit = create; diff --git a/ts/modules/validator.ts b/ts/modules/validator.ts index 2aae859..728a800 100644 --- a/ts/modules/validator.ts +++ b/ts/modules/validator.ts @@ -1,6 +1,7 @@ interface valWindow extends Window { validationStrings: pwValStrings; invalidPassword: string; + messages: { [key: string]: string }; } interface pwValString { @@ -59,7 +60,7 @@ class Requirement { validate = (count: number) => { this.valid = (count >= this._minCount); } } -export function initValidator(passwordField: HTMLInputElement, rePasswordField: HTMLInputElement, submitButton: HTMLInputElement, submitSpan: HTMLSpanElement): { [category: string]: Requirement } { +export function initValidator(passwordField: HTMLInputElement, rePasswordField: HTMLInputElement, submitButton: HTMLInputElement, submitSpan: HTMLSpanElement, validatorFunc?: (oncomplete: (valid: boolean) => void) => void): ({ [category: string]: Requirement }|(() => void))[] { var defaultPwValStrings: pwValStrings = { length: { singular: "Must have at least {n} character", @@ -84,18 +85,30 @@ export function initValidator(passwordField: HTMLInputElement, rePasswordField: } const checkPasswords = () => { - if (passwordField.value != rePasswordField.value) { - rePasswordField.setCustomValidity(window.invalidPassword); - submitButton.disabled = true; - submitSpan.setAttribute("disabled", ""); - } else { - rePasswordField.setCustomValidity(""); - submitButton.disabled = false; - submitSpan.removeAttribute("disabled"); - } + return passwordField.value == rePasswordField.value; + } + + const checkValidity = () => { + const pw = checkPasswords(); + validatorFunc((valid: boolean) => { + if (pw && valid) { + rePasswordField.setCustomValidity(""); + submitButton.disabled = false; + submitSpan.removeAttribute("disabled"); + } else if (!pw) { + rePasswordField.setCustomValidity(window.invalidPassword); + submitButton.disabled = true; + submitSpan.setAttribute("disabled", ""); + } else { + rePasswordField.setCustomValidity(""); + submitButton.disabled = true; + submitSpan.setAttribute("disabled", ""); + } + }); }; - rePasswordField.addEventListener("keyup", checkPasswords); - passwordField.addEventListener("keyup", checkPasswords); + + rePasswordField.addEventListener("keyup", checkValidity); + passwordField.addEventListener("keyup", checkValidity); // Incredible code right here @@ -150,5 +163,5 @@ export function initValidator(passwordField: HTMLInputElement, rePasswordField: } } } - return requirements + return [requirements, checkValidity] } diff --git a/views.go b/views.go index 113f098..bbdd3b0 100644 --- a/views.go +++ b/views.go @@ -316,6 +316,7 @@ func (app *appContext) GenCaptcha(gc *gin.Context) { captchaID := genAuthToken() inv.Captchas[captchaID] = capt app.storage.invites[code] = inv + app.storage.storeInvites() gc.JSON(200, genCaptchaDTO{captchaID}) return }