diff --git a/packages/web/src/components/DefaultLogo/index.jsx b/packages/web/src/components/DefaultLogo/index.jsx index 0ced62ba..01043558 100644 --- a/packages/web/src/components/DefaultLogo/index.jsx +++ b/packages/web/src/components/DefaultLogo/index.jsx @@ -3,14 +3,17 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import MationLogo from 'components/MationLogo'; import useAutomatischInfo from 'hooks/useAutomatischInfo'; -const DefaultLogo = () => { - const { isMation, loading } = useAutomatischInfo(); - if (loading) return ; + +export default function DefaultLogo() { + const { data: automatischInfo, isPending } = useAutomatischInfo(); + const isMation = automatischInfo?.data.isMation; + + if (isPending) return ; if (isMation) return ; + return ( ); -}; -export default DefaultLogo; +} diff --git a/packages/web/src/components/QueryClientProvider/index.jsx b/packages/web/src/components/QueryClientProvider/index.jsx index 09c37642..4fc83b8e 100644 --- a/packages/web/src/components/QueryClientProvider/index.jsx +++ b/packages/web/src/components/QueryClientProvider/index.jsx @@ -9,6 +9,7 @@ import useAuthentication from 'hooks/useAuthentication.js'; const queryClient = new QueryClient({ defaultOptions: { queries: { + staleTime: 1000, refetchOnWindowFocus: false, // provides a convenient default while it should be overridden for other HTTP methods queryFn: async ({ queryKey, signal }) => { diff --git a/packages/web/src/components/SettingsLayout/index.jsx b/packages/web/src/components/SettingsLayout/index.jsx index e27af0de..8cb31a91 100644 --- a/packages/web/src/components/SettingsLayout/index.jsx +++ b/packages/web/src/components/SettingsLayout/index.jsx @@ -29,7 +29,8 @@ function createDrawerLinks({ isCloud }) { return items; } export default function SettingsLayout({ children }) { - const { isCloud } = useAutomatischInfo(); + const { data: automatischInfo } = useAutomatischInfo(); + const isCloud = automatischInfo?.data.isCloud; const theme = useTheme(); const formatMessage = useFormatMessage(); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('lg')); diff --git a/packages/web/src/components/ThemeProvider/index.jsx b/packages/web/src/components/ThemeProvider/index.jsx index cad7eb5c..66cc1a5f 100644 --- a/packages/web/src/components/ThemeProvider/index.jsx +++ b/packages/web/src/components/ThemeProvider/index.jsx @@ -4,32 +4,45 @@ import clone from 'lodash/clone'; import get from 'lodash/get'; import set from 'lodash/set'; import * as React from 'react'; -import useConfig from 'hooks/useConfig'; + import useAutomatischInfo from 'hooks/useAutomatischInfo'; +import useConfig from 'hooks/useConfig'; import { defaultTheme, mationTheme } from 'styles/theme'; + const customizeTheme = (theme, config) => { // `clone` is needed so that the new theme reference triggers re-render const shallowDefaultTheme = clone(theme); + for (const key in config) { const value = config[key]; const exists = get(theme, key); + if (exists) { set(shallowDefaultTheme, key, value); } } + return shallowDefaultTheme; }; const ThemeProvider = ({ children, ...props }) => { - const { isMation, loading: automatischInfoLoading } = useAutomatischInfo(); + const { data: automatischInfo, isPending: isAutomatischInfoPending } = + useAutomatischInfo(); + const isMation = automatischInfo?.data.isMation; const { config, loading: configLoading } = useConfig(); + const customTheme = React.useMemo(() => { const installationTheme = isMation ? mationTheme : defaultTheme; - if (configLoading || automatischInfoLoading) return installationTheme; + + if (configLoading || isAutomatischInfoPending) return installationTheme; + const customTheme = customizeTheme(installationTheme, config || {}); + return customTheme; - }, [configLoading, config, isMation, automatischInfoLoading]); + }, [configLoading, config, isMation, isAutomatischInfoPending]); + // TODO: maybe a global loading state for the custom theme? - if (automatischInfoLoading || configLoading) return <>; + if (isAutomatischInfoPending || configLoading) return <>; + return ( diff --git a/packages/web/src/hooks/useAuthentication.js b/packages/web/src/hooks/useAuthentication.js index f31096f5..5cd9bf47 100644 --- a/packages/web/src/hooks/useAuthentication.js +++ b/packages/web/src/hooks/useAuthentication.js @@ -1,7 +1,9 @@ import * as React from 'react'; import { AuthenticationContext } from 'contexts/Authentication'; + export default function useAuthentication() { const authenticationContext = React.useContext(AuthenticationContext); + return { token: authenticationContext.token, updateToken: authenticationContext.updateToken, diff --git a/packages/web/src/hooks/useAutomatischInfo.js b/packages/web/src/hooks/useAutomatischInfo.js index b143ad33..f89c82d2 100644 --- a/packages/web/src/hooks/useAutomatischInfo.js +++ b/packages/web/src/hooks/useAutomatischInfo.js @@ -1,10 +1,20 @@ -import * as React from 'react'; -import { AutomatischInfoContext } from 'contexts/AutomatischInfo'; +import { useQuery } from '@tanstack/react-query'; +import api from 'helpers/api'; + export default function useAutomatischInfo() { - const automatischInfoContext = React.useContext(AutomatischInfoContext); - return { - isCloud: automatischInfoContext.isCloud, - isMation: automatischInfoContext.isMation, - loading: automatischInfoContext.loading, - }; + const query = useQuery({ + /** + * The data doesn't change by user actions, but only by server deployments. + * So we can set the `staleTime` to Infinity + **/ + staleTime: Infinity, + queryKey: ['automatischInfo'], + queryFn: async (payload, signal) => { + const { data } = await api.get('/v1/automatisch/info', null, { signal }); + + return data; + }, + }); + + return query; } diff --git a/packages/web/src/hooks/useCloud.js b/packages/web/src/hooks/useCloud.js index 006f152a..b9b557cb 100644 --- a/packages/web/src/hooks/useCloud.js +++ b/packages/web/src/hooks/useCloud.js @@ -1,11 +1,17 @@ import { useNavigate } from 'react-router-dom'; + import useAutomatischInfo from './useAutomatischInfo'; + export default function useCloud(options) { const redirect = options?.redirect || false; - const { isCloud } = useAutomatischInfo(); const navigate = useNavigate(); + const { data: automatischInfo } = useAutomatischInfo(); + + const isCloud = automatischInfo?.data.isCloud; + if (isCloud === false && redirect) { navigate('/'); } + return isCloud; } diff --git a/packages/web/src/pages/Notifications/index.jsx b/packages/web/src/pages/Notifications/index.jsx index 8448479c..637dbe45 100644 --- a/packages/web/src/pages/Notifications/index.jsx +++ b/packages/web/src/pages/Notifications/index.jsx @@ -1,27 +1,34 @@ -import * as React from 'react'; -import { useNavigate } from 'react-router-dom'; import Box from '@mui/material/Box'; import Stack from '@mui/material/Stack'; -import useNotifications from 'hooks/useNotifications'; +import * as React from 'react'; +import { useNavigate } from 'react-router-dom'; + import Container from 'components/Container'; import NotificationCard from 'components/NotificationCard'; import PageTitle from 'components/PageTitle'; -import useFormatMessage from 'hooks/useFormatMessage'; -import useAutomatischInfo from 'hooks/useAutomatischInfo'; import * as URLS from 'config/urls'; +import useAutomatischInfo from 'hooks/useAutomatischInfo'; +import useFormatMessage from 'hooks/useFormatMessage'; +import useNotifications from 'hooks/useNotifications'; + export default function Updates() { const navigate = useNavigate(); const formatMessage = useFormatMessage(); const { notifications } = useNotifications(); - const { isMation, loading } = useAutomatischInfo(); + const { data: automatischInfo, isPending } = useAutomatischInfo(); + const isMation = automatischInfo?.data.isMation; + React.useEffect( function redirectToHomepageInMation() { - if (!loading && isMation) { + if (!navigate) return; + + if (!isPending && isMation) { navigate(URLS.DASHBOARD); } }, - [loading, isMation], + [isPending, isMation, navigate], ); + return (