import React, { useState, useRef } from 'react'; import PropTypes from 'prop-types'; import './Auth.scss'; import ReCAPTCHA from 'react-google-recaptcha'; import { Link } from 'react-router-dom'; import * as auth from '../../../client/action/auth'; import Text from '../../atoms/text/Text'; import Button from '../../atoms/button/Button'; import Input from '../../atoms/input/Input'; import Spinner from '../../atoms/spinner/Spinner'; import CinnySvg from '../../../../public/res/svg/cinny.svg'; const USERNAME_REGEX = /^[a-z0-9_-]+$/; const BAD_USERNAME_ERROR = 'Username must contain only lowercase letters, numbers, dashes and underscores.'; const PASSWORD_REGEX = /.+/; const PASSWORD_STRENGHT_REGEX = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,16}$/; const BAD_PASSWORD_ERROR = 'Password must contain 1 number, 1 uppercase letters, 1 lowercase letters, 1 non-alpha numeric number, 8-16 characters with no space.'; const CONFIRM_PASSWORD_ERROR = 'Password don\'t match.'; const EMAIL_REGEX = /([a-z0-9]+[_a-z0-9.-][a-z0-9]+)@([a-z0-9-]+(?:.[a-z0-9-]+).[a-z]{2,4})/; const BAD_EMAIL_ERROR = 'Invalid email address'; function isValidInput(value, regex) { return regex.test(value); } function renderErrorMessage(error) { const $error = document.getElementById('auth_error'); $error.textContent = error; $error.style.display = 'block'; } function showBadInputError($input, error) { renderErrorMessage(error); $input.focus(); const myInput = $input; myInput.style.border = '1px solid var(--bg-danger)'; myInput.style.boxShadow = 'none'; document.getElementById('auth_submit-btn').disabled = true; } function validateOnChange(e, regex, error) { if (!isValidInput(e.target.value, regex) && e.target.value) { showBadInputError(e.target, error); return; } document.getElementById('auth_error').style.display = 'none'; e.target.style.removeProperty('border'); e.target.style.removeProperty('box-shadow'); document.getElementById('auth_submit-btn').disabled = false; } function Auth({ type }) { const [process, changeProcess] = useState(null); const usernameRef = useRef(null); const homeserverRef = useRef(null); const passwordRef = useRef(null); const confirmPasswordRef = useRef(null); const emailRef = useRef(null); function register(recaptchaValue, terms, verified) { auth.register( usernameRef.current.value, homeserverRef.current.value, passwordRef.current.value, emailRef.current.value, recaptchaValue, terms, verified, ).then((res) => { document.getElementById('auth_submit-btn').disabled = false; if (res.type === 'recaptcha') { changeProcess({ type: res.type, sitekey: res.public_key }); return; } if (res.type === 'terms') { changeProcess({ type: res.type, en: res.en }); } if (res.type === 'email') { changeProcess({ type: res.type }); } if (res.type === 'done') { window.location.replace('/'); } }).catch((error) => { changeProcess(null); renderErrorMessage(error); document.getElementById('auth_submit-btn').disabled = false; }); if (terms) { changeProcess({ type: 'loading', message: 'Sending email verification link...' }); } else changeProcess({ type: 'loading', message: 'Registration in progress...' }); } function handleLogin(e) { e.preventDefault(); document.getElementById('auth_submit-btn').disabled = true; document.getElementById('auth_error').style.display = 'none'; if (!isValidInput(usernameRef.current.value, USERNAME_REGEX)) { showBadInputError(usernameRef.current, BAD_USERNAME_ERROR); return; } auth.login(usernameRef.current.value, homeserverRef.current.value, passwordRef.current.value) .then(() => { document.getElementById('auth_submit-btn').disabled = false; window.location.replace('/'); }) .catch((error) => { changeProcess(null); renderErrorMessage(error); document.getElementById('auth_submit-btn').disabled = false; }); changeProcess({ type: 'loading', message: 'Login in progress...' }); } function handleRegister(e) { e.preventDefault(); document.getElementById('auth_submit-btn').disabled = true; document.getElementById('auth_error').style.display = 'none'; if (!isValidInput(usernameRef.current.value, USERNAME_REGEX)) { showBadInputError(usernameRef.current, BAD_USERNAME_ERROR); return; } if (!isValidInput(passwordRef.current.value, PASSWORD_STRENGHT_REGEX)) { showBadInputError(passwordRef.current, BAD_PASSWORD_ERROR); return; } if (passwordRef.current.value !== confirmPasswordRef.current.value) { showBadInputError(confirmPasswordRef.current, CONFIRM_PASSWORD_ERROR); return; } if (!isValidInput(emailRef.current.value, EMAIL_REGEX)) { showBadInputError(emailRef.current, BAD_EMAIL_ERROR); return; } register(); } const handleAuth = (type === 'login') ? handleLogin : handleRegister; return ( <> {process?.type === 'loading' && } {process?.type === 'recaptcha' && { if (typeof v === 'string') register(v); }} />} {process?.type === 'terms' && } {process?.type === 'email' && ( Verify email Please check your email {' '} {`(${emailRef.current.value})`} {' '} and validate before continuing further. register(undefined, undefined, true)}>Continue )} { type === 'login' ? 'Login' : 'Register' } validateOnChange(e, USERNAME_REGEX, BAD_USERNAME_ERROR)} id="auth_username" label="Username" required /> validateOnChange(e, ((type === 'login') ? PASSWORD_REGEX : PASSWORD_STRENGHT_REGEX), BAD_PASSWORD_ERROR)} id="auth_password" type="password" label="Password" required /> {type === 'register' && ( <> validateOnChange(e, new RegExp(`^(${passwordRef.current.value})$`), CONFIRM_PASSWORD_ERROR)} id="auth_confirmPassword" type="password" label="Confirm password" required /> validateOnChange(e, EMAIL_REGEX, BAD_EMAIL_ERROR)} id="auth_email" type="email" label="Email" required /> > )} Error {type === 'login' ? 'Login' : 'Register' } {`${(type === 'login' ? 'Don\'t have' : 'Already have')} an account?`} { type === 'login' ? ' Register' : ' Login' } > ); } Auth.propTypes = { type: PropTypes.string.isRequired, }; function StaticWrapper({ children }) { return ( Cinny Yet another matrix client. { children } ); } StaticWrapper.propTypes = { children: PropTypes.node.isRequired, }; function LoadingScreen({ message }) { return ( {message} ); } LoadingScreen.propTypes = { message: PropTypes.string.isRequired, }; function Recaptcha({ message, sitekey, onChange }) { return ( {message} ); } Recaptcha.propTypes = { message: PropTypes.string.isRequired, sitekey: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, }; function Terms({ url, onSubmit }) { return ( onSubmit(undefined, true)}> Agree with terms In order to complete registration, you need to agree with terms and conditions. {'I accept '} Terms and Conditions Submit ); } Terms.propTypes = { url: PropTypes.string.isRequired, onSubmit: PropTypes.func.isRequired, }; function ProcessWrapper({ children }) { return ( {children} ); } ProcessWrapper.propTypes = { children: PropTypes.node.isRequired, }; export default Auth;