Refactor auth pages

This commit is contained in:
Robert Long 2022-01-05 16:34:01 -08:00
parent 71986f6001
commit 8a452d80e2
8 changed files with 60 additions and 143 deletions

View file

@ -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";

View file

@ -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 (
<div ref={containerRef}>
<div ref={recaptchaRef} />
</div>
);
}

View file

@ -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() {

View file

@ -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() {
/>
</FieldRow>
{!isPasswordlessUser && (
<FieldRow>
<InputField
id="acceptTerms"
type="checkbox"
name="acceptTerms"
onChange={(e) => setAcceptTerms(e.target.checked)}
checked={acceptTerms}
label="Accept Privacy Policy"
ref={acceptTermsRef}
/>
<a target="_blank" href={privacyPolicyUrl}>
<Caption>
This site is protected by ReCAPTCHA and the Google{" "}
<Link href="https://www.google.com/policies/privacy/">
Privacy Policy
</a>
</FieldRow>
)}
{!isPasswordlessUser && recaptchaKey && (
<FieldRow>
<RecaptchaInput
publicKey={recaptchaKey}
onResponse={setRecaptchaResponse}
/>
</FieldRow>
</Link>{" "}
and{" "}
<Link href="https://policies.google.com/terms">
Terms of Service
</Link>{" "}
apply.
<br />
By clicking "Go", you agree to our{" "}
<Link href={privacyPolicyUrl}>Terms and conditions</Link>
</Caption>
)}
{error && (
<FieldRow>
@ -231,6 +193,7 @@ export function RegisterPage() {
{registering ? "Registering..." : "Register"}
</Button>
</FieldRow>
<div id={recaptchaId} />
</form>
</div>
<div className={styles.authLinks}>

View file

@ -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";

View file

@ -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";