From d7812d1b1b4595639adb8a942f6a3c86ad8dd190 Mon Sep 17 00:00:00 2001 From: Rizwan Mustafa Date: Tue, 24 May 2022 18:29:22 +0500 Subject: [PATCH 1/8] Disable signup button by default and enable only on all correct checks (#3020) rizwanmustafa * Disable signup button by default and enable only on all correct checks * Refactor code * remove debug log * null when hidden Co-authored-by: Miodec --- frontend/src/ts/elements/input-indicator.ts | 9 ++ frontend/src/ts/pages/login.ts | 107 ++++++++++++-------- frontend/static/html/pages/login.html | 2 +- 3 files changed, 77 insertions(+), 41 deletions(-) diff --git a/frontend/src/ts/elements/input-indicator.ts b/frontend/src/ts/elements/input-indicator.ts index 756225d03..4ed716e5c 100644 --- a/frontend/src/ts/elements/input-indicator.ts +++ b/frontend/src/ts/elements/input-indicator.ts @@ -8,6 +8,7 @@ interface InputIndicatorOption { export class InputIndicator { private parentElement: JQuery; private options: Record; + private currentStatus: keyof typeof this.options | null; constructor( parentElement: JQuery, @@ -18,6 +19,7 @@ export class InputIndicator { } this.parentElement = parentElement; this.options = options; + this.currentStatus = null; let indicator = `
`; @@ -50,11 +52,14 @@ export class InputIndicator { hide(): void { this.parentElement.find(".statusIndicator div").addClass("hidden"); + this.currentStatus = null; } show(optionId: keyof typeof this.options, messageOverride?: string): void { this.hide(); + this.currentStatus = optionId; + const indicator = this.parentElement.find(`[data-option-id="${optionId}"]`); indicator.removeClass("hidden"); @@ -68,4 +73,8 @@ export class InputIndicator { indicator.attr("aria-label", messageOverride); } } + + get(): keyof typeof this.options | null { + return this.currentStatus; + } } diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 6a73c6896..2e302aed8 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -22,56 +22,79 @@ export function hidePreloader(): void { $(".pageLogin .preloader").addClass("hidden"); } +const updateSignupButton = (): void => { + const $button = $(".page.pageLogin .register.side .button"); + + if ( + nameIndicator.get() !== "available" || + emailIndicator.get() !== "valid" || + verifyEmailIndicator.get() !== "match" || + passwordIndicator.get() !== "good" || + verifyPasswordIndicator.get() !== "match" + ) { + $button.addClass("disabled"); + } else { + $button.removeClass("disabled"); + } +}; + const checkNameDebounced = debounce(1000, async () => { const val = $( ".page.pageLogin .register.side .usernameInput" ).val() as string; - if (!val) return; + + if (!val) { + updateSignupButton(); + return; + } const response = await Ape.users.getNameAvailability(val); if (response.status === 200) { nameIndicator.show("available", response.message); - return; - } - - if (response.status == 422) { + } else if (response.status === 422) { nameIndicator.show("unavailable", response.message); - return; - } - - if (response.status == 409) { + } else if (response.status === 409) { nameIndicator.show("taken", response.message); - return; - } - - if (response.status !== 200) { + } else { nameIndicator.show("unavailable", response.message); - return Notifications.add( + Notifications.add( "Failed to check name availability: " + response.message, -1 ); } -}); -const checkEmailsMatch = (): void => { - const email = $(".page.pageLogin .register.side .emailInput").val(); - const verifyEmail = $( - ".page.pageLogin .register.side .verifyEmailInput" - ).val(); - verifyEmailIndicator.show(email === verifyEmail ? "match" : "mismatch"); -}; + updateSignupButton(); +}); const checkEmail = (): void => { const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; const email = $(".page.pageLogin .register.side .emailInput").val() as string; + if (emailRegex.test(email)) { + emailIndicator.show("valid"); + } else { + emailIndicator.show("invalid"); + } - emailIndicator.show(emailRegex.test(email) ? "valid" : "invalid"); + updateSignupButton(); +}; + +const checkEmailsMatch = (): void => { + const email = $(".page.pageLogin .register.side .emailInput").val(); + const verifyEmail = $( + ".page.pageLogin .register.side .verifyEmailInput" + ).val(); + if (email === verifyEmail) { + verifyEmailIndicator.show("match"); + } else { + verifyEmailIndicator.show("mismatch"); + } + + updateSignupButton(); }; const checkPassword = (): void => { - passwordIndicator.show("good"); const password = $( ".page.pageLogin .register.side .passwordInput" ).val() as string; @@ -80,20 +103,20 @@ const checkPassword = (): void => { if (password.length < 8) { passwordIndicator.show("short", "Password must be at least 8 characters"); return; + } else { + const hasCapital = password.match(/[A-Z]/); + const hasNumber = password.match(/[\d]/); + const hasSpecial = password.match(/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/); + if (!hasCapital || !hasNumber || !hasSpecial) { + passwordIndicator.show( + "weak", + "Password must contain at least one capital letter, number, and special character" + ); + } else { + passwordIndicator.show("good", "Password is good"); + } } - - const hasCapital = password.match(/[A-Z]/); - const hasNumber = password.match(/[\d]/); - const hasSpecial = password.match(/[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/); - if (!hasCapital || !hasNumber || !hasSpecial) { - passwordIndicator.show( - "weak", - "Password must contain at least one capital letter, number, and special character" - ); - return; - } - - passwordIndicator.show("good", "Password is good"); + updateSignupButton(); }; const checkPasswordsMatch = (): void => { @@ -101,9 +124,13 @@ const checkPasswordsMatch = (): void => { const verifyPassword = $( ".page.pageLogin .register.side .verifyPasswordInput" ).val(); - verifyPasswordIndicator.show( - password === verifyPassword ? "match" : "mismatch" - ); + if (password === verifyPassword) { + verifyPasswordIndicator.show("match"); + } else { + verifyPasswordIndicator.show("mismatch"); + } + + updateSignupButton(); }; const nameIndicator = new InputIndicator( diff --git a/frontend/static/html/pages/login.html b/frontend/static/html/pages/login.html index cd06dc16d..38d67b766 100644 --- a/frontend/static/html/pages/login.html +++ b/frontend/static/html/pages/login.html @@ -47,7 +47,7 @@ name="verify-password" />
-
+
Sign Up
From 39905f5c31bb64dd7fdb2ef9076ae0701247e9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=9A=B7=E1=9B=81=E1=9B=9F=E1=9A=B1=E1=9A=B7=E1=9B=81=20?= =?UTF-8?q?=E1=9B=92=E1=9A=A8=E1=9B=9A=E1=9A=A8=E1=9A=B2=E1=9A=BB=E1=9A=A8?= =?UTF-8?q?=E1=9B=9E=E1=9B=89=E1=9B=96?= Date: Tue, 24 May 2022 17:29:54 +0400 Subject: [PATCH 2/8] fix word typo georgian (#3019) Vitruvius21 --- frontend/static/languages/georgian.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/static/languages/georgian.json b/frontend/static/languages/georgian.json index 81c573a93..82c5fcbe6 100644 --- a/frontend/static/languages/georgian.json +++ b/frontend/static/languages/georgian.json @@ -42,7 +42,7 @@ "ამბებისა", "ჰიმნი", "ხაზს", - "ყველანარი", + "ყველანაირი", "რაიონი", "ძეგლი", "აღნიშნავდეს", From 80d05a186ffae53659a77a836e15d888079f7b60 Mon Sep 17 00:00:00 2001 From: Outdated <38858226+outdatedx@users.noreply.github.com> Date: Tue, 24 May 2022 19:00:22 +0530 Subject: [PATCH 3/8] Language Added, VIm: U (#3018) outdatedx Co-authored-by: // Outdated Designs --- frontend/static/languages/_groups.json | 3 +- frontend/static/languages/_list.json | 1 + frontend/static/languages/code_vim.json | 49 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 frontend/static/languages/code_vim.json diff --git a/frontend/static/languages/_groups.json b/frontend/static/languages/_groups.json index bb6589f89..85c8e54e3 100644 --- a/frontend/static/languages/_groups.json +++ b/frontend/static/languages/_groups.json @@ -331,7 +331,8 @@ "code_matlab", "code_sql", "code_perl", - "code_php" + "code_php", + "code_vim" ] }, { diff --git a/frontend/static/languages/_list.json b/frontend/static/languages/_list.json index 88f92bbe8..c14bd1596 100644 --- a/frontend/static/languages/_list.json +++ b/frontend/static/languages/_list.json @@ -186,6 +186,7 @@ ,"code_sql" ,"code_perl" ,"code_php" + ,"code_vim" ,"hindi" ,"hindi_1k" ,"macedonian" diff --git a/frontend/static/languages/code_vim.json b/frontend/static/languages/code_vim.json new file mode 100644 index 000000000..854804a5b --- /dev/null +++ b/frontend/static/languages/code_vim.json @@ -0,0 +1,49 @@ +{ + "name": "code_vim", + "leftToRight": true, + "noLazyMode": true, + "words": [ + ":e", + ":w", + ":q", + ":q!", + ":wq", + ":wq!", + ":x", + ":x!", + ":sav", + "u", + "k", + "j", + "h", + "l", + "e", + "b", + "0", + "G", + "gg", + "L", + "[[", + "[{", + "y", + "p", + "dd", + "yy", + "y$", + "D", + ":e.", + ":Sex", + ":Sex!", + ":browse e", + ":ls", + ":cd..", + ":args", + "gf", + ":tabnew", + "gt", + ":tabfirst", + ":tablast", + ":new", + ":split" + ] +} \ No newline at end of file From 2fcea65d687212ab471c8d4e42a9c1ad1c82067e Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 24 May 2022 16:03:54 +0200 Subject: [PATCH 4/8] added functions to disable certain components instead of all at once --- .../src/ts/controllers/account-controller.ts | 34 +++++++++++------- frontend/src/ts/pages/login.ts | 36 ++++++++++++------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/frontend/src/ts/controllers/account-controller.ts b/frontend/src/ts/controllers/account-controller.ts index 456391305..70c02a120 100644 --- a/frontend/src/ts/controllers/account-controller.ts +++ b/frontend/src/ts/controllers/account-controller.ts @@ -301,6 +301,8 @@ export function signIn(): void { authListener(); LoginPage.showPreloader(); LoginPage.disableInputs(); + LoginPage.disableSignUpButton(); + LoginPage.disableSignInButton(); const email = ($(".pageLogin .login input")[0] as HTMLInputElement).value; const password = ($(".pageLogin .login input")[1] as HTMLInputElement).value; @@ -343,6 +345,8 @@ export function signIn(): void { Notifications.add(message, -1); LoginPage.hidePreloader(); LoginPage.enableInputs(); + LoginPage.enableSignInButton(); + LoginPage.updateSignupButton(); }); }); } @@ -351,6 +355,8 @@ export async function signInWithGoogle(): Promise { UpdateConfig.setChangedBeforeDb(false); LoginPage.showPreloader(); LoginPage.disableInputs(); + LoginPage.disableSignUpButton(); + LoginPage.disableSignInButton(); authListener(); const persistence = $(".pageLogin .login #rememberMe input").prop("checked") ? browserLocalPersistence @@ -380,6 +386,8 @@ export async function signInWithGoogle(): Promise { Notifications.add(message, -1); LoginPage.hidePreloader(); LoginPage.enableInputs(); + LoginPage.enableSignInButton(); + LoginPage.updateSignupButton(); }); } @@ -509,6 +517,7 @@ export function signOut(): void { async function signUp(): Promise { LoginPage.disableInputs(); + LoginPage.disableSignUpButton(); LoginPage.showPreloader(); const nname = ($(".pageLogin .register input")[0] as HTMLInputElement).value; const email = ($(".pageLogin .register input")[1] as HTMLInputElement).value; @@ -523,6 +532,7 @@ async function signUp(): Promise { if (nname === "" || email === "" || emailVerify === "" || password === "") { LoginPage.hidePreloader(); LoginPage.enableInputs(); + LoginPage.updateSignupButton(); Notifications.add("Please fill in all fields", 0); return; } @@ -534,16 +544,16 @@ async function signUp(): Promise { ) { Notifications.add("Invalid email", 0, 3); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); return; } if (email !== emailVerify) { Notifications.add("Emails do not match", 0, 3); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); return; } @@ -551,8 +561,8 @@ async function signUp(): Promise { if (password.length < 8) { Notifications.add("Password must be at least 8 characters", 0, 3); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); return; } @@ -566,16 +576,16 @@ async function signUp(): Promise { 3 ); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); return; } if (password !== passwordVerify) { Notifications.add("Passwords do not match", 0, 3); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); return; } @@ -634,8 +644,8 @@ async function signUp(): Promise { const message = Misc.createErrorMessage(e, "Failed to create account"); Notifications.add(message, -1); LoginPage.hidePreloader(); - $(".pageLogin .button").removeClass("disabled"); - $(".pageLogin input").prop("disabled", false); + LoginPage.enableInputs(); + LoginPage.updateSignupButton(); signOut(); return; } diff --git a/frontend/src/ts/pages/login.ts b/frontend/src/ts/pages/login.ts index 2e302aed8..8609b47a7 100644 --- a/frontend/src/ts/pages/login.ts +++ b/frontend/src/ts/pages/login.ts @@ -4,13 +4,27 @@ import Page from "./page"; import * as Notifications from "../elements/notifications"; import { InputIndicator } from "../elements/input-indicator"; +export function enableSignUpButton(): void { + $(".page.pageLogin .register.side .button").removeClass("disabled"); +} + +export function disableSignUpButton(): void { + $(".page.pageLogin .register.side .button").addClass("disabled"); +} + +export function enableSignInButton(): void { + $(".page.pageLogin .login.side .button").removeClass("disabled"); +} + +export function disableSignInButton(): void { + $(".page.pageLogin .login.side .button").addClass("disabled"); +} + export function enableInputs(): void { - $(".pageLogin .button").removeClass("disabled"); $(".pageLogin input").prop("disabled", false); } export function disableInputs(): void { - $(".pageLogin .button").addClass("disabled"); $(".pageLogin input").prop("disabled", true); } @@ -22,9 +36,7 @@ export function hidePreloader(): void { $(".pageLogin .preloader").addClass("hidden"); } -const updateSignupButton = (): void => { - const $button = $(".page.pageLogin .register.side .button"); - +export const updateSignupButton = (): void => { if ( nameIndicator.get() !== "available" || emailIndicator.get() !== "valid" || @@ -32,9 +44,9 @@ const updateSignupButton = (): void => { passwordIndicator.get() !== "good" || verifyPasswordIndicator.get() !== "match" ) { - $button.addClass("disabled"); + disableSignUpButton(); } else { - $button.removeClass("disabled"); + enableSignUpButton(); } }; @@ -134,7 +146,7 @@ const checkPasswordsMatch = (): void => { }; const nameIndicator = new InputIndicator( - $(".page.pageLogin .register.side .username.inputAndIndicator"), + $(".page.pageLogin .register.side input.usernameInput"), { available: { icon: "fa-check", @@ -157,7 +169,7 @@ const nameIndicator = new InputIndicator( ); const emailIndicator = new InputIndicator( - $(".page.pageLogin .register.side .email.inputAndIndicator"), + $(".page.pageLogin .register.side input.emailInput"), { valid: { icon: "fa-check", @@ -171,7 +183,7 @@ const emailIndicator = new InputIndicator( ); const verifyEmailIndicator = new InputIndicator( - $(".page.pageLogin .register.side .verifyEmail.inputAndIndicator"), + $(".page.pageLogin .register.side input.verifyEmailInput"), { match: { icon: "fa-check", @@ -185,7 +197,7 @@ const verifyEmailIndicator = new InputIndicator( ); const passwordIndicator = new InputIndicator( - $(".page.pageLogin .register.side .password.inputAndIndicator"), + $(".page.pageLogin .register.side input.passwordInput"), { good: { icon: "fa-check", @@ -203,7 +215,7 @@ const passwordIndicator = new InputIndicator( ); const verifyPasswordIndicator = new InputIndicator( - $(".page.pageLogin .register.side .verifyPassword.inputAndIndicator"), + $(".page.pageLogin .register.side input.verifyPasswordInput"), { match: { icon: "fa-check", From 784e49bfa85148f9db18634a4d84f6172bd4647a Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 24 May 2022 16:05:32 +0200 Subject: [PATCH 5/8] automatically wrapping input in wrapper changing padding when indicator is visible --- frontend/src/ts/elements/input-indicator.ts | 15 +++-- frontend/static/html/pages/login.html | 74 +++++++++------------ 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/frontend/src/ts/elements/input-indicator.ts b/frontend/src/ts/elements/input-indicator.ts index 4ed716e5c..89c9fcceb 100644 --- a/frontend/src/ts/elements/input-indicator.ts +++ b/frontend/src/ts/elements/input-indicator.ts @@ -6,18 +6,18 @@ interface InputIndicatorOption { } export class InputIndicator { + private inputElement: JQuery; private parentElement: JQuery; private options: Record; private currentStatus: keyof typeof this.options | null; constructor( - parentElement: JQuery, + inputElement: JQuery, options: Record ) { - if (!parentElement.hasClass("inputAndIndicator")) { - throw new Error("Parent element must have class 'inputAndIndicator'"); - } - this.parentElement = parentElement; + this.inputElement = inputElement; + $(this.inputElement).wrap(`
`); + this.parentElement = $(this.inputElement).parent(".inputAndIndicator"); this.options = options; this.currentStatus = null; @@ -47,12 +47,13 @@ export class InputIndicator { indicator += `
`; - parentElement.append(indicator); + this.parentElement.append(indicator); } hide(): void { this.parentElement.find(".statusIndicator div").addClass("hidden"); this.currentStatus = null; + $(this.inputElement).css("padding-right", "0.5rem"); } show(optionId: keyof typeof this.options, messageOverride?: string): void { @@ -72,6 +73,8 @@ export class InputIndicator { } indicator.attr("aria-label", messageOverride); } + + $(this.inputElement).css("padding-right", "2.1rem"); } get(): keyof typeof this.options | null { diff --git a/frontend/static/html/pages/login.html b/frontend/static/html/pages/login.html index 38d67b766..4299b0534 100644 --- a/frontend/static/html/pages/login.html +++ b/frontend/static/html/pages/login.html @@ -5,48 +5,38 @@
register
-
- -
- -
- -
-
- -
-
- -
+ + + + +
Sign Up From c12579eb4a8cdfbe31d129bb73df76e8190c38a8 Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 24 May 2022 16:06:16 +0200 Subject: [PATCH 6/8] removed wrapper --- .../src/ts/popups/google-sign-up-popup.ts | 41 +++++++++---------- frontend/static/html/popups.html | 4 +- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/frontend/src/ts/popups/google-sign-up-popup.ts b/frontend/src/ts/popups/google-sign-up-popup.ts index 71db3ce72..3f84c2fd0 100644 --- a/frontend/src/ts/popups/google-sign-up-popup.ts +++ b/frontend/src/ts/popups/google-sign-up-popup.ts @@ -153,28 +153,25 @@ $("#googleSignUpPopupWrapper").on("mousedown", (e) => { } }); -const nameIndicator = new InputIndicator( - $("#googleSignUpPopup .inputAndIndicator"), - { - available: { - icon: "fa-check", - level: 1, - }, - unavailable: { - icon: "fa-times", - level: -1, - }, - taken: { - icon: "fa-times", - level: -1, - }, - checking: { - icon: "fa-circle-notch", - spinIcon: true, - level: 0, - }, - } -); +const nameIndicator = new InputIndicator($("#googleSignUpPopup input"), { + available: { + icon: "fa-check", + level: 1, + }, + unavailable: { + icon: "fa-times", + level: -1, + }, + taken: { + icon: "fa-times", + level: -1, + }, + checking: { + icon: "fa-circle-notch", + spinIcon: true, + level: 0, + }, +}); const checkNameDebounced = debounce(1000, async () => { const val = $("#googleSignUpPopup input").val() as string; diff --git a/frontend/static/html/popups.html b/frontend/static/html/popups.html index 819f8c0d7..091cc8710 100644 --- a/frontend/static/html/popups.html +++ b/frontend/static/html/popups.html @@ -474,9 +474,7 @@
Account name
You need to choose a username before continuing
-
- -
+
Sign up
From 4166003f213e795852f4b7c7e1e8515a3c6df746 Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 24 May 2022 16:08:51 +0200 Subject: [PATCH 7/8] allowing tab navigation on login page --- frontend/src/ts/controllers/input-controller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/ts/controllers/input-controller.ts b/frontend/src/ts/controllers/input-controller.ts index 36f9f5d8f..62ae41e6c 100644 --- a/frontend/src/ts/controllers/input-controller.ts +++ b/frontend/src/ts/controllers/input-controller.ts @@ -615,6 +615,9 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void { // dont do anything special if (modalVisible) return; + // dont do anything on login so we can tab betweeen inputs + if (ActivePage.get() === "login") return; + // change page if not on test page if (ActivePage.get() !== "test") { PageController.change("test"); From b05535a381eac2c2dface24a7d486eb3989c9c15 Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 24 May 2022 18:02:55 +0200 Subject: [PATCH 8/8] fixed cookie popup blocking extensions causing weird behaviour --- frontend/src/ts/popups/cookie-popup.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/ts/popups/cookie-popup.ts b/frontend/src/ts/popups/cookie-popup.ts index 51febc837..61a4f39bf 100644 --- a/frontend/src/ts/popups/cookie-popup.ts +++ b/frontend/src/ts/popups/cookie-popup.ts @@ -40,7 +40,10 @@ export function show(): void { .css("opacity", 0) .removeClass("hidden") .animate({ opacity: 1 }, 100, () => { - if ($("#cookiePopupWrapper").outerHeight(true) === 0) { + if ( + $("#cookiePopupWrapper").is(":visible") === false || + $("#cookiePopupWrapper").outerHeight(true) === 0 + ) { visible = false; } else { visible = true;