blob: 0955a4a987e9962fcff253671e8bbe9a8bcab34f [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 */
Ziwei Wang45baf532023-02-16 15:41:49 -050018import { Box, Button, Stack, Typography, useMediaQuery } from '@mui/material';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040019import { Theme, useTheme } from '@mui/material/styles';
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050020import { HttpStatusCode } from 'jami-web-common';
Ziwei Wang45baf532023-02-16 15:41:49 -050021import { ChangeEvent, FormEvent, useEffect, useState } from 'react';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040022import { useTranslation } from 'react-i18next';
Ziwei Wang45baf532023-02-16 15:41:49 -050023import { Form, Link } from 'react-router-dom';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040024
simonab4eec82022-11-08 20:32:43 -050025import { NameStatus, PasswordInput, PasswordStatus, UsernameInput } from '../components/Input';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040026import ProcessingRequest from '../components/ProcessingRequest';
Ziwei Wang3ce1ac02023-02-03 11:59:03 -050027import withAuthUI from '../components/WithAuthUI';
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050028import {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050029 checkPasswordStrength,
Ziwei Wang45baf532023-02-16 15:41:49 -050030 useCheckIfUsernameIsRegisteredQuery,
31 useLoginMutation,
32 useRegisterMutation,
33} from '../services/authQueries';
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040034import { inputWidth, jamiUsernamePattern } from '../utils/constants';
Ziwei Wang3ce1ac02023-02-03 11:59:03 -050035function RegistrationForm() {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040036 const theme: Theme = useTheme();
37 const { t } = useTranslation();
38
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050039 const [username, setUsername] = useState<string>('');
Ziwei Wang45baf532023-02-16 15:41:49 -050040 const [debouncedUsername, setDebouncedUsername] = useState<string>('');
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050041 const [password, setPassword] = useState<string>('');
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040042
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040043 const [usernameStatus, setUsernameStatus] = useState<NameStatus>('default');
44 const [passwordStatus, setPasswordStatus] = useState<PasswordStatus>('default');
45
Ziwei Wang45baf532023-02-16 15:41:49 -050046 const registerMutation = useRegisterMutation();
47 const loginMutation = useLoginMutation();
48
49 const { isLoading: isRegisterLoading } = registerMutation;
50 const { isLoading: isLoginLoading } = loginMutation;
51
52 const isLoading = isRegisterLoading || isLoginLoading;
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040053
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050054 const isMobile: boolean = useMediaQuery(theme.breakpoints.only('xs'));
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040055
Ziwei Wang45baf532023-02-16 15:41:49 -050056 const { data: response, isError } = useCheckIfUsernameIsRegisteredQuery(debouncedUsername);
57
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040058 useEffect(() => {
Ziwei Wang45baf532023-02-16 15:41:49 -050059 if (response !== undefined) {
60 const { responseMessage } = response;
61 setUsernameStatus(responseMessage);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050062 }
Ziwei Wang45baf532023-02-16 15:41:49 -050063 if (isError) {
64 setUsernameStatus('invalid');
65 }
66 }, [response, isError]);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050067
Ziwei Wang45baf532023-02-16 15:41:49 -050068 useEffect(() => {
69 const timeoutId = setTimeout(() => {
70 setDebouncedUsername(username);
71 }, 500);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050072 return () => {
Ziwei Wang45baf532023-02-16 15:41:49 -050073 clearTimeout(timeoutId);
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050074 };
Ziwei Wang45baf532023-02-16 15:41:49 -050075 }, [username]);
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040076
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050077 const createAccount = () => {
Ziwei Wang45baf532023-02-16 15:41:49 -050078 registerMutation.mutate(
79 { username, password },
80 {
81 onSuccess: (response) => {
82 if (response.status === HttpStatusCode.Created) {
83 loginMutation.mutate({ username, password, isJams: false });
84 }
85 },
86 }
87 );
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040088 };
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040089
90 const handleUsername = async (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -040091 const usernameValue: string = event.target.value;
92 setUsername(usernameValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040093
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050094 if (usernameValue === '') {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040095 setUsernameStatus('default');
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050096 return;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -040097 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -050098
99 if (!jamiUsernamePattern.test(usernameValue)) {
100 setUsernameStatus('invalid');
101 return;
102 }
103 //The valid state is when the username passes the Regex test and is ready for availability check.
104 setUsernameStatus('valid');
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400105 };
106
107 const handlePassword = (event: ChangeEvent<HTMLInputElement>) => {
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400108 const passwordValue: string = event.target.value;
109 setPassword(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400110
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400111 if (passwordValue.length > 0) {
112 const checkResult = checkPasswordStrength(passwordValue);
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400113 setPasswordStatus(checkResult.valueCode);
114 } else {
115 setPasswordStatus('default');
116 }
117 };
118
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400119 const handleSubmit = async (event: FormEvent) => {
120 event.preventDefault();
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500121 const usernameOk = usernameStatus === 'success';
122 const passwordOk = passwordStatus === 'strong';
123
124 const canCreate = usernameOk && passwordOk;
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400125
126 if (canCreate) {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500127 createAccount();
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400128 } else {
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500129 if (!usernameOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400130 setUsernameStatus('registration_failed');
131 }
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500132 if (!passwordOk) {
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400133 setPasswordStatus('registration_failed');
134 }
135 }
136 };
137
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400138 return (
139 <>
Ziwei Wang45baf532023-02-16 15:41:49 -0500140 <ProcessingRequest open={isLoading} />
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400141
142 <Stack
143 sx={{
144 minHeight: `${isMobile ? 'auto' : '100%'}`,
145 display: 'flex',
146 alignItems: 'center',
147 justifyContent: 'center',
148 }}
149 >
150 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(20) }}>
151 <Typography component={'span'} variant="h2">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400152 {t('registration_form_title')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400153 </Typography>
154 </Box>
155
156 <Form method="post" id="register-form">
157 <div>
158 <UsernameInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400159 value={username}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400160 onChange={handleUsername}
simonab4eec82022-11-08 20:32:43 -0500161 status={usernameStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400162 tooltipTitle={t('registration_form_username_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500163 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400164 />
165 </div>
166 <div>
167 <PasswordInput
Michelle Sepkap Simee580f422022-10-31 23:27:04 -0400168 value={password}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400169 onChange={handlePassword}
simonab4eec82022-11-08 20:32:43 -0500170 status={passwordStatus}
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400171 tooltipTitle={t('registration_form_password_tooltip')}
Ziwei Wang9b4e2c12023-02-06 14:45:37 -0500172 sx={{ width: theme.typography.pxToRem(inputWidth) }}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400173 />
174 </div>
175
176 <Button
177 variant="contained"
178 type="submit"
179 onClick={handleSubmit}
180 sx={{ width: theme.typography.pxToRem(inputWidth), mt: theme.typography.pxToRem(20) }}
181 >
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400182 {t('registration_form_submit_button')}
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400183 </Button>
184 </Form>
185
186 <Box sx={{ mt: theme.typography.pxToRem(50), mb: theme.typography.pxToRem(50) }}>
187 <Typography variant="body1">
Michelle Sepkap Sime559cc802022-11-05 12:06:40 -0400188 {t('registration_form_to_login_text')} &nbsp;
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500189 <Link to={'/login'}>{t('registration_form_to_login_link')}</Link>
Michelle Sepkap Sime51c00452022-10-31 21:26:38 -0400190 </Typography>
191 </Box>
192 </Stack>
193 </>
194 );
195}
Ziwei Wang3ce1ac02023-02-03 11:59:03 -0500196
197export default withAuthUI(RegistrationForm);