feat: redirect after login when applicable

This commit is contained in:
Jordan Knott 2021-04-30 23:24:23 -05:00
parent 04c12e4da9
commit e2634dc490
5 changed files with 69 additions and 49 deletions

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Switch, Route, useHistory } from 'react-router-dom';
import { Switch, Route, useHistory, useLocation, Redirect } from 'react-router-dom';
import * as H from 'history';
import Dashboard from 'Dashboard';
@ -29,8 +29,27 @@ type ValidateTokenResponse = {
userID: string;
};
const AuthorizedRoutes = () => {
const history = useHistory();
const UserRequiredRoute: React.FC<any> = ({ children }) => {
const { user } = useCurrentUser();
const location = useLocation();
if (user) {
return children;
}
return (
<Redirect
to={{
pathname: '/login',
state: { redirect: location.pathname },
}}
/>
);
};
type RoutesProps = {
history: H.History;
};
const Routes: React.FC<RoutesProps> = () => {
const [loading, setLoading] = useState(true);
const { setUser } = useCurrentUser();
useEffect(() => {
@ -38,7 +57,6 @@ const AuthorizedRoutes = () => {
method: 'POST',
credentials: 'include',
}).then(async x => {
const { status } = x;
const response: ValidateTokenResponse = await x.json();
const { valid, userID } = response;
if (valid) {
@ -47,32 +65,28 @@ const AuthorizedRoutes = () => {
setLoading(false);
});
}, []);
return loading ? null : (
if (loading) return null;
return (
<Switch>
<MainContent>
<Route exact path="/" component={Dashboard} />
<Route exact path="/projects" component={Projects} />
<Route path="/projects/:projectID" component={Project} />
<Route path="/teams/:teamID" component={Teams} />
<Route path="/profile" component={Profile} />
<Route path="/admin" component={Admin} />
<Route path="/tasks" component={MyTasks} />
</MainContent>
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
<Route exact path="/confirm" component={Confirm} />
<Switch>
<MainContent>
<Route path="/projects/:projectID" component={Project} />
<UserRequiredRoute>
<Route exact path="/" component={Dashboard} />
<Route exact path="/projects" component={Projects} />
<Route path="/teams/:teamID" component={Teams} />
<Route path="/profile" component={Profile} />
<Route path="/admin" component={Admin} />
<Route path="/tasks" component={MyTasks} />
</UserRequiredRoute>
</MainContent>
</Switch>
</Switch>
);
};
type RoutesProps = {
history: H.History;
};
const Routes: React.FC<RoutesProps> = () => (
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
<Route exact path="/confirm" component={Confirm} />
<AuthorizedRoutes />
</Switch>
);
export default Routes;

View File

@ -2,7 +2,7 @@ import React from 'react';
import TopNavbar, { MenuItem } from 'shared/components/TopNavbar';
import LoggedOutNavbar from 'shared/components/TopNavbar/LoggedOut';
import { ProfileMenu } from 'shared/components/DropdownMenu';
import { useHistory } from 'react-router';
import { useHistory, useRouteMatch } from 'react-router';
import { useCurrentUser } from 'App/context';
import { RoleCode, useTopNavbarQuery } from 'shared/generated/graphql';
import { usePopup, Popup } from 'shared/components/PopupMenu';
@ -234,6 +234,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
onRemoveFromBoard,
}) => {
const { user } = useCurrentUser();
const match = useRouteMatch();
if (user) {
return (
<LoggedInNavbar
@ -255,7 +256,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({
/>
);
}
return <LoggedOutNavbar name={name} menuType={menuType} />;
return <LoggedOutNavbar match={match.url} name={name} menuType={menuType} />;
};
export default GlobalTopNavbar;

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect, useContext } from 'react';
import { useHistory } from 'react-router';
import { useHistory, useLocation } from 'react-router';
import Login from 'shared/components/Login';
import UserContext from 'App/context';
import { Container, LoginWrapper } from './Styles';
@ -7,6 +7,7 @@ import { Container, LoginWrapper } from './Styles';
const Auth = () => {
const [invalidLoginAttempt, setInvalidLoginAttempt] = useState(0);
const history = useHistory();
const location = useLocation<{ redirect: string } | undefined>();
const { setUser } = useContext(UserContext);
const login = (
data: LoginFormData,
@ -30,7 +31,11 @@ const Auth = () => {
const response = await x.json();
const { userID } = response;
setUser(userID);
history.push('/');
if (location.state && location.state.redirect) {
history.push(location.state.redirect);
} else {
history.push('/');
}
}
});
};

View File

@ -12,9 +12,10 @@ export type MenuItem = {
type NavBarProps = {
menuType?: Array<MenuItem> | null;
name: string | null;
match: string;
};
const NavBar: React.FC<NavBarProps> = ({ menuType, name }) => {
const NavBar: React.FC<NavBarProps> = ({ menuType, name, match }) => {
return (
<S.NavbarWrapper>
<S.NavbarHeader>
@ -51,7 +52,12 @@ const NavBar: React.FC<NavBarProps> = ({ menuType, name }) => {
<S.TaskcafeTitle>Taskcafé</S.TaskcafeTitle>
</S.LogoContainer>
<S.GlobalActions>
<Link to="/login">
<Link
to={{
pathname: '/login',
state: { redirect: match },
}}
>
<S.SignIn>Sign In</S.SignIn>
</Link>
</S.GlobalActions>

View File

@ -33,24 +33,19 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler
},
}
c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (interface{}, error) {
/*
TODO: add permission check
role, ok := GetUserRole(ctx)
if !ok {
return nil, errors.New("user ID is missing")
}
if role == "admin" {
return next(ctx)
} else if level == ActionLevelOrg {
return nil, errors.New("must be an org admin")
}
*/
userID, ok := GetUserID(ctx)
userID, ok := GetUser(ctx)
if !ok {
return nil, errors.New("user must be logged in")
}
log.Info("has role")
user, err := repo.GetUserAccountByID(ctx, userID)
if err != nil {
return nil, err
}
if user.RoleCode == "admin" {
return next(ctx)
} else if level == ActionLevelOrg {
return nil, errors.New("must be an org admin")
}
var subjectID uuid.UUID
in := graphql.GetFieldContext(ctx).Args["input"]
@ -90,7 +85,6 @@ func NewHandler(repo db.Repository, emailConfig utils.EmailConfig) http.Handler
return nil, errors.New("error while casting subject uuid")
}
var err error
if level == ActionLevelProject {
logger.New(ctx).WithFields(log.Fields{"subjectID": subjectID}).Info("fetching subject ID by typeArg")
if typeArg == ObjectTypeTask {