mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: logout hook & ui store window dimensions
This commit is contained in:
parent
f6356a33df
commit
91fed1a002
@ -3,12 +3,14 @@ import { Flex, Box } from '@chakra-ui/react';
|
||||
import AppName from './AppName';
|
||||
import UserInfo from './User/UserInfo';
|
||||
import UserNotifications from './User/UserNotifications';
|
||||
import useWindowDimensions from 'hooks/useWindowDimensions';
|
||||
import NavMobile from '../Nav/Mobile/Nav';
|
||||
import { observer } from 'mobx-react';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const { isDesktop } = useWindowDimensions();
|
||||
const {
|
||||
uiStore: { isDesktop },
|
||||
} = useRootStore();
|
||||
|
||||
return (
|
||||
<Box
|
||||
|
||||
@ -11,29 +11,28 @@ import {
|
||||
Box,
|
||||
Avatar,
|
||||
Text,
|
||||
useToast,
|
||||
Skeleton,
|
||||
} from '@chakra-ui/react';
|
||||
import { MdSettings, MdExitToApp } from 'react-icons/md';
|
||||
import { observer } from 'mobx-react';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useHistory, Link } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import UserBadge from 'components/User/UserBadge';
|
||||
import useLogoutQuery from 'hooks/useLogoutQuery';
|
||||
|
||||
const UserInfo: React.FC = () => {
|
||||
const { authStore, userStore } = useRootStore();
|
||||
const history = useHistory();
|
||||
const toast = useToast();
|
||||
const { userStore } = useRootStore();
|
||||
const { logout } = useLogoutQuery();
|
||||
|
||||
const onLogoutHandler = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
try {
|
||||
await authStore.logout();
|
||||
history.push('/auth');
|
||||
toast({ title: 'You are logged out.', isClosable: true, status: 'info' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
// const onLogoutHandler = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
// try {
|
||||
// await authStore.logout();
|
||||
// history.push('/auth');
|
||||
// toast({ title: 'You are logged out.', isClosable: true, status: 'info' });
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
// };
|
||||
|
||||
if (userStore.me === null) {
|
||||
return <Skeleton width='260px' height='40px' borderRadius='md' />;
|
||||
@ -60,7 +59,7 @@ const UserInfo: React.FC = () => {
|
||||
My profile
|
||||
</MenuItem>
|
||||
</Link>
|
||||
<MenuItem onClick={onLogoutHandler}>
|
||||
<MenuItem onClick={logout}>
|
||||
<Box as={MdExitToApp} mr={2} />
|
||||
Logout
|
||||
</MenuItem>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Flex, Box } from '@chakra-ui/react';
|
||||
import useWindowDimensions from 'hooks/useWindowDimensions';
|
||||
import NavList from '../NavList';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
/**
|
||||
*
|
||||
@ -9,7 +10,9 @@ import NavList from '../NavList';
|
||||
* Component for navigating the application.
|
||||
*/
|
||||
const Nav: React.FC = () => {
|
||||
const { isDesktop } = useWindowDimensions();
|
||||
const {
|
||||
uiStore: { isDesktop },
|
||||
} = useRootStore();
|
||||
|
||||
if (isDesktop) {
|
||||
return (
|
||||
@ -32,4 +35,4 @@ const Nav: React.FC = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
export default observer(Nav);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { MdExitToApp } from 'react-icons/md';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
|
||||
@ -47,6 +48,7 @@ const NavMobile: React.FC = () => {
|
||||
View profile
|
||||
</Link>
|
||||
</Flex>
|
||||
<IconButton aria-label='Logout' backgroundColor='gray.200' marginLeft='auto' icon={<MdExitToApp />} />
|
||||
</Flex>
|
||||
</Box>
|
||||
) : (
|
||||
|
||||
@ -4,7 +4,6 @@ import Layout from '../Layout/Layout';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { IPrivateRouteProps } from 'interfaces';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { observer } from 'mobx-react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import ErrorFallback from '../ErrorFallback';
|
||||
import config from 'config';
|
||||
@ -39,4 +38,4 @@ const PrivateRoute: React.FC<IPrivateRouteProps> = ({ component: Component, titl
|
||||
);
|
||||
};
|
||||
|
||||
export default observer(PrivateRoute);
|
||||
export default PrivateRoute;
|
||||
|
||||
@ -2,14 +2,13 @@ import { Badge } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { UserRoleEnum } from 'enums';
|
||||
import { IUserRole } from 'interfaces';
|
||||
import { userHasRoles } from 'helpers';
|
||||
|
||||
/**
|
||||
* Returns the badge for user based on roles
|
||||
*/
|
||||
const UserBadge: React.FC<{ roles: IUserRole[] }> = ({ roles }) => {
|
||||
const slugs = roles.map(role => role.slug);
|
||||
|
||||
if (slugs.includes(UserRoleEnum.ADMIN)) {
|
||||
if (userHasRoles(roles, [UserRoleEnum.ADMIN])) {
|
||||
return (
|
||||
<Badge colorScheme='purple' variant='subtle'>
|
||||
Administrator
|
||||
@ -17,7 +16,7 @@ const UserBadge: React.FC<{ roles: IUserRole[] }> = ({ roles }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (slugs.includes(UserRoleEnum.TEACHER)) {
|
||||
if (userHasRoles(roles, [UserRoleEnum.TEACHER])) {
|
||||
return (
|
||||
<Badge colorScheme='orange' variant='subtle'>
|
||||
Teacher
|
||||
@ -25,7 +24,7 @@ const UserBadge: React.FC<{ roles: IUserRole[] }> = ({ roles }) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (slugs.includes(UserRoleEnum.STUDENT)) {
|
||||
if (userHasRoles(roles, [UserRoleEnum.STUDENT])) {
|
||||
return <Badge variant='green'>Student</Badge>;
|
||||
}
|
||||
|
||||
|
||||
49
src/hooks/useLogoutQuery.ts
Normal file
49
src/hooks/useLogoutQuery.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useRootStore } from './useRootStore';
|
||||
import { IDataResult } from 'interfaces';
|
||||
import { useErrorHandler } from 'react-error-boundary';
|
||||
|
||||
type StateType = {
|
||||
result: IDataResult | null;
|
||||
error: any;
|
||||
loading: boolean;
|
||||
fetched: boolean;
|
||||
};
|
||||
|
||||
const useLogoutQuery = () => {
|
||||
const { authStore } = useRootStore();
|
||||
const [state, setState] = React.useState<StateType>({ result: null, error: null, loading: false, fetched: false });
|
||||
const isMounted = React.useRef(true);
|
||||
const history = useHistory();
|
||||
const toast = useToast();
|
||||
const handleError = useErrorHandler();
|
||||
const request = React.useCallback(async () => {
|
||||
setState(s => ({ ...s, loading: true }));
|
||||
try {
|
||||
const data = await authStore.logout();
|
||||
history.push('/auth');
|
||||
|
||||
toast({ title: 'You are logged out.', isClosable: true, status: 'info' });
|
||||
if (isMounted.current) setState(s => ({ ...s, result: data }));
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
setState(s => ({ ...s, error }));
|
||||
} finally {
|
||||
if (isMounted.current) setState(s => ({ ...s, loading: false, fetched: true }));
|
||||
}
|
||||
}, [authStore, history, toast, handleError]);
|
||||
|
||||
const logout = () => request();
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { ...state, logout };
|
||||
};
|
||||
|
||||
export default useLogoutQuery;
|
||||
@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const useWindowDimensions = () => {
|
||||
const [windowDimensions, setWindowDimensions] = React.useState({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
/**
|
||||
* Update state on resize
|
||||
*/
|
||||
const handleResize = () => {
|
||||
setWindowDimensions({ width: window.innerWidth, height: window.innerHeight });
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
windowDimensions,
|
||||
isDesktop: windowDimensions.width > 768,
|
||||
isTablet: windowDimensions.width < 768,
|
||||
isMobile: windowDimensions.width < 568,
|
||||
};
|
||||
};
|
||||
|
||||
export default useWindowDimensions;
|
||||
@ -71,3 +71,8 @@ export interface IPublicRouteProps extends RouteProps {
|
||||
component: React.FC<IPageProps>;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface IWindowDimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
@ -1,23 +1,12 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { Box, Heading, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Grid } from '@chakra-ui/react';
|
||||
|
||||
import { IPageProps } from 'interfaces';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* Courses page
|
||||
*/
|
||||
const CoursesPage: React.FC<IPageProps> = ({ title }) => {
|
||||
const { authStore } = useRootStore();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
if (!authStore.isLoggedIn) {
|
||||
history.push('/auth');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb fontWeight='medium' fontSize='sm'>
|
||||
|
||||
@ -1,23 +1,12 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { Box, Heading, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Grid } from '@chakra-ui/react';
|
||||
|
||||
import { IPageProps } from 'interfaces';
|
||||
import { useRootStore } from 'hooks/useRootStore';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* Messages page
|
||||
*/
|
||||
const MessagesPage: React.FC<IPageProps> = ({ title }) => {
|
||||
const { authStore } = useRootStore();
|
||||
const history = useHistory();
|
||||
|
||||
useEffect(() => {
|
||||
if (!authStore.isLoggedIn) {
|
||||
history.push('/auth');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb fontWeight='medium' fontSize='sm'>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { makeAutoObservable } from 'mobx';
|
||||
import { makeAutoObservable, observable } from 'mobx';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import RootStore from './RootStore';
|
||||
import { IWindowDimensions } from 'interfaces';
|
||||
|
||||
export default class UIStore {
|
||||
public root: RootStore;
|
||||
@ -9,18 +10,43 @@ export default class UIStore {
|
||||
|
||||
public history: ReturnType<typeof createBrowserHistory>;
|
||||
|
||||
public windowDimensions: IWindowDimensions = { width: window.innerWidth, height: window.innerHeight };
|
||||
|
||||
constructor(root: RootStore) {
|
||||
this.root = root;
|
||||
this.history = createBrowserHistory();
|
||||
makeAutoObservable(this);
|
||||
|
||||
makeAutoObservable(this, {
|
||||
windowDimensions: observable.struct,
|
||||
});
|
||||
|
||||
this.history.listen(({ pathname }) => {
|
||||
console.log(`[history]: pathname changed to "${pathname}"`);
|
||||
this.location = pathname;
|
||||
});
|
||||
|
||||
window.onresize = () => {
|
||||
this.windowDimensions = this.getWindowDimensions();
|
||||
};
|
||||
}
|
||||
|
||||
public setLocation(location: string) {
|
||||
public setLocation(location: string): void {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
private getWindowDimensions(): IWindowDimensions {
|
||||
return { width: window.innerWidth, height: window.innerHeight };
|
||||
}
|
||||
|
||||
get isDesktop() {
|
||||
return this.windowDimensions.width > 768;
|
||||
}
|
||||
|
||||
get isTablet() {
|
||||
return this.windowDimensions.width < 768;
|
||||
}
|
||||
|
||||
get isMobile() {
|
||||
return this.windowDimensions.width < 568;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user