Handle password reset (#349)
* Refactor notifications * Handle password reset
This commit is contained in:
parent
ba3833b7dd
commit
9b8eaa2dac
16
src/App.tsx
16
src/App.tsx
|
@ -7,7 +7,10 @@ import {
|
|||
Redirect,
|
||||
generatePath,
|
||||
} from 'react-router-dom';
|
||||
import { Layout } from 'antd';
|
||||
import {
|
||||
Layout,
|
||||
Result,
|
||||
} from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
ReactNode,
|
||||
|
@ -36,6 +39,7 @@ import useStorage from './hooks/useStorage';
|
|||
import useConfig from './hooks/useConfig';
|
||||
import Login from './components/Auth/Login';
|
||||
import SignUp from './components/Auth/SignUp';
|
||||
import ResetPassword from './components/Auth/ResetPassword';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
@ -139,7 +143,17 @@ const App = ({ ui, config }: { ui: UIState, config: ConfigType }) => {
|
|||
<SignUp />
|
||||
</ContentFor>
|
||||
</Route>
|
||||
<Route path={Routes.RESET_PASSWORD}>
|
||||
<ContentFor>
|
||||
<ResetPassword />
|
||||
</ContentFor>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Result
|
||||
status="warning"
|
||||
title="Page not found"
|
||||
style={{ marginTop: 50 }}
|
||||
/>
|
||||
</Layout>
|
||||
<StatusBar />
|
||||
</>
|
||||
|
|
|
@ -4,7 +4,6 @@ import {
|
|||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import {
|
||||
MailOutlined,
|
||||
|
@ -17,7 +16,12 @@ import {
|
|||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { Routes } from '../../routes';
|
||||
import validateMessages from './validateMessages';
|
||||
import emailNotVerifiedWarning from './emailNotVerifiedWarning';
|
||||
import {
|
||||
emailNotVerified,
|
||||
logInFailed,
|
||||
logInSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -31,34 +35,24 @@ const Login = () => {
|
|||
setIsLoading(true);
|
||||
try {
|
||||
const userCredentials = await login(email, password);
|
||||
notification.success({
|
||||
message: 'Login successful',
|
||||
description: 'Welcome back!',
|
||||
});
|
||||
logInSuccessful();
|
||||
|
||||
if (!userCredentials.user.emailVerified) {
|
||||
emailNotVerifiedWarning();
|
||||
emailNotVerified();
|
||||
}
|
||||
|
||||
history.push(Routes.ROOT);
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
form.resetFields();
|
||||
console.warn(err);
|
||||
notification.error({
|
||||
message: 'Login failed',
|
||||
description: (err as Error).message,
|
||||
});
|
||||
console.warn(error);
|
||||
logInFailed(error as Error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: 20,
|
||||
maxWidth: 350,
|
||||
margin: '0 auto',
|
||||
}}>
|
||||
<Divider>Login</Divider>
|
||||
<div style={containerStyle}>
|
||||
<Divider>Log In</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
onFinish={onFinish}
|
||||
|
@ -93,11 +87,11 @@ const Login = () => {
|
|||
style={{ width: '100%' }}
|
||||
loading={isLoading}
|
||||
>
|
||||
Log in
|
||||
Log In
|
||||
</Button>
|
||||
</Item>
|
||||
<Link to={Routes.SIGN_UP}>Sign Up now!</Link>
|
||||
<Link to="/" style={{ float: 'right' }}>
|
||||
<Link to={Routes.SIGN_UP}>Sign Up now</Link>
|
||||
<Link to={Routes.RESET_PASSWORD} style={{ float: 'right' }}>
|
||||
Forgot password?
|
||||
</Link>
|
||||
</Form>
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { useState } from 'react';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
} from 'antd';
|
||||
import { MailOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Link,
|
||||
useHistory,
|
||||
} from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { Routes } from '../../routes';
|
||||
import validateMessages from './validateMessages';
|
||||
import {
|
||||
resetFailed,
|
||||
resetSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
const ResetPassword = () => {
|
||||
const [form] = Form.useForm();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { resetPassword } = useAuth();
|
||||
const history = useHistory();
|
||||
|
||||
const onFinish = async ({ email, password }: { form: any, email: string, password: string }) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await resetPassword(email);
|
||||
resetSuccessful();
|
||||
history.push(Routes.LOGIN);
|
||||
} catch (error) {
|
||||
form.resetFields();
|
||||
console.warn(error);
|
||||
resetFailed(error as Error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<Divider>Reset password</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
onFinish={onFinish}
|
||||
validateMessages={validateMessages}
|
||||
autoComplete="off"
|
||||
form={form}
|
||||
>
|
||||
<Item
|
||||
name="email"
|
||||
rules={[{ required: true, type: 'email' }]}
|
||||
hasFeedback
|
||||
>
|
||||
<Input
|
||||
prefix={<MailOutlined />}
|
||||
placeholder="Email"
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
style={{ width: '100%' }}
|
||||
loading={isLoading}
|
||||
>
|
||||
Reset password
|
||||
</Button>
|
||||
</Item>
|
||||
<Link to={Routes.SIGN_UP}>Sign Up now</Link>
|
||||
<Link to={Routes.LOGIN} style={{ float: 'right' }}>
|
||||
Log In
|
||||
</Link>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetPassword;
|
|
@ -4,7 +4,6 @@ import {
|
|||
Input,
|
||||
Button,
|
||||
Divider,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import {
|
||||
MailOutlined,
|
||||
|
@ -17,7 +16,12 @@ import {
|
|||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { Routes } from '../../routes';
|
||||
import validateMessages from './validateMessages';
|
||||
import emailNotVerifiedWarning from './emailNotVerifiedWarning';
|
||||
import {
|
||||
emailNotVerified,
|
||||
signUpFailed,
|
||||
signUpSuccessful,
|
||||
} from './notifications';
|
||||
import { containerStyle } from './common';
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
|
@ -33,30 +37,19 @@ const SignUp = () => {
|
|||
setIsLoading(true);
|
||||
try {
|
||||
await signUp(email, password);
|
||||
notification.success({
|
||||
message: 'Sign Up successful',
|
||||
description: 'Welcome on board!',
|
||||
});
|
||||
emailNotVerifiedWarning();
|
||||
|
||||
signUpSuccessful();
|
||||
emailNotVerified();
|
||||
history.push(Routes.ROOT);
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
form.resetFields();
|
||||
console.warn(err);
|
||||
notification.error({
|
||||
message: 'Failed to create an account',
|
||||
description: (err as Error).message,
|
||||
});
|
||||
console.warn(error);
|
||||
signUpFailed(error as Error);
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: 20,
|
||||
maxWidth: 350,
|
||||
margin: '0 auto',
|
||||
}}>
|
||||
<div style={containerStyle}>
|
||||
<Divider>Sign Up</Divider>
|
||||
<Form
|
||||
initialValues={{ remember: true }}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
const containerStyle = {
|
||||
padding: 20,
|
||||
maxWidth: 370,
|
||||
margin: '0 auto',
|
||||
};
|
||||
|
||||
export {
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
containerStyle,
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
import { notification } from 'antd';
|
||||
|
||||
const emailNotVerifiedWarning = () => notification.warn({
|
||||
message: 'Check your email',
|
||||
description: 'Your email address has to be verified before you can upload files!',
|
||||
});
|
||||
|
||||
export default emailNotVerifiedWarning;
|
|
@ -0,0 +1,76 @@
|
|||
import { notification } from 'antd';
|
||||
|
||||
const duration = 6;
|
||||
|
||||
const baseOptions = {
|
||||
duration,
|
||||
};
|
||||
|
||||
const emailNotVerified = () => notification.success({
|
||||
message: 'Check your email',
|
||||
description: 'Your email address has to be verified before you can upload files!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const signUpSuccessful = () => notification.success({
|
||||
message: 'Sign Up successful',
|
||||
description: 'Welcome on board!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const signUpFailed = (err: Error) => notification.error({
|
||||
message: 'Failed to create an account',
|
||||
description: err.message,
|
||||
});
|
||||
|
||||
const logInSuccessful = () => notification.success({
|
||||
message: 'Log in successful',
|
||||
description: 'Welcome back!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const logInFailed = (err: Error) => notification.error({
|
||||
message: 'Failed to log in',
|
||||
description: err.message,
|
||||
});
|
||||
|
||||
const restrictedPage = () => notification.error({
|
||||
message: 'Restricted page',
|
||||
description: 'You have to be logged in to access this page!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const logOutSuccessful = () => notification.warning({
|
||||
message: 'Log out successful',
|
||||
description: 'See you next time!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const logOutFailed = (err: Error) => notification.error({
|
||||
message: 'Log out failed',
|
||||
description: err.message,
|
||||
});
|
||||
|
||||
const resetSuccessful = () => notification.success({
|
||||
message: 'Password reset successful',
|
||||
description: 'Check your email!',
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const resetFailed = (err: Error) => notification.error({
|
||||
message: 'Password reset failed',
|
||||
description: err.message,
|
||||
});
|
||||
|
||||
export {
|
||||
emailNotVerified,
|
||||
signUpSuccessful,
|
||||
signUpFailed,
|
||||
logInSuccessful,
|
||||
logInFailed,
|
||||
restrictedPage,
|
||||
logOutSuccessful,
|
||||
logOutFailed,
|
||||
resetSuccessful,
|
||||
resetFailed,
|
||||
};
|
|
@ -17,7 +17,6 @@ import {
|
|||
Dropdown,
|
||||
Typography,
|
||||
Radio,
|
||||
notification,
|
||||
} from 'antd';
|
||||
import {
|
||||
UserOutlined,
|
||||
|
@ -57,6 +56,10 @@ import {
|
|||
} from '../utils/keyboard/shortcuts';
|
||||
import { Routes } from '../routes';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import {
|
||||
logOutFailed,
|
||||
logOutSuccessful,
|
||||
} from './Auth/notifications';
|
||||
|
||||
const { Header } = Layout;
|
||||
const { useBreakpoint } = Grid;
|
||||
|
@ -71,16 +74,10 @@ const TopBar = () => {
|
|||
const logoutClick = useCallback(async () => {
|
||||
try {
|
||||
await logout();
|
||||
notification.warning({
|
||||
message: 'Logout successful',
|
||||
description: 'See you again!',
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
notification.error({
|
||||
message: 'Login failed',
|
||||
description: (err as Error).message,
|
||||
});
|
||||
logOutSuccessful();
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
logOutFailed(error as Error);
|
||||
}
|
||||
}, [logout]);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
signInWithEmailAndPassword,
|
||||
sendEmailVerification,
|
||||
signOut,
|
||||
sendPasswordResetEmail,
|
||||
} from '../firebase';
|
||||
|
||||
const AuthContext = createContext<any>(null);
|
||||
|
@ -22,6 +23,7 @@ interface AuthValue {
|
|||
signUp: (email: string, password: string) => Promise<UserCredential>,
|
||||
login: (email: string, password: string) => Promise<UserCredential>,
|
||||
logout: () => Promise<void>,
|
||||
resetPassword: (email: string) => Promise<UserCredential>,
|
||||
}
|
||||
|
||||
const useAuth = () => useContext<AuthValue>(AuthContext);
|
||||
|
@ -37,6 +39,7 @@ const AuthProvider = (props: { children: ReactNode }) => {
|
|||
.then((userCredential) => sendEmailVerification(userCredential.user)),
|
||||
login: (email: string, password: string) => signInWithEmailAndPassword(auth, email, password),
|
||||
logout: () => signOut(auth),
|
||||
resetPassword: (email: string) => sendPasswordResetEmail(auth, email),
|
||||
}), [currentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
signInWithEmailAndPassword,
|
||||
signOut,
|
||||
sendEmailVerification,
|
||||
sendPasswordResetEmail,
|
||||
} from 'firebase/auth';
|
||||
import { getAnalytics } from 'firebase/analytics';
|
||||
|
||||
|
@ -29,4 +30,5 @@ export {
|
|||
signInWithEmailAndPassword,
|
||||
sendEmailVerification,
|
||||
signOut,
|
||||
sendPasswordResetEmail,
|
||||
};
|
||||
|
|
|
@ -6,8 +6,9 @@ export enum Routes {
|
|||
DIALOG = '/tune/:category/:dialog',
|
||||
LOG = '/log',
|
||||
DIAGNOSE = '/diagnose',
|
||||
LOGIN = '/login',
|
||||
LOGOUT = '/logout',
|
||||
SIGN_UP = '/sign-up',
|
||||
FORGOT_PASSWORD = '/forgot-password',
|
||||
LOGIN = '/auth/login',
|
||||
LOGOUT = '/auth/logout',
|
||||
SIGN_UP = '/auth/sign-up',
|
||||
FORGOT_PASSWORD = '/auth/forgot-password',
|
||||
RESET_PASSWORD = '/auth/reset-password',
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue