blob: 1b63d3329658220a76020fb4d4439ae4dde18bf1 [file] [log] [blame]
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -04001/*
2 * Copyright (C) 2022 Savoir-faire Linux Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public
15 * License along with this program. If not, see
16 * <https://www.gnu.org/licenses/>.
17 */
Gabriel Rochon7057b4f2022-11-21 13:28:01 -050018import {
19 Box,
20 Button,
21 FormControl,
22 FormControlLabel,
23 Radio,
24 RadioGroup,
25 Stack,
26 Typography,
27 useMediaQuery,
28} from '@mui/material';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040029import { Theme, useTheme } from '@mui/material/styles';
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050030import { HttpStatusCode } from 'jami-web-common';
Ziwei Wang49765ec2023-02-13 16:35:32 -050031import { ChangeEvent, FormEvent, useContext, useEffect, useState } from 'react';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040032import { useTranslation } from 'react-i18next';
Ziwei Wang3ce1ac02023-02-03 11:59:03 -050033import { Form, Link, useNavigate } from 'react-router-dom';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040034
simonab4eec82022-11-08 20:32:43 -050035import { NameStatus, PasswordInput, PasswordStatus, UsernameInput } from '../components/Input';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040036import ProcessingRequest from '../components/ProcessingRequest';
Ziwei Wang3ce1ac02023-02-03 11:59:03 -050037import withAuthUI from '../components/WithAuthUI';
Ziwei Wang49765ec2023-02-13 16:35:32 -050038import { AlertSnackbarContext } from '../contexts/AlertSnackbarProvider';
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050039import {
40 checkIfUserameIsRegistered,
41 checkPasswordStrength,
42 loginUser,
43 registerUser,
44 setAccessToken,
45} from '../utils/auth';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040046import { inputWidth, jamiUsernamePattern } from '../utils/constants';
Ziwei Wang3ce1ac02023-02-03 11:59:03 -050047function RegistrationForm() {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040048 const theme: Theme = useTheme();
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040049 const navigate = useNavigate();
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040050 const { t } = useTranslation();
51
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050052 const [loading, setLoading] = useState(false);
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040053
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050054 const [username, setUsername] = useState<string>('');
55 const [password, setPassword] = useState<string>('');
56 const [isJams, setIsJams] = useState<boolean>(false);
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040057
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040058 const [usernameStatus, setUsernameStatus] = useState<NameStatus>('default');
59 const [passwordStatus, setPasswordStatus] = useState<PasswordStatus>('default');
60
Ziwei Wang49765ec2023-02-13 16:35:32 -050061 const { setAlertContent } = useContext(AlertSnackbarContext);
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040062
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050063 const isMobile: boolean = useMediaQuery(theme.breakpoints.only('xs'));
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040064
65 useEffect(() => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050066 let timetoutId: ReturnType<typeof setTimeout>;
67 if (usernameStatus === 'valid') {
68 timetoutId = setTimeout(() => {
69 // useCheckIfUsernameIsRegisteredMutation.mutate(username);
70 checkIfUserameIsRegistered(username)
71 .then((response) => {
Ziwei Wang3c941192023-02-15 12:26:43 -050072 const { status, data } = response;
73 if (status === HttpStatusCode.Ok) {
74 if (data === 'taken') {
75 setUsernameStatus('taken');
76 } else {
77 setUsernameStatus('success');
78 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050079 }
80 })
81 .catch((e) => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050082 const { status } = e.response;
83 if (status === HttpStatusCode.BadRequest) {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050084 setUsernameStatus('invalid');
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050085 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -050086 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050087 }
88 });
89 }, 1000);
90 }
91
92 return () => {
93 clearTimeout(timetoutId);
94 };
Ziwei Wang49765ec2023-02-13 16:35:32 -050095 }, [t, username, usernameStatus, setAlertContent]);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050096
Ziwei Wang49765ec2023-02-13 16:35:32 -050097 const login = () => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050098 setLoading(true);
Ziwei Wang49765ec2023-02-13 16:35:32 -050099
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500100 loginUser(username, password, isJams)
101 .then((response) => {
102 if (response.status === HttpStatusCode.Ok) {
103 setAccessToken(response.data.accessToken);
104 navigate('/conversation', { replace: true });
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400105 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500106 })
107 .catch((e) => {
108 console.log(e);
109 const { status } = e.response;
110 if (status === HttpStatusCode.BadRequest) {
111 //TODO: the only bad request response defined in the server is missing credentials. add the response message to the locale.
112 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500113 // setAlertContent(t('unknown_error_alert'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500114 } else if (status === HttpStatusCode.NotFound) {
115 //TODO: there are two different not found responses that could be returned by the server, use message to differentiate them?
116 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500117 // setAlertContent(t('login_username_not_found'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500118 } else if (status === HttpStatusCode.Unauthorized) {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500119 setAlertContent({ messageI18nKey: 'login_invalid_password', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500120 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500121 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500122 }
123 })
124 .finally(() => {
125 setLoading(false);
126 });
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400127 };
128
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500129 const createAccount = () => {
130 setLoading(true);
131 registerUser(username, password, isJams)
132 .then((response) => {
133 if (response.status === HttpStatusCode.Created) {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500134 setAlertContent({ messageI18nKey: 'registration_success', severity: 'success', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500135 login();
136 }
137 })
138 .catch((e) => {
139 console.log(e);
140 const { status } = e.response;
141 if (status === HttpStatusCode.BadRequest) {
142 //TODO: more than one bad request response defined in the server is missing credentials. add the response message to the locale.
143 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500144 // setAlertContent(t('unknown_error_alert'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500145 } else if (status === HttpStatusCode.Conflict) {
146 //TODO: there are two different conflict responses that could be returned by the server, use message to differentiate them?
147 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500148 // setAlertContent(t('login_username_not_found'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500149 } else if (status === HttpStatusCode.Unauthorized) {
150 //TODO: this is a response for JAMS, add message to the locale
151 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500152 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500153 }
154 })
155 .finally(() => {
156 setLoading(false);
157 });
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400158 };
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400159
160 const handleUsername = async (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400161 const usernameValue: string = event.target.value;
162 setUsername(usernameValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400163
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500164 if (usernameValue === '') {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400165 setUsernameStatus('default');
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500166 return;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400167 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500168
169 if (!jamiUsernamePattern.test(usernameValue)) {
170 setUsernameStatus('invalid');
171 return;
172 }
173 //The valid state is when the username passes the Regex test and is ready for availability check.
174 setUsernameStatus('valid');
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400175 };
176
177 const handlePassword = (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400178 const passwordValue: string = event.target.value;
179 setPassword(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400180
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400181 if (passwordValue.length > 0) {
182 const checkResult = checkPasswordStrength(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400183 setPasswordStatus(checkResult.valueCode);
184 } else {
185 setPasswordStatus('default');
186 }
187 };
188
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500189 const handleIsJams = (event: ChangeEvent<HTMLInputElement>) => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500190 setIsJams(event.target.value === 'jams');
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500191 };
192
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400193 const handleSubmit = async (event: FormEvent) => {
194 event.preventDefault();
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500195 const usernameOk = usernameStatus === 'success';
196 const passwordOk = passwordStatus === 'strong';
197
198 const canCreate = usernameOk && passwordOk;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400199
200 if (canCreate) {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500201 createAccount();
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400202 } else {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500203 if (!usernameOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400204 setUsernameStatus('registration_failed');
205 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500206 if (!passwordOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400207 setPasswordStatus('registration_failed');
208 }
209 }
210 };
211
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400212 return (
213 <>
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500214 <ProcessingRequest open={loading} />
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400215
216 <Stack
217 sx={{
218 minHeight: `${isMobile ? 'auto' : '100%'}`,
219 display: 'flex',
220 alignItems: 'center',
221 justifyContent: 'center',
222 }}
223 >
224 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(20) }}>
225 <Typography component={'span'} variant="h2">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400226 {t('registration_form_title')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400227 </Typography>
228 </Box>
229
230 <Form method="post" id="register-form">
231 <div>
232 <UsernameInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400233 value={username}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400234 onChange={handleUsername}
simonab4eec82022-11-08 20:32:43 -0500235 status={usernameStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400236 tooltipTitle={t('registration_form_username_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500237 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400238 />
239 </div>
240 <div>
241 <PasswordInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400242 value={password}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400243 onChange={handlePassword}
simonab4eec82022-11-08 20:32:43 -0500244 status={passwordStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400245 tooltipTitle={t('registration_form_password_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500246 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400247 />
248 </div>
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500249 <div>
250 <FormControl
251 sx={{
252 width: theme.typography.pxToRem(inputWidth),
253 alignItems: 'center',
254 justifyContent: 'space-between',
255 }}
256 >
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500257 <RadioGroup row onChange={handleIsJams} defaultValue="jami">
258 <FormControlLabel value="jami" control={<Radio />} label={t('jami')} />
259 <FormControlLabel value="jams" control={<Radio />} label={t('jams')} />
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500260 </RadioGroup>
261 </FormControl>
262 </div>
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400263
264 <Button
265 variant="contained"
266 type="submit"
267 onClick={handleSubmit}
268 sx={{ width: theme.typography.pxToRem(inputWidth), mt: theme.typography.pxToRem(20) }}
269 >
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400270 {t('registration_form_submit_button')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400271 </Button>
272 </Form>
273
274 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(50) }}>
275 <Typography variant="body1">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400276 {t('registration_form_to_login_text')} &nbsp;
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500277 <Link to={'/login'}>{t('registration_form_to_login_link')}</Link>
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400278 </Typography>
279 </Box>
280 </Stack>
281 </>
282 );
283}
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500284
285export default withAuthUI(RegistrationForm);