Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

13 login with google #21

Merged
merged 7 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions Client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.3",
"@mui/material": "^5.15.2",
"@react-oauth/google": "^0.12.1",
"axios": "^1.6.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
5 changes: 4 additions & 1 deletion Client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import LoginPage from "./pages/login/LoginPage";
import { ThemeProvider } from "@emotion/react";
import { theme } from "./theme/theme";
import { HomePage } from "./pages/home/HomePage";
import { GoogleOAuthProvider } from "@react-oauth/google";

const router = createBrowserRouter([
{
Expand All @@ -25,7 +26,9 @@ const router = createBrowserRouter([
function App() {
return (
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
<GoogleOAuthProvider clientId="940068358470-1vud6iv0uj41vtkrho6msob9g6bhbm78.apps.googleusercontent.com">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is it the clientid?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used to identify the app. It lets the user know who is he authenticating to and so on

<RouterProvider router={router} />
</GoogleOAuthProvider>
</ThemeProvider>
);
}
Expand Down
23 changes: 23 additions & 0 deletions Client/src/components/loadingButton/LoadingButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Button, ButtonProps, CircularProgress } from "@mui/material";

interface LoadingButtonProps extends ButtonProps {
isLoading: boolean;
loader?: JSX.Element;
}

export const LoadingButton = (props: LoadingButtonProps) => {
const { isLoading, loader, children, ...buttonProps } = props;

return (
<Button
{...buttonProps}
disabled={buttonProps.disabled || isLoading}
endIcon={!isLoading && buttonProps.endIcon}
startIcon={!isLoading && buttonProps.startIcon}
>
{isLoading
? loader ?? <CircularProgress size={22} color={buttonProps.color} />
: children}
</Button>
);
};
54 changes: 43 additions & 11 deletions Client/src/pages/login/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Alert, AlertTitle, Button, styled } from "@mui/material";
import {
Alert,
AlertTitle,
CircularProgress,
Divider,
styled,
} from "@mui/material";
import TextField from "@mui/material/TextField/TextField";
import React from "react";
import {
Expand All @@ -8,16 +14,24 @@ import {
logoStyles,
textButtonStyles,
actionButtonsStyles,
dividerStyles,
googleButtonStyles,
googleLoaderStyles,
} from "./styles";
import { useLoginForm } from "./useLoginForm";
import { useLoginFormActions } from "./useLoginFormActions";
import GoogleIcon from "@mui/icons-material/Google";
import { LoadingButton } from "../../components/loadingButton/LoadingButton";

const PageWrapper = styled("div")(pageWrapperStyles);
const Logo = styled("img")(logoStyles);
const LoginForm = styled("div")(loginFormStyles);
const InputFields = styled("div")(inputFieldsStyles);
const ActionButtons = styled("div")(actionButtonsStyles);
const TextButton = styled("p")(textButtonStyles);
const ActionsDivider = styled(Divider)(dividerStyles);
const GoogleButton = styled(LoadingButton)(googleButtonStyles);
const GoogleLoader = styled(CircularProgress)(googleLoaderStyles);

const LoginPage: React.FC = () => {
const {
Expand All @@ -33,11 +47,14 @@ const LoginPage: React.FC = () => {
setError,
} = useLoginForm();

const { handleLogin, handleRegister } = useLoginFormActions(
email,
password,
setError
);
const {
handleLogin,
isLoginLoading,
handleRegister,
isRegisterLoading,
handleGoogleLogin,
isGoogleLoading,
} = useLoginFormActions(email, password, setError);

return (
<PageWrapper>
Expand Down Expand Up @@ -66,32 +83,47 @@ const LoginPage: React.FC = () => {

<ActionButtons>
{inLoginMode ? (
<Button
<LoadingButton
isLoading={isLoginLoading}
fullWidth
variant="outlined"
color="primary"
onClick={handleLogin}
disabled={!isFormValid}
>
Login
</Button>
</LoadingButton>
) : (
<Button
<LoadingButton
isLoading={isRegisterLoading}
fullWidth
variant="outlined"
color="primary"
onClick={handleRegister}
disabled={!isFormValid}
>
Login
</Button>
Register
</LoadingButton>
)}

<TextButton onClick={toggleMode}>
{inLoginMode
? "Or create an account"
: "Already have an account? Login instead"}
</TextButton>

<ActionsDivider />

<GoogleButton
isLoading={isGoogleLoading}
loader={<GoogleLoader size={20} />}
onClick={handleGoogleLogin}
fullWidth
variant="outlined"
startIcon={<GoogleIcon />}
>
Continue with Google
</GoogleButton>
</ActionButtons>
</LoginForm>
</PageWrapper>
Expand Down
21 changes: 21 additions & 0 deletions Client/src/pages/login/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,24 @@ export const textButtonStyles = css({
color: purple[800],
},
});

export const dividerStyles = css({
margin: "20px 0",
});

const GOOGLE_COLOR = "#004C97";

export const googleButtonStyles = css({
color: GOOGLE_COLOR,
"&:hover": {
borderColor: GOOGLE_COLOR,
borderWidth: "2px",
backgroundColor: GOOGLE_COLOR,
color: "white",
},
});

export const googleLoaderStyles = css({
color: GOOGLE_COLOR,
padding: 2,
});
12 changes: 0 additions & 12 deletions Client/src/pages/login/useFinishLogin.ts

This file was deleted.

50 changes: 44 additions & 6 deletions Client/src/pages/login/useLoginFormActions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
import axios from "axios";
import { useFinishLogin } from "./useFinishLogin";
import { CodeResponse, useGoogleLogin } from "@react-oauth/google";
import { useCallback, useState } from "react";
amitkeinan9 marked this conversation as resolved.
Show resolved Hide resolved
import { useNavigate } from "react-router-dom";

export const useLoginFormActions = (
email: string,
password: string,
setError: React.Dispatch<React.SetStateAction<string>>
) => {
const finishLogin = useFinishLogin();
const [isLoginLoading, setIsLoginLoading] = useState<boolean>(false);
const [isRegisterLoading, setIsRegisterLoading] = useState<boolean>(false);
const [isGoogleLoading, setIsGoogleLoading] = useState<boolean>(false);

const navigate = useNavigate();

const handleGoogleLogin = useGoogleLogin({
flow: "auth-code",
onSuccess: async (codeRes: CodeResponse) => {
setIsGoogleLoading(true);
await handleAction("/api/auth/google", codeRes);
setIsGoogleLoading(false);
},
});

const userData = { email, password };

const handleAction = async (apiRoute: string) => {
const finishLogin = (tokens: {
accessToken: string;
refreshToken: string;
}) => {
localStorage.setItem("accessToken", tokens.accessToken);
localStorage.setItem("refreshToken", tokens.refreshToken);

navigate("/home");
};

const handleAction = async (apiRoute: string, body?: object) => {
try {
const { data } = await axios.post(apiRoute, userData);
const { data } = await axios.post(apiRoute, body ?? userData);
finishLogin(data);
} catch (error) {
if (axios.isAxiosError(error)) {
Expand All @@ -25,12 +50,25 @@ export const useLoginFormActions = (
}
};

const handleLogin = () => handleAction("/api/auth/login");
const handleLogin = async () => {
setIsLoginLoading(true);
await handleAction("/api/auth/login");
setIsLoginLoading(false);
};

const handleRegister = async () => {
setIsRegisterLoading(true);

const handleRegister = () => handleAction("/api/auth/register");
handleAction("/api/auth/register");
setIsRegisterLoading(true);
amitkeinan9 marked this conversation as resolved.
Show resolved Hide resolved
};

return {
handleLogin,
isLoginLoading,
handleRegister,
isRegisterLoading,
handleGoogleLogin: () => handleGoogleLogin(),
amitkeinan9 marked this conversation as resolved.
Show resolved Hide resolved
isGoogleLoading,
};
};
6 changes: 6 additions & 0 deletions Server/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
export default {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src"],
};
Loading