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 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 * as H from 'history';
import Dashboard from 'Dashboard'; import Dashboard from 'Dashboard';
@ -29,8 +29,27 @@ type ValidateTokenResponse = {
userID: string; userID: string;
}; };
const AuthorizedRoutes = () => { const UserRequiredRoute: React.FC<any> = ({ children }) => {
const history = useHistory(); 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 [loading, setLoading] = useState(true);
const { setUser } = useCurrentUser(); const { setUser } = useCurrentUser();
useEffect(() => { useEffect(() => {
@ -38,7 +57,6 @@ const AuthorizedRoutes = () => {
method: 'POST', method: 'POST',
credentials: 'include', credentials: 'include',
}).then(async x => { }).then(async x => {
const { status } = x;
const response: ValidateTokenResponse = await x.json(); const response: ValidateTokenResponse = await x.json();
const { valid, userID } = response; const { valid, userID } = response;
if (valid) { if (valid) {
@ -47,32 +65,28 @@ const AuthorizedRoutes = () => {
setLoading(false); setLoading(false);
}); });
}, []); }, []);
return loading ? null : ( if (loading) return null;
<Switch> return (
<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>
</Switch>
);
};
type RoutesProps = {
history: H.History;
};
const Routes: React.FC<RoutesProps> = () => (
<Switch> <Switch>
<Route exact path="/login" component={Login} /> <Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} /> <Route exact path="/register" component={Register} />
<Route exact path="/confirm" component={Confirm} /> <Route exact path="/confirm" component={Confirm} />
<AuthorizedRoutes /> <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>
); </Switch>
);
};
export default Routes; export default Routes;

View File

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

View File

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

View File

@ -12,9 +12,10 @@ export type MenuItem = {
type NavBarProps = { type NavBarProps = {
menuType?: Array<MenuItem> | null; menuType?: Array<MenuItem> | null;
name: string | null; name: string | null;
match: string;
}; };
const NavBar: React.FC<NavBarProps> = ({ menuType, name }) => { const NavBar: React.FC<NavBarProps> = ({ menuType, name, match }) => {
return ( return (
<S.NavbarWrapper> <S.NavbarWrapper>
<S.NavbarHeader> <S.NavbarHeader>
@ -51,7 +52,12 @@ const NavBar: React.FC<NavBarProps> = ({ menuType, name }) => {
<S.TaskcafeTitle>Taskcafé</S.TaskcafeTitle> <S.TaskcafeTitle>Taskcafé</S.TaskcafeTitle>
</S.LogoContainer> </S.LogoContainer>
<S.GlobalActions> <S.GlobalActions>
<Link to="/login"> <Link
to={{
pathname: '/login',
state: { redirect: match },
}}
>
<S.SignIn>Sign In</S.SignIn> <S.SignIn>Sign In</S.SignIn>
</Link> </Link>
</S.GlobalActions> </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) { c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, roles []RoleLevel, level ActionLevel, typeArg ObjectType) (interface{}, error) {
/* userID, ok := GetUser(ctx)
TODO: add permission check
role, ok := GetUserRole(ctx)
if !ok { if !ok {
return nil, errors.New("user ID is missing") return nil, errors.New("user must be logged in")
} }
if role == "admin" { user, err := repo.GetUserAccountByID(ctx, userID)
if err != nil {
return nil, err
}
if user.RoleCode == "admin" {
return next(ctx) return next(ctx)
} else if level == ActionLevelOrg { } else if level == ActionLevelOrg {
return nil, errors.New("must be an org admin") return nil, errors.New("must be an org admin")
} }
*/
userID, ok := GetUserID(ctx)
if !ok {
return nil, errors.New("user must be logged in")
}
log.Info("has role")
var subjectID uuid.UUID var subjectID uuid.UUID
in := graphql.GetFieldContext(ctx).Args["input"] 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") return nil, errors.New("error while casting subject uuid")
} }
var err error
if level == ActionLevelProject { if level == ActionLevelProject {
logger.New(ctx).WithFields(log.Fields{"subjectID": subjectID}).Info("fetching subject ID by typeArg") logger.New(ctx).WithFields(log.Fields{"subjectID": subjectID}).Info("fetching subject ID by typeArg")
if typeArg == ObjectTypeTask { if typeArg == ObjectTypeTask {