Skip to content

Commit 5ac5166

Browse files
Merge pull request #118 from MobilityData/fix/105-email-validation-junk
fix: junk email + email verification page
2 parents 56103e3 + 598165a commit 5ac5166

5 files changed

Lines changed: 171 additions & 1 deletion

File tree

messages/en.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@
116116
"transitFeedsRedirectTitle": "You've been redirected from TransitFeeds",
117117
"transitFeedsRedirectBody": "This page now lives on MobilityDatabase.org, where you'll find the most up-to-date transit data."
118118
},
119+
"emailVerification": {
120+
"loadingTitle": "Verifying your email",
121+
"loadingDescription": "Please wait while we confirm your email address.",
122+
"successTitle": "Email verified",
123+
"successDescription": "Your email address has been verified successfully.You can now continue using your Mobility Database account.",
124+
"errorTitle": "Unable to verify email",
125+
"errorDescription": "This verification link is invalid, incomplete, or has already been used.",
126+
"invalidLink": "The verification link is missing required information.",
127+
"verificationFailed": "We could not verify your email with this link."
128+
},
119129
"feeds": {
120130
"feeds": "Feeds",
121131
"dataType": "Data Format",

messages/fr.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@
116116
"transitFeedsRedirectTitle": "Vous avez été redirigé depuis TransitFeeds",
117117
"transitFeedsRedirectBody": "Cette page se trouve désormais sur MobilityDatabase.org, où vous trouverez les données de transit les plus récentes."
118118
},
119+
"emailVerification": {
120+
"loadingTitle": "Vérification de votre e-mail",
121+
"loadingDescription": "Veuillez patienter pendant que nous confirmons votre adresse e-mail.",
122+
"successTitle": "E-mail vérifié",
123+
"successDescription": "Votre adresse e-mail a bien été vérifiée. Vous pouvez retourner sur Mobility Database et continuer à utiliser votre compte.",
124+
"errorTitle": "Impossible de vérifier l'e-mail",
125+
"errorDescription": "Ce lien de vérification est invalide, incomplet ou a déjà été utilisé.",
126+
"invalidLink": "Le lien de vérification ne contient pas les informations requises.",
127+
"verificationFailed": "Nous n'avons pas pu vérifier votre e-mail avec ce lien."
128+
},
119129
"feeds": {
120130
"feeds": "Feeds",
121131
"dataType": "Data Format",
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
'use client';
2+
3+
import * as React from 'react';
4+
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
5+
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
6+
import {
7+
Alert,
8+
CircularProgress,
9+
Stack,
10+
Typography,
11+
useTheme,
12+
} from '@mui/material';
13+
import { useTranslations } from 'next-intl';
14+
import { app } from '../../../firebase';
15+
import { ContentBox } from '../../components/ContentBox';
16+
17+
type VerificationStatus = 'loading' | 'success' | 'error';
18+
19+
interface EmailVerificationContentProps {
20+
mode?: string;
21+
oobCode?: string;
22+
}
23+
24+
export default function EmailVerificationContent({
25+
mode,
26+
oobCode,
27+
}: EmailVerificationContentProps): React.ReactElement {
28+
const t = useTranslations('emailVerification');
29+
const theme = useTheme();
30+
const [status, setStatus] = React.useState<VerificationStatus>('loading');
31+
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
32+
33+
React.useEffect(() => {
34+
const verifyEmail = async (): Promise<void> => {
35+
if (mode !== 'verifyEmail' || oobCode == null || oobCode.length === 0) {
36+
setStatus('error');
37+
setErrorMessage(t('invalidLink'));
38+
return;
39+
}
40+
41+
try {
42+
await app.auth().applyActionCode(oobCode);
43+
44+
setStatus('success');
45+
setErrorMessage(null);
46+
} catch {
47+
setStatus('error');
48+
setErrorMessage(t('verificationFailed'));
49+
}
50+
};
51+
52+
void verifyEmail();
53+
}, [mode, oobCode]);
54+
55+
return (
56+
<ContentBox
57+
title=''
58+
sx={{
59+
display: 'flex',
60+
justifyContent: 'center',
61+
backgroundColor: theme.palette.background.paper,
62+
maxWidth: theme.breakpoints.values.sm,
63+
mx: 'auto',
64+
mt: 6,
65+
}}
66+
>
67+
<Stack spacing={3} alignItems='center' textAlign='center'>
68+
{status === 'loading' ? (
69+
<CircularProgress aria-label={t('loadingTitle')} />
70+
) : status === 'success' ? (
71+
<CheckCircleOutlineIcon color='success' sx={{ fontSize: 56 }} />
72+
) : (
73+
<ErrorOutlineIcon color='error' sx={{ fontSize: 56 }} />
74+
)}
75+
76+
<Stack spacing={1.5}>
77+
<Typography variant='h4' component='h1' sx={{ fontWeight: 700 }}>
78+
{status === 'loading'
79+
? t('loadingTitle')
80+
: status === 'success'
81+
? t('successTitle')
82+
: t('errorTitle')}
83+
</Typography>
84+
<Typography variant='body1' color='text.secondary'>
85+
{status === 'loading'
86+
? t('loadingDescription')
87+
: status === 'success'
88+
? t('successDescription')
89+
: t('errorDescription')}
90+
</Typography>
91+
</Stack>
92+
93+
{errorMessage != null && (
94+
<Alert severity='error' variant='outlined' sx={{ width: '100%' }}>
95+
{errorMessage}
96+
</Alert>
97+
)}
98+
</Stack>
99+
</ContentBox>
100+
);
101+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { type ReactElement } from 'react';
2+
import { setRequestLocale } from 'next-intl/server';
3+
import { type Metadata } from 'next';
4+
import { type Locale, routing } from '../../../i18n/routing';
5+
import EmailVerificationContent from './EmailVerificationContent';
6+
7+
export const metadata: Metadata = {
8+
title: 'Email Verification | MobilityDatabase',
9+
description:
10+
'Verify your Mobility Database account email address through Firebase authentication.',
11+
robots: {
12+
index: false,
13+
follow: false,
14+
googleBot: {
15+
index: false,
16+
follow: false,
17+
'max-image-preview': 'none',
18+
'max-snippet': -1,
19+
'max-video-preview': -1,
20+
},
21+
},
22+
};
23+
24+
export function generateStaticParams(): Array<{
25+
locale: Locale;
26+
}> {
27+
return routing.locales.map((locale) => ({ locale }));
28+
}
29+
30+
interface PageProps {
31+
params: Promise<{ locale: string }>;
32+
searchParams: Promise<{
33+
mode?: string;
34+
oobCode?: string;
35+
}>;
36+
}
37+
38+
export default async function EmailVerificationPage({
39+
params,
40+
searchParams,
41+
}: PageProps): Promise<ReactElement> {
42+
const { locale } = await params;
43+
const { mode, oobCode } = await searchParams;
44+
45+
setRequestLocale(locale);
46+
47+
return <EmailVerificationContent mode={mode} oobCode={oobCode} />;
48+
}

src/app/screens/PostRegistration.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ export default function PostRegistration(): React.ReactElement {
9393
</Typography>
9494
<Box sx={{ mt: 2 }}>
9595
An email has been sent or will be sent to you shortly confirming your
96-
account registration.
96+
account registration. Please also check your junk or spam folder if
97+
you do not see it in your inbox.
9798
</Box>
9899
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 2 }}>
99100
<Button

0 commit comments

Comments
 (0)