119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
import React, { ReactNode, useEffect } from 'react';
|
||
import { useState } from 'react';
|
||
import jwt from 'jsonwebtoken';
|
||
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js';
|
||
import menuAside from '../menuAside';
|
||
import menuNavBar from '../menuNavBar';
|
||
import BaseIcon from '../components/BaseIcon';
|
||
import NavBar from '../components/NavBar';
|
||
import NavBarItemPlain from '../components/NavBarItemPlain';
|
||
import AsideMenu from '../components/AsideMenu';
|
||
import FooterBar from '../components/FooterBar';
|
||
import { useAppDispatch, useAppSelector } from '../stores/hooks';
|
||
import Search from '../components/Search';
|
||
import { useRouter } from 'next/router';
|
||
import { findMe, logoutUser } from '../stores/authSlice';
|
||
|
||
type Props = {
|
||
children: ReactNode;
|
||
};
|
||
|
||
export default function LayoutAuthenticated({ children }: Props) {
|
||
const dispatch = useAppDispatch();
|
||
const router = useRouter();
|
||
const { token, currentUser } = useAppSelector((state) => state.auth);
|
||
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
|
||
let localToken;
|
||
if (typeof window !== 'undefined') {
|
||
// Perform localStorage action
|
||
localToken = localStorage.getItem('token');
|
||
}
|
||
|
||
const isTokenValid = () => {
|
||
const token = localStorage.getItem('token');
|
||
if (!token) return;
|
||
const date = new Date().getTime() / 1000;
|
||
const data = jwt.decode(token);
|
||
if (!data) return;
|
||
return date < data.exp;
|
||
};
|
||
|
||
useEffect(() => {
|
||
dispatch(findMe());
|
||
if (!isTokenValid()) {
|
||
dispatch(logoutUser());
|
||
router.push('/login');
|
||
}
|
||
}, [token, localToken]);
|
||
|
||
const darkMode = useAppSelector((state) => state.style.darkMode);
|
||
|
||
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false);
|
||
const [isAsideLgActive, setIsAsideLgActive] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const handleRouteChangeStart = () => {
|
||
setIsAsideMobileExpanded(false);
|
||
setIsAsideLgActive(false);
|
||
};
|
||
|
||
router.events.on('routeChangeStart', handleRouteChangeStart);
|
||
|
||
// If the component is unmounted, unsubscribe
|
||
// from the event with the `off` method:
|
||
return () => {
|
||
router.events.off('routeChangeStart', handleRouteChangeStart);
|
||
};
|
||
}, [router.events, dispatch]);
|
||
|
||
const layoutAsidePadding = 'xl:pl-60';
|
||
|
||
return (
|
||
<div
|
||
className={`${
|
||
darkMode ? 'dark' : ''
|
||
} overflow-hidden lg:overflow-visible`}
|
||
>
|
||
<div
|
||
className={`${layoutAsidePadding} ${
|
||
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
|
||
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
|
||
>
|
||
<NavBar
|
||
menu={menuNavBar}
|
||
className={`${layoutAsidePadding} ${
|
||
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
|
||
}`}
|
||
>
|
||
<NavBarItemPlain
|
||
display='flex lg:hidden'
|
||
onClick={() => setIsAsideMobileExpanded(!isAsideMobileExpanded)}
|
||
>
|
||
<BaseIcon
|
||
path={isAsideMobileExpanded ? mdiBackburger : mdiForwardburger}
|
||
size='24'
|
||
/>
|
||
</NavBarItemPlain>
|
||
<NavBarItemPlain
|
||
display='hidden lg:flex xl:hidden'
|
||
onClick={() => setIsAsideLgActive(true)}
|
||
>
|
||
<BaseIcon path={mdiMenu} size='24' />
|
||
</NavBarItemPlain>
|
||
<NavBarItemPlain useMargin>
|
||
<Search />
|
||
</NavBarItemPlain>
|
||
</NavBar>
|
||
<AsideMenu
|
||
isAsideMobileExpanded={isAsideMobileExpanded}
|
||
isAsideLgActive={isAsideLgActive}
|
||
menu={menuAside}
|
||
onAsideLgClose={() => setIsAsideLgActive(false)}
|
||
/>
|
||
{children}
|
||
<FooterBar>Hand-crafted & Made with ❤️</FooterBar>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|