Skip to content

Commit

Permalink
feat: remove terra functionality from authprovider into its own provi…
Browse files Browse the repository at this point in the history
…der (#178)
  • Loading branch information
Fran McDade authored and Fran McDade committed Jan 5, 2025
1 parent 0b8a8b6 commit c60f81c
Show file tree
Hide file tree
Showing 127 changed files with 2,477 additions and 1,071 deletions.
266 changes: 259 additions & 7 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,12 @@
"isomorphic-dompurify": "0.24.0",
"ky": "^1.7.2",
"next": "^14.1.0",
"next-auth": "^4.24.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.2.3",
"react-gtm-module": "2.0.11",
"react-idle-timer": "^5.6.2",
"react-idle-timer": "^5.7.2",
"react-window": "1.8.9",
"uuid": "8.3.2",
"validate.js": "^0.13.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useSession } from "next-auth/react";
import React, { Fragment, useEffect, useMemo } from "react";
import { updateAuthentication } from "../../../../providers/authentication/authentication/dispatch";
import { useAuthentication } from "../../../../providers/authentication/authentication/hook";
import { updateAuthorization } from "../../../../providers/authentication/authorization/dispatch";
import { useAuthorization } from "../../../../providers/authentication/authorization/hook";
import { SessionControllerProps } from "./types";
import { mapAuthentication, mapAuthorization } from "./utils";

export function SessionController({
children,
}: SessionControllerProps): JSX.Element {
const { authenticationDispatch } = useAuthentication();
const { authorizationDispatch } = useAuthorization();
const session = useSession();
const authentication = useMemo(() => mapAuthentication(session), [session]);
const authorization = useMemo(() => mapAuthorization(session), [session]);

useEffect(() => {
authenticationDispatch?.(updateAuthentication(authentication));
authorizationDispatch?.(updateAuthorization(authorization));
}, [
authentication,
authenticationDispatch,
authorization,
authorizationDispatch,
]);

return <Fragment>{children}</Fragment>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ReactNode } from "react";

export interface SessionControllerProps {
children: ReactNode | ReactNode[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { Session } from "next-auth";
import { SessionContextValue } from "next-auth/react";
import {
AUTHENTICATION_STATUS,
UpdateAuthenticationPayload,
UserProfile,
} from "../../../../providers/authentication/authentication/types";
import {
AUTHORIZATION_STATUS,
UpdateAuthorizationStatusPayload,
} from "../../../../providers/authentication/authorization/types";

/**
* Returns the authentication profile and status from the session context.
* @param session - Session context value.
* @returns authentication profile and status.
*/
export function mapAuthentication(
session: SessionContextValue
): UpdateAuthenticationPayload {
return {
profile: mapProfile(session.data),
status: mapAuthenticationStatus(session.status),
};
}

/**
* Maps the session data to a user profile.
* @param sessionData - Session data.
* @returns user profile.
*/
function mapProfile(sessionData: Session | null): UserProfile | undefined {
if (!sessionData) return;
const { user } = sessionData;
if (!user) return;
const { email, image, name } = user;
return {
email: email || "",
image: image || "",
name: name || "",
};
}

/**
* Returns the authentication status based on the session status.
* @param status - Session status.
* @returns authentication status.
*/
function mapAuthenticationStatus(
status: SessionContextValue["status"]
): AUTHENTICATION_STATUS {
switch (status) {
case "authenticated":
return AUTHENTICATION_STATUS.AUTHENTICATED;
case "loading":
return AUTHENTICATION_STATUS.PENDING;
default:
return AUTHENTICATION_STATUS.UNAUTHENTICATED;
}
}

/**
* Maps the session context value to an authorization status.
* @param session - Session context value.
* @returns authorization status.
*/
export function mapAuthorization(
session: SessionContextValue
): UpdateAuthorizationStatusPayload {
switch (session.status) {
case "authenticated":
return AUTHORIZATION_STATUS.AUTHORIZED;
case "loading":
return AUTHORIZATION_STATUS.PENDING;
default:
return AUTHORIZATION_STATUS.UNAUTHORIZED;
}
}
7 changes: 4 additions & 3 deletions src/components/ComponentCreator/ComponentCreator.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from "react";
import { ComponentsConfig, ViewContext } from "../../config/entities";
import { useAuthentication } from "../../hooks/useAuthentication/useAuthentication";
import { useConfig } from "../../hooks/useConfig";
import { useExploreState } from "../../hooks/useExploreState";
import { useFileManifestState } from "../../hooks/useFileManifestState";
import { useSystemStatus } from "../../hooks/useSystemStatus";
import { useAuth } from "../../providers/authentication/auth/hook";

export interface ComponentCreatorProps<T> {
components: ComponentsConfig;
Expand All @@ -27,7 +27,9 @@ export const ComponentCreator = <T,>({
response,
viewContext,
}: ComponentCreatorProps<T>): JSX.Element => {
const { authenticationStatus, isAuthenticated } = useAuthentication();
const {
authState: { isAuthenticated },
} = useAuth();
const { config, entityConfig } = useConfig();
const { exploreState } = useExploreState();
const { fileManifestState } = useFileManifestState();
Expand All @@ -53,7 +55,6 @@ export const ComponentCreator = <T,>({
? c.viewBuilder(response, {
...viewContext,
authState: {
authenticationStatus,
isAuthenticated,
},
entityConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { ReactNode } from "react";
import { LoginStatus } from "../../../../../../../../../../hooks/useAuthentication/common/entities";
import { useAuthentication } from "../../../../../../../../../../hooks/useAuthentication/useAuthentication";
import { TerraResponse } from "../../../../../../../../../../hooks/useAuthentication/useFetchTerraProfile";
import { useConfig } from "../../../../../../../../../../hooks/useConfig";
import { useTerraProfile } from "../../../../../../../../../../providers/authentication/terra/hook";
import { LoginStatus } from "../../../../../../../../../../providers/authentication/terra/hooks/common/entities";
import { TerraResponse } from "../../../../../../../../../../providers/authentication/terra/hooks/useFetchTerraProfile";
import { ButtonPrimary } from "../../../../../../../../../common/Button/components/ButtonPrimary/buttonPrimary";
import {
ANCHOR_TARGET,
Expand All @@ -23,7 +23,7 @@ export const AcceptTerraTOS = ({
}: AcceptTerraTOSProps): JSX.Element | null => {
const { config } = useConfig();
const { exportToTerraUrl } = config;
const { terraProfileLoginStatus } = useAuthentication();
const { terraProfileLoginStatus } = useTerraProfile();
const isTOSAccepted = isTermsOfServiceAccepted(terraProfileLoginStatus);

const onOpenTerra = (): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import {
expireTimeInSeconds,
useAuthenticationNIHExpiry,
} from "../../../../../../../../hooks/useAuthentication/useAuthenticationNIHExpiry";
} from "../../../../../../../../hooks/authentication/terra/useAuthenticationNIHExpiry";
import { Alert } from "../../../../../../../common/Alert/alert";
import { ALERT_PROPS } from "../../../../../../../common/Alert/constants";
import { FluidPaper } from "../../../../../../../common/Paper/paper.styles";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
ONBOARDING_STEP,
OnboardingStatus,
useAuthenticationForm,
} from "../../../../../../hooks/useAuthentication/useAuthenticationForm";
} from "../../../../../../hooks/authentication/terra/useAuthenticationForm";
import { useAuth } from "../../../../../../providers/authentication/auth/hook";
import { AUTH_STATUS } from "../../../../../../providers/authentication/auth/types";
import { TEXT_BODY_400_2_LINES } from "../../../../../../theme/common/typography";
import {
FluidPaper,
Expand All @@ -17,11 +19,12 @@ import { CreateTerraAccount } from "./components/FormStep/components/CreateTerra
import { Section, SectionContent } from "./terraSetUpForm.styles";

export const TerraSetUpForm = (): JSX.Element | null => {
const { isComplete, isReady, onboardingStatusByStep } =
useAuthenticationForm();

if (!isReady) return null;

const {
authState: { isAuthenticated, status },
} = useAuth();
const { isComplete, onboardingStatusByStep } = useAuthenticationForm();
if (!isAuthenticated) return null;
if (status === AUTH_STATUS.PENDING) return null;
return isComplete ? null : (
<FluidPaper>
<GridPaper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {
ButtonProps as MButtonProps,
IconButton as MIconButton,
IconButtonProps as MIconButtonProps,
Skeleton,
} from "@mui/material";
import { useRouter } from "next/router";
import React, { ElementType, useCallback } from "react";
import { useAuthentication } from "../../../../../../../../../../hooks/useAuthentication/useAuthentication";
import Router from "next/router";
import React, { ElementType } from "react";
import { useProfile } from "../../../../../../../../../../hooks/authentication/profile/useProfile";
import { ROUTE } from "../../../../../../../../../../routes/constants";
import { AuthenticationMenu } from "./components/AuthenticationMenu/authenticationMenu";
import { StyledButton } from "./components/Button/button.styles";

Expand All @@ -21,28 +23,17 @@ export const Authentication = ({
Button,
closeMenu,
}: AuthenticationProps): JSX.Element | null => {
const { isAuthenticated, requestAuthentication, userProfile } =
useAuthentication();
const router = useRouter();
const onLogout = useCallback((): void => {
location.href = router.basePath;
}, [router]);

const { isLoading, profile } = useProfile();
if (!authenticationEnabled) return null;

if (isLoading) return <Skeleton height={32} variant="circular" width={32} />;
if (profile) return <AuthenticationMenu profile={profile} />;
return (
<>
{isAuthenticated && userProfile ? (
<AuthenticationMenu onLogout={onLogout} userProfile={userProfile} />
) : (
<Button
onClick={(): void => {
requestAuthentication();
closeMenu();
}}
/>
)}
</>
<Button
onClick={async (): Promise<void> => {
await Router.push(ROUTE.LOGIN);
closeMenu();
}}
/>
);
};

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,69 +1,45 @@
import { MenuItem } from "@mui/material";
import React, { MouseEvent, useState } from "react";
import { UserProfile } from "../../../../../../../../../../../../hooks/useAuthentication/useFetchGoogleProfile";
import React, { Fragment } from "react";
import { useAuth } from "../../../../../../../../../../../../providers/authentication/auth/hook";
import { UserProfile } from "../../../../../../../../../../../../providers/authentication/authentication/types";
import { useMenu } from "../../../../../../../../../../../common/Menu/hooks/useMenu";
import {
Avatar,
AuthenticationMenu as Menu,
UserIcon,
UserNames,
UserSummary,
} from "./authenticationMenu.styles";
import { MENU_PROPS } from "./constants";

export interface AuthenticationMenuProps {
onLogout: () => void;
userProfile: UserProfile;
profile: UserProfile;
}

export const AuthenticationMenu = ({
onLogout,
userProfile,
profile,
}: AuthenticationMenuProps): JSX.Element => {
const [anchorEl, setAnchorEl] = useState<null | HTMLButtonElement>(null);
const open = Boolean(anchorEl);

const onOpenMenu = (event: MouseEvent<HTMLButtonElement>): void => {
setAnchorEl(event.currentTarget);
};

const onCloseMenu = (): void => {
setAnchorEl(null);
};

const { service: { requestLogout } = {} } = useAuth();
const { anchorEl, onClose, onOpen, open } = useMenu<HTMLElement>();
return (
<>
<UserIcon onClick={onOpenMenu}>
<Avatar
alt={`${userProfile.given_name} ${userProfile.family_name}`}
src={userProfile.picture}
/>
<Fragment>
<UserIcon onClick={onOpen}>
<Avatar alt={profile.name} src={profile.image} />
</UserIcon>
<Menu
anchorEl={anchorEl}
anchorOrigin={{ horizontal: "right", vertical: "bottom" }}
autoFocus={false}
onClose={onCloseMenu}
open={open}
slotProps={{ paper: { variant: "menu" } }}
transformOrigin={{
horizontal: "right",
vertical: "top",
}}
>
<Menu {...MENU_PROPS} anchorEl={anchorEl} onClose={onClose} open={open}>
<UserSummary>
You are signed in as:
<UserNames noWrap>
{userProfile.given_name} {userProfile.family_name}
</UserNames>
<UserNames noWrap>{profile.name}</UserNames>
</UserSummary>
<MenuItem
onClick={(): void => {
onCloseMenu();
onLogout();
requestLogout?.();
onClose();
}}
>
Logout
</MenuItem>
</Menu>
</>
</Fragment>
);
};
Loading

0 comments on commit c60f81c

Please sign in to comment.