11import Ape from "../ape" ;
22import Page from "./page" ;
33import * as Skeleton from "../utils/skeleton" ;
4- import * as Misc from "../utils/misc" ;
54import TypoList from "../utils/typo-list" ;
6- import { UserEmailSchema , UserNameSchema } from "@monkeytype/schemas/users" ;
5+ import {
6+ PasswordSchema ,
7+ UserEmailSchema ,
8+ UserNameSchema ,
9+ } from "@monkeytype/schemas/users" ;
710import { validateWithIndicator } from "../elements/input-validation" ;
11+ import { isDevEnvironment } from "../utils/misc" ;
812import { z } from "zod" ;
913
1014let registerForm : {
@@ -39,11 +43,19 @@ export function hidePreloader(): void {
3943 $ ( ".pageLogin .preloader" ) . addClass ( "hidden" ) ;
4044}
4145
46+ function isFormComplete ( ) : boolean {
47+ return (
48+ registerForm . name !== undefined &&
49+ registerForm . email !== undefined &&
50+ registerForm . password !== undefined
51+ ) ;
52+ }
53+
4254export const updateSignupButton = ( ) : void => {
43- if ( Object . values ( registerForm ) . some ( ( it ) => it === undefined ) ) {
44- disableSignUpButton ( ) ;
45- } else {
55+ if ( isFormComplete ( ) ) {
4656 enableSignUpButton ( ) ;
57+ } else {
58+ disableSignUpButton ( ) ;
4759 }
4860} ;
4961
@@ -53,9 +65,7 @@ type SignupData = {
5365 password : string ;
5466} ;
5567export function getSignupData ( ) : SignupData | false {
56- return Object . values ( registerForm ) . some ( ( it ) => it === undefined )
57- ? false
58- : ( registerForm as SignupData ) ;
68+ return isFormComplete ( ) ? ( registerForm as SignupData ) : false ;
5969}
6070
6171const nameInputEl = document . querySelector (
@@ -84,9 +94,58 @@ let disposableEmailModule: typeof import("disposable-email-domains-js") | null =
8494 null ;
8595let moduleLoadAttempted = false ;
8696
87- const emailInputEl = document . querySelector (
88- ".page.pageLogin .register.side input.emailInput"
89- ) as HTMLInputElement ;
97+ const emailInputEl = validateWithIndicator (
98+ document . querySelector (
99+ ".page.pageLogin .register.side input.emailInput"
100+ ) as HTMLInputElement ,
101+ {
102+ schema : UserEmailSchema ,
103+ isValid : async ( email : string ) => {
104+ const educationRegex =
105+ / @ .* ( s t u d e n t | e d u c a t i o n | s c h o o l | \. e d u $ | \. e d u \. | \. a c \. | \. s c h \. ) / i;
106+ if ( educationRegex . test ( email ) ) {
107+ return {
108+ warning :
109+ "Some education emails will fail to receive our messages, or disable the account as soon as you graduate. Consider using a personal email address." ,
110+ } ;
111+ }
112+
113+ const emailHasTypo = TypoList . some ( ( typo ) => {
114+ return email . endsWith ( typo ) ;
115+ } ) ;
116+ if ( emailHasTypo ) {
117+ return {
118+ warning : "Please check your email address, it may contain a typo." ,
119+ } ;
120+ }
121+
122+ if (
123+ disposableEmailModule &&
124+ disposableEmailModule . isDisposableEmail !== undefined
125+ ) {
126+ try {
127+ if ( disposableEmailModule . isDisposableEmail ( email ) ) {
128+ return {
129+ warning :
130+ "Using a temporary email may cause issues with logging in, password resets and support. Consider using a permanent email address. Don't worry, we don't send spam." ,
131+ } ;
132+ }
133+ } catch ( e ) {
134+ // Silent failure
135+ }
136+ }
137+
138+ return true ;
139+ } ,
140+ debounceDelay : 0 ,
141+ callback : ( result ) => {
142+ if ( result . status === "success" ) {
143+ //re-validate the verify email
144+ emailVerifyInputEl . dispatchEvent ( new Event ( "input" ) ) ;
145+ }
146+ } ,
147+ }
148+ ) ;
90149
91150emailInputEl . addEventListener ( "focus" , async ( ) => {
92151 if ( ! moduleLoadAttempted ) {
@@ -99,54 +158,6 @@ emailInputEl.addEventListener("focus", async () => {
99158 }
100159} ) ;
101160
102- validateWithIndicator ( emailInputEl , {
103- schema : UserEmailSchema ,
104- isValid : async ( email : string ) => {
105- const educationRegex =
106- / @ .* ( s t u d e n t | e d u c a t i o n | s c h o o l | \. e d u $ | \. e d u \. | \. a c \. | \. s c h \. ) / i;
107- if ( educationRegex . test ( email ) ) {
108- return {
109- warning :
110- "Some education emails will fail to receive our messages, or disable the account as soon as you graduate. Consider using a personal email address." ,
111- } ;
112- }
113-
114- const emailHasTypo = TypoList . some ( ( typo ) => {
115- return email . endsWith ( typo ) ;
116- } ) ;
117- if ( emailHasTypo ) {
118- return {
119- warning : "Please check your email address, it may contain a typo." ,
120- } ;
121- }
122-
123- if (
124- disposableEmailModule &&
125- disposableEmailModule . isDisposableEmail !== undefined
126- ) {
127- try {
128- if ( disposableEmailModule . isDisposableEmail ( email ) ) {
129- return {
130- warning :
131- "Using a temporary email may cause issues with logging in, password resets and support. Consider using a permanent email address. Don't worry, we don't send spam." ,
132- } ;
133- }
134- } catch ( e ) {
135- // Silent failure
136- }
137- }
138-
139- return true ;
140- } ,
141- debounceDelay : 0 ,
142- callback : ( result ) => {
143- if ( result . status === "success" ) {
144- //re-validate the verify email
145- emailVerifyInputEl . dispatchEvent ( new Event ( "input" ) ) ;
146- }
147- } ,
148- } ) ;
149-
150161const emailVerifyInputEl = document . querySelector (
151162 ".page.pageLogin .register.side input.verifyEmailInput"
152163) as HTMLInputElement ;
@@ -159,36 +170,27 @@ validateWithIndicator(emailVerifyInputEl, {
159170 debounceDelay : 0 ,
160171 callback : ( result ) => {
161172 registerForm . email =
162- result . status === "success" ? emailInputEl . value : undefined ;
173+ emailInputEl . isValid ( ) && result . status === "success"
174+ ? emailInputEl . value
175+ : undefined ;
163176 updateSignupButton ( ) ;
164177 } ,
165178} ) ;
166179
167- const passwordInputEl = document . querySelector (
168- ".page.pageLogin .register.side .passwordInput"
169- ) as HTMLInputElement ;
170- validateWithIndicator ( passwordInputEl , {
171- schema : z . string ( ) . min ( 6 ) , //firebase requires min 6 chars, we apply stricter rules on prod
172- isValid : async ( password : string ) => {
173- if ( ! Misc . isDevEnvironment ( ) && ! Misc . isPasswordStrong ( password ) ) {
174- if ( password . length < 8 ) {
175- return "Password must be at least 8 characters" ;
176- } else if ( password . length > 64 ) {
177- return "Password must be at most 64 characters" ;
178- } else {
179- return "Password must contain at least one capital letter, number, and special character" ;
180+ const passwordInputEl = validateWithIndicator (
181+ document . querySelector (
182+ ".page.pageLogin .register.side .passwordInput"
183+ ) as HTMLInputElement ,
184+ {
185+ schema : isDevEnvironment ( ) ? z . string ( ) . min ( 6 ) : PasswordSchema ,
186+ callback : ( result ) => {
187+ if ( result . status === "success" ) {
188+ //re-validate the verify password
189+ passwordVerifyInputEl . dispatchEvent ( new Event ( "input" ) ) ;
180190 }
181- }
182- return true ;
183- } ,
184- debounceDelay : 0 ,
185- callback : ( result ) => {
186- if ( result . status === "success" ) {
187- //re-validate the verify password
188- passwordVerifyInputEl . dispatchEvent ( new Event ( "input" ) ) ;
189- }
190- } ,
191- } ) ;
191+ } ,
192+ }
193+ ) ;
192194
193195const passwordVerifyInputEl = document . querySelector (
194196 ".page.pageLogin .register.side .verifyPasswordInput"
@@ -202,7 +204,9 @@ validateWithIndicator(passwordVerifyInputEl, {
202204 debounceDelay : 0 ,
203205 callback : ( result ) => {
204206 registerForm . password =
205- result . status === "success" ? passwordInputEl . value : undefined ;
207+ passwordInputEl . isValid ( ) && result . status === "success"
208+ ? passwordInputEl . value
209+ : undefined ;
206210 updateSignupButton ( ) ;
207211 } ,
208212} ) ;
0 commit comments