-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Email verification landing page states, expiration, resend email without auth #867
Changes from 3 commits
bf9efff
d5389b2
a63b2c7
d423136
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,9 +118,13 @@ const verifyEmail = async () => { | |
/** | ||
* Sends request to resend the account verification link to the user's email | ||
*/ | ||
const resendVerifyEmail = async () => { | ||
const resendVerifyEmail = async (token?: string) => { | ||
try { | ||
await postAuthRequest(`${BASE_URL}auth/resend_verify_email`); | ||
if (token) { | ||
await postAuthRequest(`${BASE_URL}auth/resend_verification_with_token?u=${token}`); | ||
} else { | ||
await postAuthRequest(`${BASE_URL}auth/resend_verification`); | ||
} | ||
return true; | ||
} catch { | ||
return false; | ||
|
@@ -237,6 +241,8 @@ const postAuthRequest = async ( | |
try { | ||
if (result.ok) { | ||
return await result.json(); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't know how this else clause got removed, but this is necessary for any non-200 result status code and text to be returned. checked that returning the non-200 result json works on other There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yeah that's great to have for error handling |
||
return await result.json(); | ||
} | ||
} catch { | ||
return result; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,55 +2,62 @@ import { useState, useEffect } from "react"; | |
import { withI18n, withI18nProps } from "@lingui/react"; | ||
import { Trans, t } from "@lingui/macro"; | ||
import AuthClient, { VerifyStatusCode } from "../components/AuthClient"; | ||
import { JustfixUser } from "state-machine"; | ||
import Page from "components/Page"; | ||
import { useLocation } from "react-router-dom"; | ||
|
||
const VerifyEmailPage = withI18n()((props: withI18nProps) => { | ||
const { i18n } = props; | ||
const [user, setUser] = useState<JustfixUser>(); | ||
const { search } = useLocation(); | ||
const [loading, setLoading] = useState(true); | ||
const [isVerified, setIsVerified] = useState(false); | ||
const [isAlreadyVerified, setIsAlreadyVerified] = useState(false); | ||
const [isExpired, setIsExpired] = useState(false); | ||
const [isEmailResent, setIsEmailResent] = useState(false); | ||
|
||
useEffect(() => { | ||
const asyncFetchUser = async () => { | ||
const _user = await AuthClient.fetchUser(); | ||
if (_user) { | ||
setUser({ | ||
..._user, | ||
subscriptions: | ||
_user.subscriptions?.map((s: any) => { | ||
return { ...s }; | ||
}) || [], | ||
}); | ||
setIsVerified(_user.verified); | ||
} | ||
}; | ||
asyncFetchUser(); | ||
}, []); | ||
const [unknownError, setUnknownError] = useState(false); | ||
const params = new URLSearchParams(search); | ||
const token = params.get("u") || ""; | ||
|
||
useEffect(() => { | ||
const asyncVerifyEmail = async () => { | ||
return await AuthClient.verifyEmail(); | ||
}; | ||
|
||
asyncVerifyEmail().then((result) => { | ||
if ( | ||
result.statusCode === VerifyStatusCode.Success || | ||
result.statusCode === VerifyStatusCode.AlreadyVerified | ||
) { | ||
setIsVerified(true); | ||
} | ||
|
||
if ( | ||
result.statusCode === VerifyStatusCode.Expired || | ||
result.statusCode === VerifyStatusCode.Unknown | ||
) { | ||
setIsExpired(true); | ||
switch (result.statusCode) { | ||
case VerifyStatusCode.Success: | ||
setIsVerified(true); | ||
break; | ||
case VerifyStatusCode.AlreadyVerified: | ||
setIsVerified(true); | ||
setIsAlreadyVerified(true); | ||
break; | ||
case VerifyStatusCode.Expired: | ||
setIsExpired(true); | ||
break; | ||
default: | ||
setUnknownError(true); | ||
} | ||
setLoading(false); | ||
}); | ||
}, []); | ||
|
||
const delaySeconds = 5; | ||
const baseUrl = window.location.origin; | ||
const redirectUrl = `${baseUrl}/${i18n.language}`; | ||
|
||
const updateCountdown = () => { | ||
let timeLeft = delaySeconds; | ||
const delayInterval = delaySeconds * 100; | ||
|
||
setInterval(() => { | ||
timeLeft && timeLeft--; // prevents counter from going below 0 | ||
document.getElementById("countdown")!.textContent = timeLeft.toString(); | ||
if (timeLeft <= 0) { | ||
document.location.href = redirectUrl; | ||
} | ||
}, delayInterval); | ||
}; | ||
|
||
const expiredLinkPage = () => { | ||
const resendEmailPage = ( | ||
<div className="text-center"> | ||
|
@@ -59,7 +66,7 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { | |
<button | ||
className="button is-secondary" | ||
onClick={async () => { | ||
setIsEmailResent(await AuthClient.resendVerifyEmail()); | ||
setIsEmailResent(await AuthClient.resendVerifyEmail(token)); | ||
}} | ||
> | ||
<Trans>Resend verification email</Trans> | ||
kiwansim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -72,16 +79,28 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { | |
<Trans render="h4">Check your email inbox & spam</Trans> | ||
<br /> | ||
<Trans> | ||
Click the link we sent to verify your email address {!!user && user.email}. It may take a | ||
few minutes to arrive. Once your email has been verified, you’ll be signed up for Data | ||
Updates. | ||
Click the link we sent to verify your email address. It may take a few minutes to arrive. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removing the user email here since user could be in expired verification link flow while not logged in |
||
Once your email has been verified, you’ll be signed up for Data Updates. | ||
</Trans> | ||
</div> | ||
); | ||
|
||
return isEmailResent ? resendEmailConfirmation : resendEmailPage; | ||
}; | ||
|
||
// TODO add error logging | ||
const errorPage = () => ( | ||
<div className="text-center"> | ||
<Trans render="h4">We’re having trouble verifying your email at this time.</Trans> | ||
<br /> | ||
<Trans> | ||
Please try again later. If you’re still having issues, contact support@justfix.org. | ||
<br /> | ||
<br />A report about this issue has been sent to our team. | ||
</Trans> | ||
</div> | ||
); | ||
|
||
const successPage = () => ( | ||
<div className="text-center"> | ||
<Trans render="h4">Email verified</Trans> | ||
|
@@ -90,11 +109,36 @@ const VerifyEmailPage = withI18n()((props: withI18nProps) => { | |
</div> | ||
); | ||
|
||
const alreadyVerifiedPage = () => ( | ||
<div className="text-center"> | ||
<Trans render="h4">Your email is already verified</Trans> | ||
<br /> | ||
<Trans className="text-center">You will be redirected back to Who Owns What in:</Trans> | ||
<br /> | ||
<br>{updateCountdown()}</br> | ||
<Trans className="d-flex justify-content-center"> | ||
<span id="countdown">{delaySeconds}</span> seconds | ||
</Trans> | ||
<br /> | ||
<br /> | ||
<Trans className="text-center">If you are not redirected, please click this link:</Trans> | ||
<br /> | ||
<a href={redirectUrl}>{redirectUrl}</a> | ||
</div> | ||
); | ||
|
||
return ( | ||
<Page title={i18n._(t`Verify your email address`)}> | ||
<div className="TextPage Page"> | ||
<div className="page-container"> | ||
{isVerified ? successPage() : isExpired && expiredLinkPage()} | ||
{!loading && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
the nested ternary operator is gross but i didn't have any other ideas. lmk if you do! |
||
(isVerified | ||
? isAlreadyVerified | ||
? alreadyVerifiedPage() | ||
: successPage() | ||
: isExpired | ||
? expiredLinkPage() | ||
: unknownError && errorPage())} | ||
</div> | ||
</div> | ||
</Page> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
optional token would only be included from the "verification link has expired" flow