blob: e857f7b7772611a67411c6cabc1be75a47c29904 [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) => {
72 //server responds 200 if name IS found
73 if (response.status === HttpStatusCode.Ok) {
74 setUsernameStatus('taken');
75 }
76 })
77 .catch((e) => {
78 // console.log(e.response.data);
79 const { status } = e.response;
80 if (status === HttpStatusCode.BadRequest) {
81 //TODO: server sends invalid username as the message, add it to the locale
82 // console.log(e.response.data);
83 setUsernameStatus('invalid');
84 } else if (status === HttpStatusCode.NotFound) {
85 //TODO: server didn't find this username, therefore it can be registered, add the message to the locale
86 // console.log(e.response.data);
87 setUsernameStatus('success');
88 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -050089 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050090 }
91 });
92 }, 1000);
93 }
94
95 return () => {
96 clearTimeout(timetoutId);
97 };
Ziwei Wang49765ec2023-02-13 16:35:32 -050098 }, [t, username, usernameStatus, setAlertContent]);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050099
Ziwei Wang49765ec2023-02-13 16:35:32 -0500100 const login = () => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500101 setLoading(true);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500102
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500103 loginUser(username, password, isJams)
104 .then((response) => {
105 if (response.status === HttpStatusCode.Ok) {
106 setAccessToken(response.data.accessToken);
107 navigate('/conversation', { replace: true });
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400108 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500109 })
110 .catch((e) => {
111 console.log(e);
112 const { status } = e.response;
113 if (status === HttpStatusCode.BadRequest) {
114 //TODO: the only bad request response defined in the server is missing credentials. add the response message to the locale.
115 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500116 // setAlertContent(t('unknown_error_alert'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500117 } else if (status === HttpStatusCode.NotFound) {
118 //TODO: there are two different not found responses that could be returned by the server, use message to differentiate them?
119 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500120 // setAlertContent(t('login_username_not_found'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500121 } else if (status === HttpStatusCode.Unauthorized) {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500122 setAlertContent({ messageI18nKey: 'login_invalid_password', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500123 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500124 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500125 }
126 })
127 .finally(() => {
128 setLoading(false);
129 });
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400130 };
131
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500132 const createAccount = () => {
133 setLoading(true);
134 registerUser(username, password, isJams)
135 .then((response) => {
136 if (response.status === HttpStatusCode.Created) {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500137 setAlertContent({ messageI18nKey: 'registration_success', severity: 'success', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500138 login();
139 }
140 })
141 .catch((e) => {
142 console.log(e);
143 const { status } = e.response;
144 if (status === HttpStatusCode.BadRequest) {
145 //TODO: more than one bad request response defined in the server is missing credentials. add the response message to the locale.
146 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500147 // setAlertContent(t('unknown_error_alert'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500148 } else if (status === HttpStatusCode.Conflict) {
149 //TODO: there are two different conflict responses that could be returned by the server, use message to differentiate them?
150 console.log(e.response.data);
Ziwei Wang49765ec2023-02-13 16:35:32 -0500151 // setAlertContent(t('login_username_not_found'));
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500152 } else if (status === HttpStatusCode.Unauthorized) {
153 //TODO: this is a response for JAMS, add message to the locale
154 } else {
Ziwei Wang49765ec2023-02-13 16:35:32 -0500155 setAlertContent({ messageI18nKey: 'unknown_error_alert', severity: 'error', alertOpen: true });
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500156 }
157 })
158 .finally(() => {
159 setLoading(false);
160 });
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400161 };
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400162
163 const handleUsername = async (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400164 const usernameValue: string = event.target.value;
165 setUsername(usernameValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400166
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500167 if (usernameValue === '') {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400168 setUsernameStatus('default');
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500169 return;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400170 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500171
172 if (!jamiUsernamePattern.test(usernameValue)) {
173 setUsernameStatus('invalid');
174 return;
175 }
176 //The valid state is when the username passes the Regex test and is ready for availability check.
177 setUsernameStatus('valid');
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400178 };
179
180 const handlePassword = (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400181 const passwordValue: string = event.target.value;
182 setPassword(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400183
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400184 if (passwordValue.length > 0) {
185 const checkResult = checkPasswordStrength(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400186 setPasswordStatus(checkResult.valueCode);
187 } else {
188 setPasswordStatus('default');
189 }
190 };
191
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500192 const handleIsJams = (event: ChangeEvent<HTMLInputElement>) => {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500193 setIsJams(event.target.value === 'jams');
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500194 };
195
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400196 const handleSubmit = async (event: FormEvent) => {
197 event.preventDefault();
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500198 const usernameOk = usernameStatus === 'success';
199 const passwordOk = passwordStatus === 'strong';
200
201 const canCreate = usernameOk && passwordOk;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400202
203 if (canCreate) {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500204 createAccount();
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400205 } else {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500206 if (!usernameOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400207 setUsernameStatus('registration_failed');
208 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500209 if (!passwordOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400210 setPasswordStatus('registration_failed');
211 }
212 }
213 };
214
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400215 return (
216 <>
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500217 <ProcessingRequest open={loading} />
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400218
219 <Stack
220 sx={{
221 minHeight: `${isMobile ? 'auto' : '100%'}`,
222 display: 'flex',
223 alignItems: 'center',
224 justifyContent: 'center',
225 }}
226 >
227 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(20) }}>
228 <Typography component={'span'} variant="h2">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400229 {t('registration_form_title')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400230 </Typography>
231 </Box>
232
233 <Form method="post" id="register-form">
234 <div>
235 <UsernameInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400236 value={username}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400237 onChange={handleUsername}
simonab4eec82022-11-08 20:32:43 -0500238 status={usernameStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400239 tooltipTitle={t('registration_form_username_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500240 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400241 />
242 </div>
243 <div>
244 <PasswordInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400245 value={password}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400246 onChange={handlePassword}
simonab4eec82022-11-08 20:32:43 -0500247 status={passwordStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400248 tooltipTitle={t('registration_form_password_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500249 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400250 />
251 </div>
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500252 <div>
253 <FormControl
254 sx={{
255 width: theme.typography.pxToRem(inputWidth),
256 alignItems: 'center',
257 justifyContent: 'space-between',
258 }}
259 >
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500260 <RadioGroup row onChange={handleIsJams} defaultValue="jami">
261 <FormControlLabel value="jami" control={<Radio />} label={t('jami')} />
262 <FormControlLabel value="jams" control={<Radio />} label={t('jams')} />
Gabriel Rochon7057b4f2022-11-21 13:28:01 -0500263 </RadioGroup>
264 </FormControl>
265 </div>
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400266
267 <Button
268 variant="contained"
269 type="submit"
270 onClick={handleSubmit}
271 sx={{ width: theme.typography.pxToRem(inputWidth), mt: theme.typography.pxToRem(20) }}
272 >
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400273 {t('registration_form_submit_button')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400274 </Button>
275 </Form>
276
277 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(50) }}>
278 <Typography variant="body1">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400279 {t('registration_form_to_login_text')} &nbsp;
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500280 <Link to={'/login'}>{t('registration_form_to_login_link')}</Link>
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400281 </Typography>
282 </Box>
283 </Stack>
284 </>
285 );
286}
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500287
288export default withAuthUI(RegistrationForm);