Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions src/app/[locale]/complete-registration/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { type ReactElement } from 'react';
import CompleteRegistration from './CompleteRegistration';
import { ReduxGateWrapper } from '../../components/ReduxGateWrapper';
import { ProtectedPageWrapper } from '../../components/ProtectedPageWrapper';

export default function CompleteRegistrationPage(): ReactElement {
return (
<ReduxGateWrapper>
{/* TODO: Revisit protected page wrappers. This page changes the status of the user which causes flickers of mismatched authentication */}
{/* <ProtectedPageWrapper targetStatus='authenticated'> */}
<CompleteRegistration />
{/* </ProtectedPageWrapper> */}
<ProtectedPageWrapper targetStatus='authenticated'>
<CompleteRegistration />
</ProtectedPageWrapper>
</ReduxGateWrapper>
);
}
8 changes: 4 additions & 4 deletions src/app/[locale]/verify-email/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { type ReactElement } from 'react';
import PostRegistration from './PostRegistration';
import { ReduxGateWrapper } from '../../components/ReduxGateWrapper';
import { ProtectedPageWrapper } from '../../components/ProtectedPageWrapper';

export default function VerifyEmailPage(): ReactElement {
return (
<ReduxGateWrapper>
{/* TODO: Revisit protected page wrappers. This page changes the status of the user which causes flickers of mismatched authentication */}
{/* <ProtectedPageWrapper targetStatus='unverified'> */}
<PostRegistration />
{/* </ProtectedPageWrapper> */}
<ProtectedPageWrapper targetStatus={['unverified', 'authenticated']}>
<PostRegistration />
</ProtectedPageWrapper>
</ReduxGateWrapper>
);
}
37 changes: 30 additions & 7 deletions src/app/components/ProtectedPageWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@
// targetStatus can be used to scope groups further (e.g. (authenticated), (unverified)).
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useRouter } from 'next/navigation';
import { useRouter, useSearchParams } from 'next/navigation';
import { useAppDispatch } from '../hooks';
import { refreshApp, refreshAppSuccess } from '../store/profile-reducer';
import { selectUserProfileStatus } from '../store/selectors';
import { app } from '../../firebase';
import { SIGN_IN_TARGET } from '../constants/Navigation';
import { useAuthSession } from './AuthSessionProvider';

interface ProtectedPageWrapperProps {
children: React.ReactNode;
/**
* The user profile status required to access this page.
* The user profile status or statuses required to access this page.
* Mirrors the `targetStatus` prop from the legacy ProtectedRoute component.
* Defaults to 'registered'.
*/
targetStatus?: string;
targetStatus?: string | string[];
redirect?: string;
}

Expand All @@ -29,9 +30,15 @@ export function ProtectedPageWrapper({
redirect = SIGN_IN_TARGET,
}: ProtectedPageWrapperProps): React.ReactElement | null {
const userProfileStatus = useSelector(selectUserProfileStatus);
const { isAuthResolved } = useAuthSession();
const router = useRouter();
const searchParams = useSearchParams();
const dispatch = useAppDispatch();

const allowedStatuses = Array.isArray(targetStatus)
? targetStatus
: [targetStatus];

useEffect(() => {
app.auth();

Expand All @@ -51,13 +58,29 @@ export function ProtectedPageWrapper({
};
}, [dispatch]);

const isAuthorized = allowedStatuses.includes(userProfileStatus);

useEffect(() => {
if (userProfileStatus !== targetStatus) {
router.replace(redirect);
// Only redirect once auth state has resolved and profile is hydrated
if (!isAuthResolved) return;
if (userProfileStatus === 'idle' || userProfileStatus === 'loading') return;

if (!isAuthorized) {
const query = searchParams?.toString();
const target =
query && !redirect.includes('?') ? `${redirect}?${query}` : redirect;
router.replace(target);
}
}, [userProfileStatus, targetStatus, redirect, router]);
}, [
isAuthResolved,
userProfileStatus,
isAuthorized,
redirect,
router,
searchParams,
]);

if (userProfileStatus !== targetStatus) {
if (!isAuthResolved || !isAuthorized) {
return null;
}

Expand Down