Skip to content

Commit

Permalink
feat(app-general): redirect user to visited link after authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrieleMazzola committed Jan 18, 2021
1 parent c5a907b commit 9946a73
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 22 deletions.
18 changes: 15 additions & 3 deletions packages/game-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Suspense } from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import React, { Suspense, useEffect, useState } from 'react';
import { Redirect, Route, Switch, useLocation, useHistory } from 'react-router-dom';
import { PrivateRoute, RoutingPath } from '@pipeline/routing';
import { useBootstrapIsFinished } from './_shared';
import { AuthUser, useLoggedUser } from '@pipeline/auth';
Expand All @@ -26,7 +26,7 @@ function renderAuthRoutes(user: AuthUser | null) {
<Route path={RoutingPath.Signup} component={Signup} />,
<Route path={RoutingPath.VerifyEmail} component={VerifyEmail} />,
<Route path="*">
<Redirect to={RoutingPath.Signup} />
<Redirect to={RoutingPath.Login} />
</Route>,
];
}
Expand All @@ -52,6 +52,18 @@ function App() {
const bootstrapIsFinished = useBootstrapIsFinished();

const user = useLoggedUser();
const [state, setState] = useState(false);

const location = useLocation<{ desiredUrl: string }>();
const history = useHistory();

useEffect(() => {
if (!state && user?.emailVerified && location.state?.desiredUrl) {
history.push(location.state.desiredUrl);
setState(true);
//TODO: verify if this boolean state if really necessary and whether we can avoid it.
}
}, [location, user, history, state]);

return bootstrapIsFinished ? (
<Suspense fallback={null}>
Expand Down
11 changes: 10 additions & 1 deletion packages/game-app/src/_shared/routing/PrivateRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ type Props = React.ComponentProps<typeof Route>;
const PrivateRoute: React.FC<Props> = props => {
const currentUser = useSelector(authSelectors.getCurrentUser);

return currentUser && currentUser.emailVerified ? <Route {...props} /> : <Redirect to={RoutingPath.Signup} />;
return currentUser && currentUser.emailVerified ? (
<Route {...props} />
) : (
<Redirect
to={{
pathname: RoutingPath.Login,
state: { desiredUrl: props.location?.pathname },
}}
/>
);
};

PrivateRoute.displayName = 'PrivateRoute';
Expand Down
10 changes: 7 additions & 3 deletions packages/game-app/src/_shared/routing/useNavigateOnCondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import { RoutingPath } from './routingPath';
* @param condition the condition to check
* @param route the route to go
*/
export default function useNavigateOnCondition(condition: boolean, route: RoutingPath | string) {
export default function useNavigateOnCondition<State = any>(
condition: boolean,
route: RoutingPath | string,
state?: State,
) {
const [alreadyNavigated, setAlreadyNavigated] = useState<boolean>(false);

const history = useHistory();

useEffect(() => {
if (condition && !alreadyNavigated) {
history.push(route);
history.push(route, state);
setAlreadyNavigated(true);
}
}, [condition, history, route, alreadyNavigated]);
}, [condition, history, route, alreadyNavigated, state]);
}
17 changes: 9 additions & 8 deletions packages/game-app/src/login/components/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FormTextField } from '@pipeline/form';
import { PasswordInput } from '@pipeline/components';
import { useTranslate } from '@pipeline/i18n';
import { useLogin } from '@pipeline/auth';
import { useHistory } from 'react-router-dom';
import { useHistory, useLocation } from 'react-router-dom';
import { RoutingPath } from '@pipeline/routing';
import { Button, Link } from '@pipeline/components';

Expand All @@ -21,17 +21,18 @@ const Login: React.FC<Props> = () => {
},
});

const handleSubmit = methods.handleSubmit;
const { handleSubmit } = methods;

const { call, translatedError, loading } = useLogin();
const { call: login, translatedError: loginTranslateError, loading: loginLoading } = useLogin();

const submit = useMemo(() => handleSubmit(call), [handleSubmit, call]);
const submit = useMemo(() => handleSubmit(login), [login, handleSubmit]);

const history = useHistory();
const location = useLocation<{ desiredUrl: string }>();

const goToSignUp = useCallback(() => {
history.push(RoutingPath.Signup);
}, [history]);
history.push(RoutingPath.Signup, location.state);
}, [history, location]);

