From 8a452d80e22086fe341c4916232a891db0890868 Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Jan 2022 16:34:01 -0800 Subject: [PATCH] Refactor auth pages --- src/App.jsx | 4 +- src/RecaptchaInput.jsx | 46 --------- src/{ => auth}/LoginPage.jsx | 8 +- src/{ => auth}/LoginPage.module.css | 0 src/{ => auth}/RegisterPage.jsx | 141 ++++++++++------------------ src/{ => auth}/useRecaptcha.js | 0 src/home/UnauthenticatedView.jsx | 2 +- src/room/RoomAuthView.jsx | 2 +- 8 files changed, 60 insertions(+), 143 deletions(-) delete mode 100644 src/RecaptchaInput.jsx rename src/{ => auth}/LoginPage.jsx (94%) rename src/{ => auth}/LoginPage.module.css (100%) rename src/{ => auth}/RegisterPage.jsx (64%) rename src/{ => auth}/useRecaptcha.js (100%) diff --git a/src/App.jsx b/src/App.jsx index dc5c6e68..4808a67d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -19,8 +19,8 @@ import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import * as Sentry from "@sentry/react"; import { OverlayProvider } from "@react-aria/overlays"; import { HomePage } from "./home/HomePage"; -import { LoginPage } from "./LoginPage"; -import { RegisterPage } from "./RegisterPage"; +import { LoginPage } from "./auth/LoginPage"; +import { RegisterPage } from "./auth/RegisterPage"; import { RoomPage } from "./room/RoomPage"; import { RoomRedirect } from "./room/RoomRedirect"; import { ClientProvider } from "./ConferenceCallManagerHooks"; diff --git a/src/RecaptchaInput.jsx b/src/RecaptchaInput.jsx deleted file mode 100644 index 1901196c..00000000 --- a/src/RecaptchaInput.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useEffect, useRef } from "react"; - -export function RecaptchaInput({ publicKey, onResponse }) { - const containerRef = useRef(); - const recaptchaRef = useRef(); - - useEffect(() => { - const onRecaptchaLoaded = () => { - if (!recaptchaRef.current) { - return; - } - - window.grecaptcha.render(recaptchaRef.current, { - sitekey: publicKey, - callback: (response) => { - if (!recaptchaRef.current) { - return; - } - - onResponse(response); - }, - }); - }; - - if ( - typeof window.grecaptcha !== "undefined" && - typeof window.grecaptcha.render === "function" - ) { - onRecaptchaLoaded(); - } else { - window.mxOnRecaptchaLoaded = onRecaptchaLoaded; - const scriptTag = document.createElement("script"); - scriptTag.setAttribute( - "src", - `https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit` - ); - containerRef.current.appendChild(scriptTag); - } - }, []); - - return ( -
-
-
- ); -} diff --git a/src/LoginPage.jsx b/src/auth/LoginPage.jsx similarity index 94% rename from src/LoginPage.jsx rename to src/auth/LoginPage.jsx index 60992611..c5a6841a 100644 --- a/src/LoginPage.jsx +++ b/src/auth/LoginPage.jsx @@ -16,14 +16,14 @@ limitations under the License. import React, { useCallback, useRef, useState, useMemo } from "react"; import { useHistory, useLocation, Link } from "react-router-dom"; -import { ReactComponent as Logo } from "./icons/LogoLarge.svg"; -import { FieldRow, InputField, ErrorMessage } from "./Input"; -import { Button } from "./button"; +import { ReactComponent as Logo } from "../icons/LogoLarge.svg"; +import { FieldRow, InputField, ErrorMessage } from "../Input"; +import { Button } from "../button"; import { defaultHomeserver, defaultHomeserverHost, useInteractiveLogin, -} from "./ConferenceCallManagerHooks"; +} from "../ConferenceCallManagerHooks"; import styles from "./LoginPage.module.css"; export function LoginPage() { diff --git a/src/LoginPage.module.css b/src/auth/LoginPage.module.css similarity index 100% rename from src/LoginPage.module.css rename to src/auth/LoginPage.module.css diff --git a/src/RegisterPage.jsx b/src/auth/RegisterPage.jsx similarity index 64% rename from src/RegisterPage.jsx rename to src/auth/RegisterPage.jsx index 81c6662e..c9ed9b82 100644 --- a/src/RegisterPage.jsx +++ b/src/auth/RegisterPage.jsx @@ -15,18 +15,19 @@ limitations under the License. */ import React, { useCallback, useEffect, useRef, useState } from "react"; -import { useHistory, useLocation, Link } from "react-router-dom"; -import { FieldRow, InputField, ErrorMessage, Field } from "./Input"; -import { Button } from "./button"; +import { useHistory, useLocation } from "react-router-dom"; +import { FieldRow, InputField, ErrorMessage } from "../Input"; +import { Button } from "../button"; import { useClient, defaultHomeserverHost, useInteractiveRegistration, -} from "./ConferenceCallManagerHooks"; +} from "../ConferenceCallManagerHooks"; import styles from "./LoginPage.module.css"; -import { ReactComponent as Logo } from "./icons/LogoLarge.svg"; -import { LoadingView } from "./FullScreenView"; -import { RecaptchaInput } from "./RecaptchaInput"; +import { ReactComponent as Logo } from "../icons/LogoLarge.svg"; +import { LoadingView } from "../FullScreenView"; +import { useRecaptcha } from "./useRecaptcha"; +import { Caption, Link } from "../typography/Typography"; export function RegisterPage() { const { @@ -37,17 +38,15 @@ export function RegisterPage() { isPasswordlessUser, } = useClient(); const confirmPasswordRef = useRef(); - const acceptTermsRef = useRef(); const history = useHistory(); const location = useLocation(); const [registering, setRegistering] = useState(false); const [error, setError] = useState(); const [password, setPassword] = useState(""); const [passwordConfirmation, setPasswordConfirmation] = useState(""); - const [acceptTerms, setAcceptTerms] = useState(false); const [{ privacyPolicyUrl, recaptchaKey }, register] = useInteractiveRegistration(); - const [recaptchaResponse, setRecaptchaResponse] = useState(); + const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey); const onSubmitRegisterForm = useCallback( (e) => { @@ -56,51 +55,35 @@ export function RegisterPage() { const userName = data.get("userName"); const password = data.get("password"); const passwordConfirmation = data.get("passwordConfirmation"); - const acceptTerms = data.get("acceptTerms"); - if (isPasswordlessUser) { - if (password !== passwordConfirmation) { - return; - } - - setRegistering(true); - - changePassword(password) - .then(() => { - if (location.state && location.state.from) { - history.push(location.state.from); - } else { - history.push("/"); - } - }) - .catch((error) => { - setError(error); - setRegistering(false); - }); - } else { - if ( - password !== passwordConfirmation || - !acceptTerms || - !recaptchaResponse - ) { - return; - } - - setRegistering(true); - - register(userName, password, recaptchaResponse) - .then(() => { - if (location.state && location.state.from) { - history.push(location.state.from); - } else { - history.push("/"); - } - }) - .catch((error) => { - setError(error); - setRegistering(false); - }); + if (password !== passwordConfirmation) { + return; } + + async function submit() { + setRegistering(true); + + if (isPasswordlessUser) { + changePassword(password); + } else { + const recaptchaResponse = await execute(); + await register(userName, password, recaptchaResponse); + } + } + + submit() + .then(() => { + if (location.state && location.state.from) { + history.push(location.state.from); + } else { + history.push("/"); + } + }) + .catch((error) => { + setError(error); + setRegistering(false); + reset(); + }); }, [ register, @@ -108,7 +91,8 @@ export function RegisterPage() { location, history, isPasswordlessUser, - recaptchaResponse, + reset, + execute, ] ); @@ -124,20 +108,6 @@ export function RegisterPage() { } }, [password, passwordConfirmation]); - useEffect(() => { - if (!acceptTermsRef.current) { - return; - } - - if (!acceptTerms) { - acceptTermsRef.current.setCustomValidity( - "You must accept the terms to continue." - ); - } else { - acceptTermsRef.current.setCustomValidity(""); - } - }, [acceptTerms]); - useEffect(() => { if (!loading && isAuthenticated && !isPasswordlessUser) { history.push("/"); @@ -198,28 +168,20 @@ export function RegisterPage() { /> {!isPasswordlessUser && ( - - setAcceptTerms(e.target.checked)} - checked={acceptTerms} - label="Accept Privacy Policy" - ref={acceptTermsRef} - /> - + + This site is protected by ReCAPTCHA and the Google{" "} + Privacy Policy - - - )} - {!isPasswordlessUser && recaptchaKey && ( - - - + {" "} + and{" "} + + Terms of Service + {" "} + apply. +
+ By clicking "Go", you agree to our{" "} + Terms and conditions + )} {error && ( @@ -231,6 +193,7 @@ export function RegisterPage() { {registering ? "Registering..." : "Register"} +
diff --git a/src/useRecaptcha.js b/src/auth/useRecaptcha.js similarity index 100% rename from src/useRecaptcha.js rename to src/auth/useRecaptcha.js diff --git a/src/home/UnauthenticatedView.jsx b/src/home/UnauthenticatedView.jsx index 79d33987..bb5306ca 100644 --- a/src/home/UnauthenticatedView.jsx +++ b/src/home/UnauthenticatedView.jsx @@ -12,7 +12,7 @@ import { } from "../ConferenceCallManagerHooks"; import { useModalTriggerState } from "../Modal"; import { JoinExistingCallModal } from "../JoinExistingCallModal"; -import { useRecaptcha } from "../useRecaptcha"; +import { useRecaptcha } from "../auth/useRecaptcha"; import { Body, Caption, Link, Headline } from "../typography/Typography"; import { Form } from "../form/Form"; import styles from "./UnauthenticatedView.module.css"; diff --git a/src/room/RoomAuthView.jsx b/src/room/RoomAuthView.jsx index acaa1bb4..72634e59 100644 --- a/src/room/RoomAuthView.jsx +++ b/src/room/RoomAuthView.jsx @@ -4,7 +4,7 @@ import { Button } from "../button"; import { Body, Caption, Link, Headline } from "../typography/Typography"; import { Header, HeaderLogo, LeftNav, RightNav } from "../Header"; import { useLocation } from "react-router-dom"; -import { useRecaptcha } from "../useRecaptcha"; +import { useRecaptcha } from "../auth/useRecaptcha"; import { FieldRow, InputField, ErrorMessage } from "../Input"; import { randomString } from "matrix-js-sdk/src/randomstring"; import { useInteractiveRegistration } from "../ConferenceCallManagerHooks";