diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 84836cd8c8b0530ec6b49acf6c51fccdef381ac6..7cb27c1935e6da97adf3040b8117439b90931dcd 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -21,6 +21,7 @@ import { Provider } from 'react-redux'; import { BrowserRouter, HashRouter, Redirect, Route, RouteProps, Switch, useHistory } from 'react-router-dom'; import { ClusterTitle } from './components/cluster/Chooser'; import ActionsNotifier from './components/common/ActionsNotifier'; +import AlertNotification from './components/common/AlertNotification'; import Sidebar, { drawerWidth, NavigationTabs, useSidebarItem } from './components/Sidebar'; import { isElectron } from './helpers'; import { getToken, setToken } from './lib/auth'; @@ -42,8 +43,7 @@ const useStyle = makeStyles(theme => ({ } }, content: { - flexGrow: 1, - padding: theme.spacing(3), + flexGrow: 1 }, toolbar: theme.mixins.toolbar, })); @@ -227,11 +227,14 @@ function AppContainer(props: AppContainerProps) { </AppBar> <Sidebar /> <main className={classes.content}> - <div className={classes.toolbar} /> - <Container maxWidth="lg"> - <NavigationTabs /> - <RouteSwitcher /> - </Container> + <AlertNotification/> + <Box p={3}> + <div className={classes.toolbar} /> + <Container maxWidth="lg"> + <NavigationTabs /> + <RouteSwitcher /> + </Container> + </Box> </main> <ActionsNotifier /> </Box> diff --git a/frontend/src/components/common/AlertNotification.tsx b/frontend/src/components/common/AlertNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4d4f924319dc7f8ad3e64e9d47779f5202930d58 --- /dev/null +++ b/frontend/src/components/common/AlertNotification.tsx @@ -0,0 +1,118 @@ +import { Box, useTheme } from '@material-ui/core'; +import React from 'react'; +import { useRouteMatch } from 'react-router-dom'; +import { testAuth } from '../../lib/k8s/apiProxy'; +import { getRoutePath, ROUTES } from '../../lib/router'; +import { useTypedSelector } from '../../redux/reducers/reducers'; + +const NOT_FOUND_ERROR_MESSAGES = ['Error: Api request error: Bad Gateway', 'Offline']; +// in ms +const NETWORK_STATUS_CHECK_TIME = 5000; + +export default function AlertNotification(){ + const routes = useTypedSelector(state => state.ui.routes); + const [networkStatusCheckTimeFactor, setNetworkStatusCheckTimeFactor] = React.useState(0); + const [error, setError] = React.useState<null | string | boolean>(null); + const [intervalID, setIntervalID] = React.useState<NodeJS.Timeout | null>(null); + + function registerSetInterval(): NodeJS.Timeout { + return setInterval(() => { + if (!window.navigator.onLine) { + setError('Offline'); + return; + } + setError(null); + testAuth().then(() => { + setError(false); + }) + .catch((err) => { + const error = new Error(err); + setError(error.message); + setNetworkStatusCheckTimeFactor(networkStatusCheckTimeFactor => + networkStatusCheckTimeFactor + 1); + }); + }, (networkStatusCheckTimeFactor + 1) * NETWORK_STATUS_CHECK_TIME); + } + + React.useEffect(() => { + const id = registerSetInterval(); + setIntervalID(id); + return () => clearInterval(id); + }, + // eslint-disable-next-line + []); + + React.useEffect(() => { + if (intervalID) { + clearInterval(intervalID); + } + const id = registerSetInterval(); + setIntervalID(id); + return () => clearInterval(id); + }, + // eslint-disable-next-line + [networkStatusCheckTimeFactor]); + + function checkWhetherInNoAuthRequireRoute():boolean { + const noAuthRequiringRoutes = + Object.values(ROUTES) + .concat(Object.values(routes)) + .filter((route) => route.noAuthRequired); + + for (const route of noAuthRequiringRoutes) { + // eslint-disable-next-line react-hooks/rules-of-hooks + const routeMatch = useRouteMatch({ + path: getRoutePath(route), + strict: true + }); + if (routeMatch && routeMatch.isExact) { + return true; + } + } + return false; + } + + const whetherInNoAuthRoute = checkWhetherInNoAuthRequireRoute(); + let isErrorInNoAuthRequiredRoute = false; + if (whetherInNoAuthRoute) { + isErrorInNoAuthRequiredRoute = true; + } + const theme = useTheme(); + if (!error) { + return null; + } + + if (NOT_FOUND_ERROR_MESSAGES.filter((err) => err === error).length === 0) { + return null; + } + return ( + <Box + bgcolor="error.main" + color={theme.palette.common.white} + textAlign="center" + display="flex" + p={1} + justifyContent="center" + position="fixed" + zIndex={1400} + width="100%" + top={'0'} + height={'3.8vh'} + > + <Box marginLeft={isErrorInNoAuthRequiredRoute ? '0%' : '-15%'}> + Something Went Wrong. + </Box> + <Box + bgcolor={theme.palette.common.white} + color="error.main" + ml={1} + px={1} + py={0.1} + style={{cursor: 'pointer'}} + onClick={() => setNetworkStatusCheckTimeFactor(0)} + > + Try Again + </Box> + </Box> + ); +}