return (
<div className="login">
Expand All @@ -43,8 +44,8 @@ const Login: React.FC<Props> = () => {
<div className="text-center">
<Button label={t('login.form.buttonText')} onClick={submit} />
</div>
{translatedError ? <span className="error-message">{translatedError}</span> : null}
{loading ? <span>Loading</span> : null}
{loginTranslateError ? <span className="error-message">{loginTranslateError}</span> : null}
{loginLoading ? <span>Loading</span> : null}
<div className="text-center">
<span>{t('login.notYetAccount')}</span>&nbsp;
<Link onClick={goToSignUp}>{t('login.goToSignup')}</Link>
Expand Down
10 changes: 9 additions & 1 deletion packages/game-app/src/signup/apis/executeSignup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ export async function executeSignup(signupInfo: SignupInfo): Promise<AuthUser> {
role: signupInfo.role,
devOpsMaturity: signupInfo.devOpsMaturity,
});

const emailVerified = user.emailVerified;
if (!emailVerified) {
await user.sendEmailVerification();
if (signupInfo.desiredUrl) {
console.log('desiredUrl:', signupInfo.desiredUrl);
// it has to be a full URL otherwise firebase will not send the continueUrl parameter in the verification link
const redirectUrl = window.location.origin + signupInfo.desiredUrl;
user.sendEmailVerification({ url: redirectUrl });
} else {
user.sendEmailVerification();
}
}
return {
id: user.uid,
Expand Down
11 changes: 7 additions & 4 deletions packages/game-app/src/signup/components/Signup/Signup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { signupValidationSchema } from '../../utils/validation';
import { useTranslate } from '@pipeline/i18n';
import { PasswordInput } from '@pipeline/components';
import { RoutingPath, useNavigateOnCondition } from '@pipeline/routing';
import { useHistory } from 'react-router-dom';
import { useHistory, useLocation } from 'react-router-dom';
import { Link, Button } from '@pipeline/components';

type Props = {};
Expand Down Expand Up @@ -41,19 +41,22 @@ const Signup: React.FC<Props> = () => {
const devOpsMaturities = useDevOpsMaturities();
const gameRoles = useGameRoles();

const location = useLocation<{ desiredUrl: string }>();

const submit = useMemo(
() =>
handleSubmit((info: SignupInfo) => {
info.desiredUrl = location.state?.desiredUrl;
signup(info);
}),
[signup, handleSubmit],
[signup, handleSubmit, location],
);

const history = useHistory();

const goToSignIn = useCallback(() => {
history.push(RoutingPath.Login);
}, [history]);
history.push(RoutingPath.Login, location.state);
}, [history, location]);

useNavigateOnCondition(signupSuccess, RoutingPath.EmailVerificationRequired);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@ import React, { useEffect } from 'react';
import { VerifyEmailParams } from '../../types/emailValidationParams';
import { RoutingPath, useNavigateOnCondition, useQueryParams } from '@pipeline/routing';
import { useEmailVerification, useLoggedUser } from '@pipeline/auth';
import { useLocation } from 'react-router-dom';

type Props = {};

const VerifyEmail: React.FC<Props> = () => {
let params = useQueryParams<VerifyEmailParams>();

const { call, success, translatedError } = useEmailVerification();
const loggedUser = useLoggedUser();

useEffect(() => {
call({ code: params.oobCode });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useNavigateOnCondition(success, loggedUser ? RoutingPath.Dashboard : RoutingPath.Login);
let redirectUrl: string;
if (params.continueUrl) {
redirectUrl = new URL(params.continueUrl).pathname;
} else {
redirectUrl = RoutingPath.Dashboard;
}

const location = useLocation<{ desiredUrl: string }>();

useEffect(() => {
location.state = {
desiredUrl: redirectUrl,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useNavigateOnCondition(success, RoutingPath.Login, {
desiredUrl: redirectUrl,
}); // This is ran only if the user is not logged.

return (
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export interface VerifyEmailParams {
lang: string;
oobCode: string;
apiKey: string;
continueUrl?: string;
}
1 change: 1 addition & 0 deletions packages/game-app/src/signup/types/signupInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface SignupInfo {
repeatPassword: string;
role: string;
devOpsMaturity: string;
desiredUrl: string | null;
}

0 comments on commit 9946a73

Please sign in to comment.