Browse Source

Added more types to CustomInput and polished more functions.

KrisVos130 7 years ago
parent
commit
9a56c226d5

+ 56 - 16
frontend/app/js/views/Auth/CustomInput.jsx

@@ -1,6 +1,39 @@
 import React, { Component } from "react";
 import PropTypes from "prop-types";
 
+const regex = {
+	azAZ09_: /^[A-Za-z0-9_]+$/,
+	az09_: /^[a-z0-9_]+$/,
+	emailSimple: /^[\x00-\x7F]+@[a-z0-9]+\.[a-z0-9]+(\.[a-z0-9]+)?$/,
+	password: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]/,
+	ascii: /^[\x00-\x7F]+$/,
+};
+
+const isLength = (string, min, max) => {
+	return !(typeof string !== "string" || string.length < min || string.length > max);
+};
+
+const validation = {
+	username: (value) => {
+		const errors = [];
+		if (!isLength(value, 2, 32)) errors.push("Username must be between 2 and 32 characters long.");
+		if (!regex.azAZ09_.test(value)) errors.push("Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.");
+		return errors;
+	},
+	email: (value) => {
+		const errors = [];
+		if (!isLength(value, 3, 254)) errors.push("Email must be between 3 and 254 characters long.");
+		if (value.indexOf("@") !== value.lastIndexOf("@") || !regex.emailSimple.test(value)) errors.push("Invalid email format.");
+		return errors;
+	},
+	password: (value) => {
+		const errors = [];
+		if (!isLength(value, 6, 200)) errors.push("Password must be between 6 and 200 characters long.");
+		if (!regex.password.test(value)) errors.push("Invalid password format.");
+		return errors;
+	},
+};
+
 export default class CustomInput extends Component {
 	static propTypes = {
 		type: PropTypes.string,
@@ -10,6 +43,7 @@ export default class CustomInput extends Component {
 		label: PropTypes.string,
 		placeholder: PropTypes.string,
 		customInputEvents: PropTypes.object,
+		validationCallback: PropTypes.func,
 	};
 
 	static defaultProps = {
@@ -20,6 +54,23 @@ export default class CustomInput extends Component {
 		label: "",
 		placeholder: "",
 		customInputEvents: {},
+		validationCallback: () => {},
+	};
+
+	static validationCallback = (ctx) => {
+		return (name, invalid) => {
+			const inputInvalid = ctx.state.inputInvalid;
+			inputInvalid[name] = invalid;
+			ctx.setState({ inputInvalid });
+		};
+	};
+
+	static hasInvalidInput = (inputInvalid) => {
+		let invalid = false;
+		Object.values(inputInvalid).forEach((value) => {
+			if (value) invalid = true;
+		});
+		return invalid;
 	};
 
 	constructor(props) {
@@ -60,9 +111,11 @@ export default class CustomInput extends Component {
 
 	listErrors = () => {
 		let errors = this.state.errors;
+		let key = 0;
 		if (errors.length > 0) {
 			errors = errors.map((error) => {
-				return (<li>{ error }</li>);
+				key++;
+				return (<li key={ key }>{ error }</li>);
 			});
 			return (
 				<ul className="validation-errors">
@@ -75,22 +128,9 @@ export default class CustomInput extends Component {
 	validateInput = () => {
 		const value = this.state.value;
 		const type = this.props.type;
-		const errors = [];
-		if (type === "email") {
-			if (value.indexOf("@") === -1) {
-				errors.push(`${ this.props.label } must have at least one @.`);
-			} else if (value.lastIndexOf("@") !== value.indexOf("@")) {
-				errors.push(`${ this.props.label } must not have more than one @.`);
-			}
-		} else if (type === "password") {
-			if (value.length < 4) {
-				errors.push(`${ this.props.label } must be at least 4 characters long.`);
-			} else if (value.length > 10) {
-				errors.push(`${ this.props.label } can't be more than 10 characters long.`);
-			}
-		}
-
+		const errors = (validation[type]) ? validation[type](value) : [];
 		this.setState({ errors });
+		this.props.validationCallback(this.props.name, errors.length > 0);
 	};
 
 	render() {

+ 2 - 2
frontend/app/js/views/Auth/Login.jsx

@@ -1,7 +1,7 @@
-import CustomInput from "./CustomInput.jsx";
-
 import React, { Component } from "react";
 
+import CustomInput from "./CustomInput.jsx";
+
 import io from "../../io";
 import config from "../../../../config/default";
 

+ 31 - 29
frontend/app/js/views/Auth/Register.jsx

@@ -1,5 +1,6 @@
 import React, { Component } from "react";
-import PropTypes from "prop-types";
+
+import CustomInput from "./CustomInput.jsx";
 
 import io from "../../io";
 import config from "../../../../config/default";
@@ -13,6 +14,11 @@ export default class Register extends Component {
 			username: "",
 			email: "",
 			recaptcha: "",
+			inputInvalid: {
+				email: true,
+				username: true,
+				password: true,
+			},
 		};
 
 		this.register = this.register.bind(this);
@@ -31,46 +37,42 @@ export default class Register extends Component {
 	}
 
 	register() {
-		io.getSocket(socket => {
-			// if (!email || !username || !password) return Toast.methods.addToast('Please fill in all fields', 8000);
-			// if (!validation.isLength(email, 3, 254)) return Toast.methods.addToast('Email must have between 3 and 254 characters.', 8000);
-			// if (email.indexOf('@') !== email.lastIndexOf('@') || !validation.regex.emailSimple.test(email)) return Toast.methods.addToast('Invalid email format.', 8000);
-			// if (!validation.isLength(username, 2, 32)) return Toast.methods.addToast('Username must have between 2 and 32 characters.', 8000);
-			// if (!validation.regex.azAZ09_.test(username)) return Toast.methods.addToast('Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.', 8000);
-			// if (!validation.isLength(password, 6, 200)) return Toast.methods.addToast('Password must have between 6 and 200 characters.', 8000);
-			// if (!validation.regex.password.test(password)) return Toast.methods.addToast('Invalid password format. Must have one lowercase letter, one uppercase letter, one number and one special character.', 8000);
-			socket.emit("users.register", this.state.username, this.state.email, this.state.password, grecaptcha.getResponse(this.state.recaptcha), res => {
-				if (res.status === "success") {
-					if (res.SID) {
-						const date = new Date();
-						date.setTime(new Date().getTime() + (2 * 365 * 24 * 60 * 60 * 1000));
-						const secure = (config.cookie.secure) ? "secure=true; " : "";
-						document.cookie = `SID=${ res.SID }; expires=${ date.toGMTString() }; domain=${ config.cookie.domain }; ${ secure }path=/`;
-						location.reload(); // if we could avoid this, then that would be better
+		if (CustomInput.hasInvalidInput(this.state.inputInvalid)) {
+			alert("Input invalid. Fix before continuing.");
+		} else {
+			io.getSocket(socket => {
+				socket.emit("users.register", this.state.username, this.state.email, this.state.password, grecaptcha.getResponse(this.state.recaptcha), res => {
+					if (res.status === "success") {
+						if (res.SID) {
+							const date = new Date();
+							date.setTime(new Date().getTime() + (2 * 365 * 24 * 60 * 60 * 1000));
+							const secure = (config.cookie.secure) ? "secure=true; " : "";
+							document.cookie = `SID=${ res.SID }; expires=${ date.toGMTString() }; domain=${ config.cookie.domain }; ${ secure }path=/`;
+							location.reload(); // if we could avoid this, then that would be better
+						} else {
+							// redirect to login
+						}
 					} else {
-						// redirect to login
+						// return res.message, temporarily:
+						alert(res.message); // eslint-disable-line no-alert
 					}
-				} else {
-					// return res.message, temporarily:
-					alert(res.message); // eslint-disable-line no-alert
-				}
+				});
 			});
-		});
+		}
 	}
 
 	githubRedirect() {
 		localStorage.setItem("github_redirect", window.location.pathname);
 	}
 
+	validationCallback = CustomInput.validationCallback(this);
+
 	render() {
 		return (
 			<div>
-				<label htmlFor="email">Email</label>
-				<input type="text" id="email" value={ this.state.email } onChange={ event => this.updateField("email", event) } />
-				<label htmlFor="username">Username</label>
-				<input type="text" id="username" value={ this.state.username } onChange={ event => this.updateField("username", event) } />
-				<label htmlFor="password">Password</label>
-				<input type="password" id="password" value={ this.state.password } onChange={ event => this.updateField("password", event) } />
+				<CustomInput label="Email" placeholder="Email" inputType="email" type="email" name="email" value={ this.state.email } customInputEvents={ { onChange: event => this.updateField("email", event) } } validationCallback={ this.validationCallback } />
+				<CustomInput label="Username" placeholder="Username" inputType="text" type="username" name="username" value={ this.state.username } customInputEvents={ { onChange: event => this.updateField("username", event) } } validationCallback={ this.validationCallback } />
+				<CustomInput label="Password" placeholder="Password" inputType="password" type="password" name="password" value={ this.state.password } customInputEvents={ { onChange: event => this.updateField("password", event) } } validationCallback={ this.validationCallback } />
 				<div id="recaptcha" />
 				<p>By logging in/registering you agree to our <a href="/terms">Terms of Service</a> and <a href="/privacy">Privacy Policy</a>.</p>
 				<button onClick={ this.register }>Register</button>