Skip to content

Commit

Permalink
Added documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
mbeps committed Jan 31, 2023
1 parent 584c35e commit 3e5499d
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 51 deletions.
38 changes: 29 additions & 9 deletions src/atoms/authModalAtom.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
import { atom } from "recoil"
import { atom } from "recoil";

/**
* Interface which describes the state of the authentication modal.
* The modal has 2 properties:
*
* - `open` (boolean): whether it is open or not
* - `view` ("login" | "signup" | "resetPassword"): which specific view of the modal should be displayed
*/
export interface AuthModalState {
open: boolean;
view: "login" | "signup" | "resetPassword";
};
open: boolean;
view: "login" | "signup" | "resetPassword";
}

/**
* Describes the default state of the authentication modal.
* By default, the modal is closed and
* if no state is specified, it will open in the log in view.
*/
const defaultModalState: AuthModalState = {
open: false,
view: "login",
open: false,
view: "login",
};

/**
* Atom which describes the state of the authentication modal.
* The atom has the state options defined by `AuthModalState` and
* uses the default state defined in `AuthModalState`.
* @requires AuthModalState
* @requires defaultModalState
* @see https://recoiljs.org/docs/basic-tutorial/atoms/
*/
export const authModalState = atom<AuthModalState>({
key: "authModalState",
default: defaultModalState,
});
key: "authModalState", // unique identifier for the atom
default: defaultModalState,
});
10 changes: 10 additions & 0 deletions src/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import React from 'react';
import Navbar from '../Navbar/Navbar';

