Skip to content

Commit

Permalink
Add state for current user (#665)
Browse files Browse the repository at this point in the history
  • Loading branch information
acrantel committed Feb 8, 2024
1 parent 6da1a1c commit 4fefb80
Show file tree
Hide file tree
Showing 8 changed files with 464 additions and 10 deletions.
25 changes: 19 additions & 6 deletions frontend2/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ import {
import { DEFAULT_EPISODE } from "./utils/constants";
import NotFound from "./views/NotFound";
import Rankings from "./views/Rankings";
import { CurrentUserProvider } from "./components/CurrentUserProvider";
import PrivateRoute from "./components/PrivateRoute";

const App: React.FC = () => {
const [episodeId, setEpisodeId] = useState(DEFAULT_EPISODE);
return (
<EpisodeContext.Provider value={{ episodeId, setEpisodeId }}>
<RouterProvider router={router} />
</EpisodeContext.Provider>
<CurrentUserProvider>
<EpisodeContext.Provider value={{ episodeId, setEpisodeId }}>
<RouterProvider router={router} />
</EpisodeContext.Provider>
</CurrentUserProvider>
);
};

Expand All @@ -44,10 +48,19 @@ const router = createBrowserRouter([
{ path: "/:episodeId/quickstart", element: <QuickStart /> },
{ path: "/:episodeId/*", element: <NotFound /> },
{ path: "/:episodeId/rankings", element: <Rankings /> },
// Pages that should only be visible when logged in
// TODO: /:episodeId/team, /:episodeId/submissions, /:episodeId/scrimmaging
],
},
// Pages that should only be visible when logged in
{
element: <PrivateRoute />,
children: [
{
element: <EpisodeLayout />,
children: [
// TODO: /:episodeId/team, /:episodeId/submissions, /:episodeId/scrimmaging
],
},
{ path: "/account", element: <Account /> },
// etc
],
},
// Pages that should redirect
Expand Down
63 changes: 63 additions & 0 deletions frontend2/src/components/CurrentUserProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState, useEffect } from "react";
import { type UserPrivate } from "../utils/types";
import {
AuthStateEnum,
type AuthState,
CurrentUserContext,
} from "../contexts/CurrentUserContext";
import * as Api from "../utils/api";
import { removeApiTokens, doApiTokensExist } from "../utils/cookies";

export const CurrentUserProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [userData, setUserData] = useState<{
user?: UserPrivate;
authState: AuthState;
}>({
authState: AuthStateEnum.LOADING,
});

const login = (user: UserPrivate): void => {
setUserData({
user,
authState: AuthStateEnum.AUTHENTICATED,
});
};
const logout = (): void => {
setUserData({
authState: AuthStateEnum.NOT_AUTHENTICATED,
});
};

useEffect(() => {
const checkLoggedIn = async (): Promise<void> => {
// check if cookies exist before attempting to load user
if (!doApiTokensExist()) {
logout();
return;
}
try {
const user = await Api.getUserUserProfile();
login(user);
} catch (error) {
logout();
removeApiTokens();
}
};
void checkLoggedIn();
}, []);

return (
<CurrentUserContext.Provider
value={{
authState: userData.authState,
user: userData.user,
login,
logout,
}}
>
{children}
</CurrentUserContext.Provider>
);
};
23 changes: 23 additions & 0 deletions frontend2/src/components/PrivateRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import { AuthStateEnum, useCurrentUser } from "../contexts/CurrentUserContext";
import { Outlet, useNavigate } from "react-router-dom";
import Spinner from "./Spinner";

const PrivateRoute: React.FC = () => {
const { authState } = useCurrentUser();
const navigate = useNavigate();
if (authState === AuthStateEnum.AUTHENTICATED) {
return <Outlet />;
} else if (authState === AuthStateEnum.NOT_AUTHENTICATED) {
navigate("/login");
return null;
} else {
return (
<div className="h-screen flex items-center justify-center">
<Spinner />
</div>
);
}
};

export default PrivateRoute;
33 changes: 33 additions & 0 deletions frontend2/src/contexts/CurrentUserContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createContext, useContext } from "react";
import { type UserPrivate } from "../utils/types/model/models";

export enum AuthStateEnum {
LOADING = "loading",
AUTHENTICATED = "authenticated",
NOT_AUTHENTICATED = "not_authenticated",
}

export type AuthState = `${AuthStateEnum}`;

interface CurrentUserContextType {
authState: AuthState;
user?: UserPrivate;
login: (user: UserPrivate) => void;
logout: () => void;
}

export const CurrentUserContext = createContext<CurrentUserContextType | null>(
null
);

export const useCurrentUser = (): CurrentUserContextType => {
const currentUserContext = useContext(CurrentUserContext);

if (currentUserContext === null) {
throw new Error(
"useCurrentUser has to be used within <CurrentUserContext.Provider>"
);
}

return currentUserContext;
};
26 changes: 24 additions & 2 deletions frontend2/src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ApiApi } from "./types/api/ApiApi";
import Cookies from "js-cookie";
import * as $ from "jquery";
import * as models from "./types/model/models";
import type * as customModels from "./apiTypes";

// hacky, fall back to localhost for now
const baseUrl = process.env.REACT_APP_BACKEND_URL ?? "http://localhost:8000";
Expand Down Expand Up @@ -297,9 +298,30 @@ export const getAllUserTournamentSubmissions = async (
* @param user The user's info.
*/
export const createUser = async (
user: models.UserCreate
user: customModels.CreateUserInput
): Promise<models.UserCreate> => {
return (await API.apiUserUCreate(user)).body;
const defaultUser = {
id: -1,
isStaff: false,
profile: {
avatarUrl: "",
hasAvatar: false,
hasResume: false,
},
};
// convert our input into models.UserCreate.
return (
await API.apiUserUCreate({
...defaultUser,
...user,
profile: {
...defaultUser.profile,
...user.profile,
gender: user.profile.gender as unknown as models.GenderEnum,
country: user.profile.country as unknown as models.CountryEnum,
},
})
).body;
};

/**
Expand Down
Loading

0 comments on commit 4fefb80

Please sign in to comment.