Skip to content

Commit

Permalink
Merge pull request #42 from the-collab-lab/sc-issue-17
Browse files Browse the repository at this point in the history
17. As a user I want my lists protected by google and firebase authentication. I want the log in/ sign up to be separated from the rest of the app for clarity and protection/privacy.
  • Loading branch information
StefieCaff authored Mar 28, 2024
2 parents a1f7ffa + c36cff3 commit 7b5ee2e
Show file tree
Hide file tree
Showing 17 changed files with 278 additions and 51 deletions.
68 changes: 38 additions & 30 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

import { Home, Layout, List, ManageList } from './views';
import { Home, Layout, List, ManageList, Err, Login } from './views';

import { PublicRoute, PrivateRoute, Spinner } from './components';

import { useAuth } from './api';

Expand All @@ -13,9 +15,6 @@ export function App() {
* This custom hook takes the path of a shopping list
* in our database and syncs it with localStorage for later use.
* Check ./utils/hooks.js for its implementation.
*
* We'll later use `setListPath` when we allow a user
* to create and switch between lists.
*/
const [listPath, setListPath] = useStateWithStorage(
'tcl-shopping-list-path',
Expand All @@ -42,36 +41,45 @@ export function App() {
*/
const { data, loading, setLoading } = useShoppingListData(listPath);

if (loading) {
return <Spinner />;
}
return (
<Router>
<Routes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Home
data={lists}
setListPath={setListPath}
setLoading={setLoading}
/>
}
/>
<Route
path="/list"
element={
<List
data={data}
listPath={listPath}
loading={loading}
setLoading={setLoading}
/>
}
/>
<Route
path="/manage-list"
element={<ManageList listPath={listPath} data={data} />}
/>
<Route element={<PublicRoute />}>
<Route path="/login" element={<Login />} />
</Route>
<Route element={<PrivateRoute />}>
<Route path="/" element={<Layout />}>
<Route
index
element={
<Home
data={lists}
setListPath={setListPath}
setLoading={setLoading}
/>
}
/>
<Route
path="/list"
element={
<List
data={data}
listPath={listPath}
loading={loading}
setLoading={setLoading}
/>
}
/>
<Route
path="/manage-list"
element={<ManageList listPath={listPath} data={data} />}
/>
</Route>
</Route>
<Route path="*" element={<Err />} />
</Routes>
</Router>
);
Expand Down
85 changes: 66 additions & 19 deletions src/api/useAuth.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,82 @@
import { useEffect, useState } from 'react';
import { auth } from './config.js';
import { GoogleAuthProvider, signInWithRedirect } from 'firebase/auth';
import {
GoogleAuthProvider,
getRedirectResult,
signInWithPopup,
} from 'firebase/auth';
import { addUserToDatabase } from './firebase.js';
import { useNavigate } from 'react-router-dom';
import GoogleIcon from '../assets/GoogleIcon.jsx';

/**
* A button that signs the user in using Google OAuth. When clicked,
* the button redirects the user to the Google OAuth sign-in page.
* After the user signs in, they are redirected back to the app.
*/
export const SignInButton = () => (
<button
type="button"
className="block px-4 py-2 rounded-md hover:bg-gray-100"
onClick={() => signInWithRedirect(auth, new GoogleAuthProvider())}
>
Sign In
</button>
);

export const SignInButton = () => {
const handleSignIn = async () => {
try {
// Attempt sign-in with a popup
await signInWithPopup(auth, new GoogleAuthProvider());
} catch (error) {
// If popup is blocked, fall back to redirect
if (error.code === 'auth/popup-blocked') {
try {
// Trigger sign-in with redirect
await getRedirectResult(auth);
} catch (redirectError) {
console.error('Error during redirect sign-in:', redirectError);
// TODO ADD TOAST ONCE COMPONENT COMMITTED
}
} else {
console.error('Error during popup sign-in:', error);
// TODO ADD TOAST ONCE COMPONENT COMMITTED
}
}
};

return (
<button
type="button"
className="flex items-center h-[67px] py-2 rounded-md border-1 border-navBorder hover:bg-gray-100 justify-center sm:px-[120px] md:px-48"
aria-label="Sign up or Log in with google verification"
onClick={handleSignIn}
>
<GoogleIcon /> Verify with Google
</button>
);
};

/**
* A button that signs the user out of the app using Firebase Auth.
*/
export const SignOutButton = () => (
<button
type="button"
className="block px-4 py-2 rounded-md hover:bg-gray-100"
onClick={() => auth.signOut()}
>
Sign Out
</button>
);
export const SignOutButton = () => {
const navigate = useNavigate();

const handleSignOut = async () => {
try {
await auth.signOut();
navigate('/');
} catch (error) {
console.error('Error signing out:', error);
window.alert('Error signing out. Please try again.');
// TODO ADD TOAST ONCE COMPONENT IS COMPLETE
}
};

return (
<button
type="button"
className="block px-4 py-2 rounded-md hover:bg-gray-100"
onClick={handleSignOut}
aria-label="Sign Out"
>
Sign Out
</button>
);
};

/**
* A custom hook that listens for changes to the user's auth state.
Expand Down
32 changes: 32 additions & 0 deletions src/assets/GoogleIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const GoogleIcon = () => {
return (
<svg
width="20"
height="20"
className="mr-2"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-label="Blue Green Yellow Red Google/ capital 'G' logo"
>
<path
d="M19.1666 9.9C19.1666 9.26433 19.115 8.62521 19.0051 7.99984H10.1846V11.6009H15.2357C15.0261 12.7623 14.3526 13.7897 13.3664 14.4425V16.7791H16.3799C18.1495 15.1504 19.1666 12.7451 19.1666 9.9Z"
fill="#4285F4"
/>
<path
d="M10.1846 19.0366C12.7067 19.0366 14.8336 18.2085 16.3833 16.7791L13.3699 14.4425C12.5314 15.0129 11.4491 15.3359 10.188 15.3359C7.7484 15.3359 5.67987 13.69 4.93767 11.4772H1.828V13.8859C3.41548 17.0436 6.64885 19.0366 10.1846 19.0366Z"
fill="#34A853"
/>
<path
d="M4.93424 11.4772C4.54253 10.3158 4.54253 9.05815 4.93424 7.89675V5.48804H1.82801C0.501673 8.1304 0.501664 11.2435 1.828 13.8859L4.93424 11.4772Z"
fill="#FBBC04"
/>
<path
d="M10.1846 4.03456C11.5178 4.01394 12.8063 4.51561 13.7719 5.43649L16.4417 2.76664C14.7512 1.17916 12.5074 0.306393 10.1846 0.333882C6.64885 0.333882 3.41549 2.32683 1.82801 5.48804L4.93424 7.89675C5.673 5.68046 7.74496 4.03456 10.1846 4.03456Z"
fill="#EA4335"
/>
</svg>
);
};

export default GoogleIcon;
34 changes: 34 additions & 0 deletions src/assets/Logo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const Logo = () => {
return (
<svg
width="146"
height="18"
viewBox="0 0 146 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="pr-6 xsm:pr-2 xsm:h-[13px] max-w-fit sm:h-fit sm:mr-6"
role="img of words collablab"
aria-label="Shopping List App Logo"
>
<title>Shopping List App Logo</title>
<g clipPath="url(#clip0_24_7737)">
<path
d="M9.60005 17.4238C8.36251 17.4238 7.21675 17.2093 6.16272 16.7827C5.10869 16.355 4.18771 15.7632 3.40092 15.0074C2.61412 14.2517 2.00282 13.3618 1.56813 12.3377C1.13344 11.3136 0.915527 10.1977 0.915527 8.99123C0.915527 7.78479 1.12885 6.69649 1.55666 5.68038C1.98446 4.66431 2.59119 3.78241 3.37798 3.03354C4.16477 2.28581 5.08117 1.69749 6.12831 1.26974C7.17429 0.841972 8.31667 0.628662 9.55416 0.628662C10.7917 0.628662 11.9903 0.83165 13.0145 1.23533C14.0375 1.64015 14.939 2.19407 15.7189 2.89592L12.9686 5.66894C12.5706 5.24116 12.0901 4.90514 11.5246 4.66087C10.9592 4.4166 10.302 4.29389 9.55416 4.29389C8.91304 4.29389 8.31319 4.40513 7.7558 4.62646C7.1984 4.8478 6.72473 5.16548 6.33474 5.57716C5.9448 5.99003 5.63973 6.48662 5.41838 7.06689C5.197 7.64717 5.08575 8.28939 5.08575 8.99123C5.08575 9.69311 5.19586 10.3812 5.41838 10.9615C5.63973 11.5418 5.94594 12.0384 6.33474 12.4512C6.72473 12.864 7.19383 13.184 7.74431 13.4134C8.29485 13.6427 8.89813 13.7574 9.55416 13.7574C10.318 13.7574 10.9867 13.6393 11.559 13.4019C12.1325 13.1656 12.6325 12.8331 13.0603 12.4053L15.8107 15.1554C15.0158 15.8584 14.119 16.4123 13.1177 16.8171C12.1164 17.2219 10.9442 17.4238 9.60005 17.4238ZM36.7776 17.1485V0.902749H40.879V17.1485H36.7776ZM40.0544 17.1485V13.6198H47.8685V17.1485H40.0544ZM50.3895 17.1485V0.902749H54.4909V17.1485H50.3895ZM53.6659 17.1485V13.6198H61.4802V17.1485H53.6659ZM62.3738 17.1485L68.6987 0.902749H72.6637L78.9192 17.1485H74.6115L69.9135 3.49226H71.3803L66.6137 17.1485H62.3738ZM66.2239 14.3067V11.0532H75.2067V14.3067H66.2239ZM81.0271 17.1485V0.902749H85.0598V17.1485H81.0271ZM84.2811 17.1485V14.1015H87.6952C88.3068 14.1015 88.8035 13.918 89.1854 13.551C89.5673 13.184 89.7578 12.7184 89.7578 12.153C89.7578 11.7562 89.6694 11.4168 89.4938 11.1335C89.3183 10.8514 89.0776 10.6254 88.7723 10.458C88.4661 10.2906 88.0921 10.2057 87.6495 10.2057H84.2811V7.27333H87.3974C87.9478 7.27333 88.3939 7.1357 88.738 6.86046C89.082 6.58522 89.254 6.17354 89.254 5.62305C89.254 5.07256 89.082 4.63793 88.738 4.3627C88.3939 4.08746 87.9548 3.94984 87.4203 3.94984H84.2811V0.902753H88.4053C89.4283 0.902753 90.3069 1.09427 91.0412 1.47616C91.7737 1.85805 92.3395 2.36953 92.7364 3.01174C93.1332 3.65282 93.3326 4.37875 93.3326 5.18841C93.3326 6.25722 92.9621 7.15176 92.2212 7.86966C91.4803 8.58755 90.4068 9.03826 89.0019 9.22177V7.82377C90.5445 8.00728 91.7359 8.50731 92.5767 9.32499C93.4161 10.1427 93.8372 11.1691 93.8372 12.4065C93.8372 13.3078 93.608 14.1209 93.1504 14.8469C92.6915 15.5728 92.047 16.1382 91.2141 16.543C90.3817 16.9479 89.3997 17.1497 88.2698 17.1497H84.282L84.2811 17.1485ZM101.399 17.1485V0.902749H105.5V17.1485H101.399ZM104.676 17.1485V13.6198H112.49V17.1485H104.676ZM113.383 17.1485L119.707 0.902749H123.672L129.928 17.1485H125.62L120.922 3.49226H122.389L117.622 17.1485H113.383ZM117.233 14.3067V11.0532H126.216V14.3067H117.233ZM132.036 17.1485V0.902749H136.069V17.1485H132.036ZM135.29 17.1485V14.1015H138.704C139.315 14.1015 139.812 13.918 140.194 13.551C140.575 13.184 140.767 12.7184 140.767 12.153C140.767 11.7562 140.679 11.4168 140.503 11.1335C140.328 10.8514 140.087 10.6254 139.782 10.458C139.476 10.2906 139.102 10.2057 138.659 10.2057H135.291V7.27333H138.407C138.957 7.27333 139.403 7.1357 139.747 6.86046C140.091 6.58522 140.263 6.17354 140.263 5.62305C140.263 5.07256 140.091 4.63793 139.747 4.3627C139.405 4.08746 138.964 3.94984 138.43 3.94984H135.291V0.902753H139.415C140.438 0.902753 141.317 1.09427 142.051 1.47616C142.784 1.85805 143.349 2.36953 143.747 3.01174C144.144 3.65282 144.342 4.37875 144.342 5.18841C144.342 6.25722 143.972 7.15176 143.231 7.86966C142.49 8.58755 141.416 9.03826 140.011 9.22177V7.82377C141.554 8.00728 142.746 8.50731 143.586 9.32499C144.426 10.1427 144.847 11.1691 144.847 12.4065C144.847 13.3078 144.617 14.1209 144.16 14.8469C143.701 15.5728 143.055 16.1382 142.224 16.543C141.391 16.9479 140.41 17.1497 139.28 17.1497H135.292L135.29 17.1485ZM33.1935 6.12879C32.7737 5.14252 32.1888 4.27668 31.4376 3.53355C30.6863 2.79041 29.802 2.2193 28.7858 1.82135C27.7697 1.42341 26.6571 1.22501 25.4494 1.22501C24.2417 1.22501 23.1292 1.42341 22.113 1.82135C21.0969 2.2193 20.2126 2.79041 19.4613 3.53355C18.7101 4.27783 18.1252 5.13909 17.7054 6.11845C17.2856 7.09787 17.0757 8.16211 17.0757 9.31121C17.0757 10.4603 17.2891 11.5314 17.7168 12.5257C18.1435 13.52 18.7365 14.3847 19.4957 15.1221C20.2539 15.8584 21.1416 16.4329 22.1577 16.8458C23.1739 17.2575 24.2864 17.4639 25.4942 17.4639C26.7019 17.4639 27.788 17.261 28.7973 16.8561C29.8066 16.4513 30.6863 15.8767 31.4376 15.1324C32.1888 14.3893 32.7737 13.5235 33.1935 12.536C33.6132 11.5498 33.8231 10.4821 33.8231 9.33299C33.8231 8.18388 33.6132 7.11621 33.1935 6.12994V6.12879ZM23.2806 6.77216L19.9499 9.34104L23.2806 11.9099V13.6817L18.7285 9.98322V8.69882L23.2806 5.00032V6.77216ZM25.3554 13.6863H23.9091L25.4781 5.0118H26.9026L25.3542 13.6863H25.3554ZM32.0947 9.98436L27.5426 13.6829V11.911L30.8732 9.34219L27.5426 6.7733V5.00146L32.0947 8.69997V9.98436Z"
fill="#0C0C0C"
/>
</g>
<defs>
<clipPath id="clip0_24_7737">
<rect
width="144.169"
height="16.9784"
fill="white"
transform="translate(0.915527 0.510742)"
/>
</clipPath>
</defs>
</svg>
);
};

export default Logo;
Binary file added src/assets/contrastGraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/lightContrastGraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/lightestContrastGraph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/components/PrivateRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Outlet } from 'react-router-dom';
import { useAuth } from '../api';
import { Login } from '../views';

export function PrivateRoute() {
const { user } = useAuth();
return user ? <Outlet /> : <Login />;
}
8 changes: 8 additions & 0 deletions src/components/PublicRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Outlet } from 'react-router-dom';
import { useAuth } from '../api';
import { Login } from '../views';

export function PublicRoute() {
const { user } = useAuth();
return !user ? <Login /> : <Outlet />;
}
23 changes: 23 additions & 0 deletions src/components/Spinner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export function Spinner() {
return (
<div role="status" className="flex my-auto h-screen">
<svg
aria-hidden="true"
className="w-12 h-12 m-auto text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
<span className="sr-only">Loading...</span>
</div>
);
}
3 changes: 3 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './ListItem';
export * from './SingleList';
export * from './PrivateRoute';
export * from './PublicRoute';
export * from './Spinner';
37 changes: 37 additions & 0 deletions src/views/Err.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { NavLink } from 'react-router-dom';
import Logo from '../assets/Logo.jsx';

export function Err() {
return (
<div className="flex my-auto h-screen bg-[url('assets/lightContrastGraph.png')] bg-cover xsm:p-4">
<div className="m-auto">
<div className="flex flex-1 pb-14 justify-center items-center">
<Logo />
<div className="inline-block h-[26px] min-h-[1em] w-[3px] self-stretch bg-loginLine/50 xsm:ml-2 sm:ml-0"></div>
<h1 className="sm:ps-6 xsm:ps-3 xsm:pl-2 xsm:text-[14px] sm:text-lg ">
Navigation error
</h1>
</div>
<div className="w-full max-w-xl p-7 lg:p-10 gap-5 rounded-lg shadow-lg bg-white">
<h2 className="leading-6 font-semibold text-lg">
Eeep, it seems the page you're looking for doesn't exist.
</h2>
<div className="inline-block border-t-1 self-stretch border-loginLine/50"></div>
<div className="px-4">
<h3>
Click{' '}
<NavLink
to="/"
aria-current="page"
className="text-blue-500 hover:text-red-300"
>
here
</NavLink>{' '}
to return to your lists.
</h3>
</div>
</div>
</div>
</div>
);
}
5 changes: 3 additions & 2 deletions src/views/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import AddItem from '../components/AddItem';
import { ListItem } from '../components/ListItem';
import { comparePurchaseUrgency } from '../api/firebase';
import { Spinner } from '../components/Spinner';

export function List({ data, listPath, loading }) {
const [search, setSearch] = useState('');
Expand Down Expand Up @@ -32,7 +33,7 @@ export function List({ data, listPath, loading }) {
return (
<>
{loading ? (
<p>loading . . .</p>
<Spinner />
) : data.length > 0 ? (
<>
<h2 className="flex justify-center xsm:text-md sm:text-lg md:text-3xl mt-6 mb-10">
Expand Down Expand Up @@ -82,7 +83,7 @@ export function List({ data, listPath, loading }) {
</ul>
</>
) : loading ? (
<p>loading . . .</p>
<Spinner />
) : data.length < 1 ? (
<>
<p className="py-2">
Expand Down
23 changes: 23 additions & 0 deletions src/views/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { SignInButton } from '../api/useAuth';
import Logo from '../assets/Logo.jsx';

export function Login() {
return (
<div className="flex my-auto h-screen bg-[url('assets/lightestContrastGraph.png')] bg-cover xsm:p-4">
<div className="m-auto">
<div className="flex flex-1 pb-14 sm:justify-center xsm:justify-between items-center">
<Logo />
<div className="inline-block h-[26px] min-h-[1em] w-[3px] self-stretch bg-loginLine/50"></div>
<h1 className="sm:ps-6 xsm:pl-2 xsm:text-[14px] sm:text-lg ">
Shopping List App
</h1>
</div>
<div className="flex justify-between flex-col w-max-w-xl p-5 gap-5 h-[174px] rounded-lg shadow-lg bg-white">
<h2 className="leading-6 font-semibold text-lg">Log in or Sign up</h2>
<div className="inline-block border-t-1 self-stretch border-loginLine/50"></div>
<SignInButton />
</div>
</div>
</div>
);
}
Loading

0 comments on commit 7b5ee2e

Please sign in to comment.