/**
* Provides a common layout for the entire application.
* Each page in the application will follow this standard layout.
* Each page will display the navbar component.
* Each page will be different hence different children components can be passed.
* @param param0 children components for different pages
* @returns Navbar and children
* @see https://nextjs.org/docs/basic-features/layouts
* @requires src/components/Navbar/Navbar.tsx - navbar at the top of every page
*/
const Layout:React.FC = ({ children }) => {

return (
Expand Down
8 changes: 8 additions & 0 deletions src/components/Modal/Auth/AuthInputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ type AuthInputsProps = {

};

/**
* Checks what the current view of the authentication modal state is.
* If the state is `login`, the the modal will display the log in view.
* If the state is `signup`, the modal will display the sign up view.
* @returns log in or sign up components views
* @requires ./Login - log in view
* @requires ./Signup - sign up view
*/
const AuthInputs:React.FC<AuthInputsProps> = () => {
const modalState = useRecoilValue(authModalState);

Expand Down
37 changes: 37 additions & 0 deletions src/components/Modal/Auth/AuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,50 @@ import AuthInputs from "./AuthInputs";
import OAuthButtons from "./OAuthButtons";
import ResetPassword from "./ResetPassword";

/**
* Displays an authentication modal while `open` is `true`.
* If the `open` is `false`, then the modal is closed.
* The modal has 3 different views as described by `authModalAtom`:
*
* - `login`: displays the log in view
* - `signup`: displays the signup view
* - `resetPassword`: displays the reset password view
*
* If the user is trying to log in or sign up,
* Third party authentication providers are displayed and
* sign up or log in forms are displayed.
* If the user is resetting the password,
* only the reset password elements are shown and
* Third party authentication providers and log in or sign up forms are not displayed.
* @returns authentication modal
* @requires ./AuthInputs - display correct form depending on `login` or `signup` state
* @requires ./OAuthButtons - third party authentication providers such as Google or GitHub
* @requires ./ResetPassword - display reset password view
* @see https://chakra-ui.com/docs/components/modal/usage
*/
const AuthModal: React.FC = () => {
const [modalState, setModalState] = useRecoilState(authModalState);
/**
* Keeps track of whether a user is authenticated via Firebase.
* It returns the `user` details, if it fails then `null` is stored.
* While communicating with Firebase, `loading` (boolean) is set to `true` and
* once the communication is complete it is set to `false`.
* `error` is null until an error takes place while communicating with Firebase.
*/
const [user, loading, error] = useAuthState(auth);

/**
* If a user is authenticated, the modal will automatically close.
* This is used after signing up or logging in as once the user is authenticated,
* the modal does not need to be open.
*/
useEffect(() => {
if (user) handleClose();
}, [user]);

/**
* Closes the authentication modal by setting its state to `open` state to false.
*/
const handleClose = () => {
setModalState((prev) => ({
...prev,
Expand All @@ -37,6 +73,7 @@ const AuthModal: React.FC = () => {
<Modal isOpen={modalState.open} onClose={handleClose}>
<ModalOverlay />
<ModalContent>
{/* Dynamically display header depending on the authentication state */}
<ModalHeader textAlign="center">
{modalState.view === "login" && "Login"}
{modalState.view === "signup" && "Sign Up"}
Expand Down
22 changes: 17 additions & 5 deletions src/components/Modal/Auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import React, { useState } from "react";
import { Button, Flex, Input, Text } from "@chakra-ui/react";
import { useSetRecoilState } from "recoil";
import { authModalState, AuthModalState } from "../../../atoms/authModalAtom";
import React, { useState } from "react";
import { useSignInWithEmailAndPassword } from "react-firebase-hooks/auth";
import { useSetRecoilState } from "recoil";
import { authModalState } from "../../../atoms/authModalAtom";
import { auth } from "../../../firebase/clientApp";
import { FIREBASE_ERRORS } from "../../../firebase/errors";

type LoginProps = {};

/**
* Allows the user to input the log in credentials (email and password) to log into the site.
* Contains 2 input fields, `Email` and `Password` and a log in button.
*
* If the credentials are correct, the user is signed in.
* If the credentials are incorrect, error messages are displayed.
*
* Buttons for resetting the password and signing up are present.
* Clicking these buttons would change the modal to the appropriate view.
* @returns Log in components view for modal.
* @see https://github.com/CSFrequency/react-firebase-hooks/tree/master/auth
*/
const Login: React.FC<LoginProps> = () => {
const setAuthModalState = useSetRecoilState(authModalState); // Set global state
const [loginForm, setLoginForm] = useState({
Expand All @@ -32,8 +44,8 @@ const Login: React.FC<LoginProps> = () => {

/**
* Function to execute when the form is changed (when email and password are typed).
* Multiple inputs use the same onChange function.
* @param event(React.ChangeEvent<HTMLInputElement>) - the event that is triggered when the form is changed
* Multiple inputs use the same `onChange` function.
* @param event (React.ChangeEvent<HTMLInputElement>) - the event that is triggered when the form is changed
*/
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// Update form state
Expand Down
9 changes: 9 additions & 0 deletions src/components/Modal/Auth/OAuthButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import {
import { auth } from "../../../firebase/clientApp";
import { FIREBASE_ERRORS } from "../../../firebase/errors";

/**
* Displays third party authentication providers, in this case Google and GitHub.
* When a provider is clicked:
* - A new account is created if the user does not already exist
* - Signed in if it is an existing user
* - An error is displayed if the user already exist with a different provider.
* @returns
* @see https://github.com/CSFrequency/react-firebase-hooks/tree/master/auth
*/
const OAuthButtons: React.FC = ({}) => {
const [signInWithGoogle, userGoogle, loadingGoogle, errorGoogle] =
useSignInWithGoogle(auth);
Expand Down
23 changes: 19 additions & 4 deletions src/components/Modal/Auth/ResetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,43 @@ import { useSetRecoilState } from "recoil";
import { authModalState } from "../../../atoms/authModalAtom";
import { auth } from "../../../firebase/clientApp";

/**
* Allows the user to reset their password.
* Takes the email as the input and sends the user an email from Firebase to reset the password.
* Once the email is submitted, a new view is shown telling the user to check their email.
* @returns
* @see https://github.com/CSFrequency/react-firebase-hooks/tree/master/auth
*/
const ResetPassword: React.FC = () => {
const setAuthModalState = useSetRecoilState(authModalState);
const [email, setEmail] = useState("");
const [success, setSuccess] = useState(false);
const [sendPasswordResetEmail, sending, error] =
useSendPasswordResetEmail(auth);

/**
* This function is used as the event handler for a form submission.
* It will prevent the page from refreshing.
* Sends the email from Firebase to the email that was inputted in the form.
* @param event (React.FormEvent): the submit event triggered by the form
*/
const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
event.preventDefault(); // Prevent page from reloading

await sendPasswordResetEmail(email);
setSuccess(true);
await sendPasswordResetEmail(email); // try to send email
setSuccess(true); // once the email is successfully send
};
return (
<Flex direction="column" alignItems="center" width="100%">
<Image src="/images/logo.svg" height="40px" mb={2} alt="Website logo"/>
<Image src="/images/logo.svg" height="40px" mb={2} alt="Website logo" />
<Text fontWeight={700} mb={2}>
Reset your password
</Text>
{/* Go to next page once the email is successfully sent */}
{success ? (
<Text mb={4}>Check your email</Text>
) : (
// While the email has not been sent, show the form
<>
<Text fontSize="sm" textAlign="center" mb={2}>
Enter the email associated with your account and we will send you a
Expand Down
10 changes: 10 additions & 0 deletions src/components/Modal/Auth/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import { auth } from "../../../firebase/clientApp";
import { FIREBASE_ERRORS } from "../../../firebase/errors";
import { useCreateUserWithEmailAndPassword } from "react-firebase-hooks/auth";

/**
* Allows the user to create an account by inputting the required credentials (email and password).
* There are 2 password fields to ensure that the user inputs the correct password.
* If the 2 passwords do not match, the account is not created and an error is displayed.
* If the email already exists, the account is not created and an error is displayed.
*
* A button to log in instead is available which would switch the modal to the log in view when clicked.
* @returns Sign up components view for modal.
* @see https://github.com/CSFrequency/react-firebase-hooks/tree/master/auth
*/
const SignUp = () => {
const setAuthModalState = useSetRecoilState(authModalState); // Set global state
const [signUpForm, setSignUpForm] = useState({
Expand Down
70 changes: 41 additions & 29 deletions src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
import { auth } from '@/firebase/clientApp';
import { Flex, Image } from '@chakra-ui/react';
import React from 'react';
import { useAuthState } from 'react-firebase-hooks/auth';
import RightContent from './RightContent/RightContent';
import SearchInput from './SearchInput';

const Navbar:React.FC = () => {
const [user, loading, error] = useAuthState(auth); // will be passed to child components
return (
<Flex bg="white" height="44px" padding="6px 12px">
<Flex align="center">
<Image src="/images/logo.svg" height="30px" alt="Website logo"/>

{/* When screen size is mobile, SVG bellow is not displayed */}
<Image
src="/images/logo_text.svg"
height="46px"
display={{ base: "none", md: "unset" }}
alt="Website text logo"
/>
</Flex>
<SearchInput />
{/* <Directory/> */}
<RightContent user={user}/>
</Flex>
);
}
export default Navbar;
import { auth } from "@/firebase/clientApp";
import { Flex, Image } from "@chakra-ui/react";
import React from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import RightContent from "./RightContent/RightContent";
import SearchInput from "./SearchInput";

/**
* Creates a navbar component which contains the following elements:
*
* - Logo which is visible on mobile and desktop sizes
* - Logo name which is visible only on desktop sizes
* - Search bar which is visible on mobile and desktop sizes and resizes dynamically
* @returns navbar component
* @requires ./RightContent/RightContent
* @requires ./SearchInput
*/
const Navbar: React.FC = () => {
const [user, loading, error] = useAuthState(auth); // will be passed to child components
return (
<Flex bg="white" height="44px" padding="6px 12px">
<Flex align="center">
{/* Logo which is always visible */}
<Image src="/images/logo.svg" height="30px" alt="Website logo" />

{/* Logo name not visible on mobile */}
<Image
src="/images/logo_text.svg"
height="46px"
display={{ base: "none", md: "unset" }}
alt="Website text logo"
/>
</Flex>
<SearchInput />
{/* <Directory/> */}
{/* Changes depending on whether user is authenticated or not */}
<RightContent user={user} />
</Flex>
);
};
export default Navbar;
8 changes: 7 additions & 1 deletion src/components/Navbar/RightContent/AuthButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import { authModalState } from "@/atoms/authModalAtom";
import { Button } from "@chakra-ui/react";
import React from "react";
import { useSetRecoilState } from "recoil";
// import { authModalAtom } from "../../../atoms/authModalAtom";

/**
* Displays 2 authentication buttons which open the authentication modal when clicked:
*
* - `Log In`: opens the log in modal
* - `Sign Up`: opens the sign up modal
* @returns Authentication buttons (log in and sign up)
*/
const AuthButtons: React.FC = () => {
const setAuthModalState = useSetRecoilState(authModalState);// Set global state
return (
Expand Down
4 changes: 4 additions & 0 deletions src/components/Navbar/RightContent/LogOutButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { Button } from '@chakra-ui/react';
import { signOut } from 'firebase/auth';
import React from 'react';

/**
* Displays a log out button which signs out the currently logged in user.
* @returns Log out button
*/
const LogOutButton:React.FC = () => {

return (
Expand Down
9 changes: 9 additions & 0 deletions src/components/Navbar/RightContent/RightContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ type RightContentProps = {
user: any;
};

/**
* Right content is a section of the navbar which dynamically adjusts based on state.
* If the user is not authenticated, the right content will display log in and sign up buttons.
* If the user is authenticated, the right content will display the log out button.
* @param {user} - to manage state and adjust the UI based on said state
* @returns
* @requires ./AuthButtons
* @requires ./LogOutButton
*/
const RightContent: React.FC<RightContentProps> = ({ user }) => {
return (
<>
Expand Down
9 changes: 8 additions & 1 deletion src/components/Navbar/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@ type SearchInputProps = {
// user:
};

/**
* Search bar which would allow the user to carry out searches on the site.
* Search bar dynamically resizes depending on the screen size.
* It will use all the available space of the parent component (navbar).
* @returns Search component
* @see https://chakra-ui.com/docs/components/input/usage
*/
const SearchInput: React.FC<SearchInputProps> = () => {
return (
// flexGrow uses the remaining space in the navbar
<Flex flexGrow={1} mr={2} align='center'>
<Flex flexGrow={1} mr={2} align="center">
<InputGroup>
<InputLeftElement
pointerEvents="none"
Expand Down
Loading

0 comments on commit 3e5499d

Please sign in to comment.