From 9b8eaa2daca7ddecec6a48af861a96673f510a83 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Sun, 26 Dec 2021 16:20:02 +0100 Subject: [PATCH] Handle password reset (#349) * Refactor notifications * Handle password reset --- src/App.tsx | 16 +++- src/components/Auth/Login.tsx | 38 ++++----- src/components/Auth/ResetPassword.tsx | 83 +++++++++++++++++++ src/components/Auth/SignUp.tsx | 31 +++---- src/components/Auth/common.ts | 10 +++ .../Auth/emailNotVerifiedWarning.ts | 8 -- src/components/Auth/notifications.ts | 76 +++++++++++++++++ src/components/TopBar.tsx | 19 ++--- src/contexts/AuthContext.tsx | 3 + src/firebase.ts | 2 + src/routes.ts | 9 +- 11 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 src/components/Auth/ResetPassword.tsx create mode 100644 src/components/Auth/common.ts delete mode 100644 src/components/Auth/emailNotVerifiedWarning.ts create mode 100644 src/components/Auth/notifications.ts diff --git a/src/App.tsx b/src/App.tsx index 5ea20e5..4c0626a 100644 --- a/src/App.tsx +++ b/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 }) => { + + + + + + diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx index a3d411b..80ba038 100644 --- a/src/components/Auth/Login.tsx +++ b/src/components/Auth/Login.tsx @@ -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 ( -
- Login +
+ Log In
{ style={{ width: '100%' }} loading={isLoading} > - Log in + Log In - Sign Up now! - + Sign Up now + Forgot password?
diff --git a/src/components/Auth/ResetPassword.tsx b/src/components/Auth/ResetPassword.tsx new file mode 100644 index 0000000..29495ea --- /dev/null +++ b/src/components/Auth/ResetPassword.tsx @@ -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 ( +
+ Reset password +
+ + } + placeholder="Email" + /> + + + + + Sign Up now + + Log In + +
+
+ ); +}; + +export default ResetPassword; diff --git a/src/components/Auth/SignUp.tsx b/src/components/Auth/SignUp.tsx index dc96acd..4f2bd63 100644 --- a/src/components/Auth/SignUp.tsx +++ b/src/components/Auth/SignUp.tsx @@ -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 ( -
+
Sign Up
notification.warn({ - message: 'Check your email', - description: 'Your email address has to be verified before you can upload files!', -}); - -export default emailNotVerifiedWarning; diff --git a/src/components/Auth/notifications.ts b/src/components/Auth/notifications.ts new file mode 100644 index 0000000..0cd0c3e --- /dev/null +++ b/src/components/Auth/notifications.ts @@ -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, +}; diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index c2d22aa..4a67a44 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -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]); diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 7816f68..ae4fb63 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -13,6 +13,7 @@ import { signInWithEmailAndPassword, sendEmailVerification, signOut, + sendPasswordResetEmail, } from '../firebase'; const AuthContext = createContext(null); @@ -22,6 +23,7 @@ interface AuthValue { signUp: (email: string, password: string) => Promise, login: (email: string, password: string) => Promise, logout: () => Promise, + resetPassword: (email: string) => Promise, } const useAuth = () => useContext(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(() => { diff --git a/src/firebase.ts b/src/firebase.ts index f6aa3f2..43cd2c9 100644 --- a/src/firebase.ts +++ b/src/firebase.ts @@ -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, }; diff --git a/src/routes.ts b/src/routes.ts index 774399f..58ae721 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -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', }