Merge branch 'main' into feature/adc/retour-utilisateur

This commit is contained in:
danis
2023-09-22 15:11:33 +02:00
21 changed files with 3764 additions and 3685 deletions
+2 -1
View File
@@ -23,6 +23,7 @@ import { AccessTokenResponseHandler } from './models/AccessTokenResponse';
import * as yup from 'yup';
import { base64ToBlob } from './utils/base64ToBlob';
import { ImagePickerAsset } from 'expo-image-picker';
import Constant from 'expo-constants';
type AuthenticationInput = { username: string; password: string };
type RegistrationInput = AuthenticationInput & { email: string };
@@ -68,7 +69,7 @@ export default class API {
public static readonly baseUrl =
process.env.NODE_ENV != 'development' && Platform.OS === 'web'
? '/api'
: 'https://nightly.chroma.octohub.app/api';
: Constant.manifest?.extra?.apiUrl;
public static async fetch(
params: FetchParams,
handle: Pick<Required<HandleParams>, 'raw'>
+12
View File
@@ -33,6 +33,8 @@ import VerifiedView from './views/VerifiedView';
import SigninView from './views/SigninView';
import SignupView from './views/SignupView';
import TabNavigation from './components/V2/TabNavigation';
import PasswordResetView from './views/PasswordResetView';
import ForgotPasswordView from './views/ForgotPasswordView';
// Util function to hide route props in URL
const removeMe = () => '';
@@ -123,6 +125,16 @@ const publicRoutes = () =>
options: { title: 'Google signin', headerShown: false },
link: '/logged/google',
},
PasswordReset: {
component: PasswordResetView,
options: { title: 'Password reset form', headerShown: false },
link: '/password_reset',
},
ForgotPassword: {
component: ForgotPasswordView,
options: { title: 'Password reset form', headerShown: false },
link: '/forgot_password',
},
} as const);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -0,0 +1,73 @@
import React from 'react';
import { translate } from '../../i18n/i18n';
import { string } from 'yup';
import { FormControl, Input, Stack, WarningOutlineIcon, Box, Button, useToast } from 'native-base';
interface ForgotPasswordFormProps {
onSubmit: (email: string) => Promise<string>;
}
const validationSchemas = {
email: string().email('Invalid email').required('Email is required'),
};
const ForgotPasswordForm = ({ onSubmit }: ForgotPasswordFormProps) => {
const [formData, setFormData] = React.useState({
newEmail: {
value: '',
error: null as string | null,
},
});
const [submittingForm, setSubmittingForm] = React.useState(false);
const toast = useToast();
return (
<Box>
<Stack mx="4" style={{ width: '80%', maxWidth: 400 }}>
<FormControl isRequired isInvalid={formData.newEmail.error !== null}>
<FormControl.Label>{translate('newEmail')}</FormControl.Label>
<Input
isRequired
type="text"
placeholder={translate('newEmail')}
value={formData.newEmail.value}
onChangeText={(t) => {
let error: null | string = null;
validationSchemas.email
.validate(t)
.catch((e) => (error = e.message))
.finally(() => {
setFormData({ ...formData, newEmail: { value: t, error } });
});
}}
/>
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
{formData.newEmail.error}
</FormControl.ErrorMessage>
<Button
style={{ marginTop: 10 }}
isLoading={submittingForm}
isDisabled={formData.newEmail.error !== null}
onPress={async () => {
setSubmittingForm(true);
try {
const resp = await onSubmit(formData.newEmail.value);
toast.show({ description: resp });
} catch (e) {
toast.show({ description: e as string });
} finally {
setSubmittingForm(false);
}
}}
>
{translate('submitBtn')}
</Button>
</FormControl>
</Stack>
</Box>
);
};
export default ForgotPasswordForm;
@@ -0,0 +1,114 @@
import React from 'react';
import { translate } from '../../i18n/i18n';
import { string } from 'yup';
import { FormControl, Input, Stack, WarningOutlineIcon, Box, Button, useToast } from 'native-base';
interface PasswordResetFormProps {
onSubmit: (newPassword: string) => Promise<string>;
}
const PasswordResetForm = ({ onSubmit }: PasswordResetFormProps) => {
const [formData, setFormData] = React.useState({
newPassword: {
value: '',
error: null as string | null,
},
confirmNewPassword: {
value: '',
error: null as string | null,
},
});
const [submittingForm, setSubmittingForm] = React.useState(false);
const validationSchemas = {
password: string()
.min(4, translate('passwordTooShort'))
.max(100, translate('passwordTooLong'))
.required('Password is required'),
};
const toast = useToast();
return (
<Box>
<Stack mx="4" style={{ width: '80%', maxWidth: 400 }}>
<FormControl
isRequired
isInvalid={
formData.newPassword.error !== null ||
formData.confirmNewPassword.error !== null
}
>
<FormControl.Label>{translate('newPassword')}</FormControl.Label>
<Input
isRequired
type="password"
placeholder={translate('newPassword')}
value={formData.newPassword.value}
onChangeText={(t) => {
let error: null | string = null;
validationSchemas.password
.validate(t)
.catch((e) => (error = e.message))
.finally(() => {
setFormData({ ...formData, newPassword: { value: t, error } });
});
}}
/>
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
{formData.newPassword.error}
</FormControl.ErrorMessage>
<FormControl.Label>{translate('confirmNewPassword')}</FormControl.Label>
<Input
isRequired
type="password"
placeholder={translate('confirmNewPassword')}
value={formData.confirmNewPassword.value}
onChangeText={(t) => {
let error: null | string = null;
validationSchemas.password
.validate(t)
.catch((e) => (error = e.message));
if (!error && t !== formData.newPassword.value) {
error = translate('passwordsDontMatch');
}
setFormData({
...formData,
confirmNewPassword: { value: t, error },
});
}}
/>
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
{formData.confirmNewPassword.error}
</FormControl.ErrorMessage>
<Button
style={{ marginTop: 10 }}
isLoading={submittingForm}
isDisabled={
formData.newPassword.error !== null ||
formData.confirmNewPassword.error !== null ||
formData.newPassword.value === '' ||
formData.confirmNewPassword.value === ''
}
onPress={async () => {
setSubmittingForm(true);
try {
const resp = await onSubmit(formData.newPassword.value);
toast.show({ description: resp });
} catch (e) {
toast.show({ description: e as string });
} finally {
setSubmittingForm(false);
}
}}
>
{translate('submitBtn')}
</Button>
</FormControl>
</Stack>
</Box>
);
};
export default PasswordResetForm;
+3 -3
View File
@@ -54,7 +54,7 @@
"native-base": "^3.4.17",
"opensheetmusicdisplay": "^1.7.5",
"phaser": "^3.60.0",
"react": "18.1.0",
"react": "18.2.0",
"react-dom": "18.1.0",
"react-i18next": "^11.18.3",
"react-native": "0.70.5",
@@ -91,8 +91,8 @@
"@storybook/testing-library": "^0.0.13",
"@testing-library/react-native": "^11.0.0",
"@types/node": "^18.11.8",
"@types/react": "~18.0.24",
"@types/react-native": "~0.70.6",
"@types/react": "~18.2.0",
"@types/react-native": "~0.70.5",
"@types/react-navigation": "^3.4.0",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.0.0",
+28
View File
@@ -0,0 +1,28 @@
import API from '../API';
import { useNavigation } from '../Navigation';
import ForgotPasswordForm from '../components/forms/forgotPasswordForm';
const ForgotPasswordView = () => {
const navigation = useNavigation();
async function handleSubmit(email: string) {
try {
await API.fetch({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
route: `/auth/forgot-password?email=${email}`,
method: 'PUT',
});
navigation.navigate('Home');
return 'email sent';
} catch {
return 'Error with email, please contact support';
}
}
return (
<div>
<ForgotPasswordForm onSubmit={handleSubmit} />
</div>
);
};
export default ForgotPasswordView;
+34
View File
@@ -0,0 +1,34 @@
import API from '../API';
import { useNavigation } from '../Navigation';
import { useRoute } from '@react-navigation/native';
import PasswordResetForm from '../components/forms/passwordResetForm';
const PasswordResetView = () => {
const navigation = useNavigation();
const route = useRoute();
const handlePasswordReset = async (password: string) => {
try {
await API.fetch({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
route: `/auth/password-reset?token=${(route.params as any).token}`,
method: 'PUT',
body: {
password,
},
});
navigation.navigate('Home');
return 'password succesfully reset';
} catch {
return 'password reset failed';
}
};
return (
<div>
<PasswordResetForm onSubmit={(password) => handlePasswordReset(password)} />
</div>
);
};
export default PasswordResetView;
+1 -1
View File
@@ -100,7 +100,7 @@ const SigninView = () => {
}}
isRequired
/>,
<LinkBase key={'signin-link'} onPress={() => console.log('Link clicked!')}>
<LinkBase key={'signin-link'} onPress={() => navigation.navigate('ForgotPassword')}>
{translate('forgottenPassword')}
</LinkBase>,
]}
+2 -2
View File
@@ -178,8 +178,8 @@ const SignupView = () => {
try {
const resp = await onSubmit(
formData.username.value,
formData.password.value,
formData.email.value
formData.email.value,
formData.password.value
);
toast.show({ description: resp, colorScheme: 'secondary' });
} catch (e) {
+17
View File
@@ -178,6 +178,23 @@ const StartPageView = () => {
</Link>
</Box>
</Box>
<Box
style={{
width: '90%',
marginTop: 20,
}}
>
<Box
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
}}
>
<Link href="/forgot_password">I forgot my password</Link>
</Box>
</Box>
</Column>
</View>
);
+3384 -3662
View File
File diff suppressed because it is too large Load Diff