1
0
Fork 0
mirror of https://github.com/betagouv/mon-entreprise synced 2025-02-09 05:15:02 +00:00
mon-entreprise/site/source/Provider.tsx
Maxime Quandalle 3e1bb91279 Configuration du Rendu coté serveur (SSR)
Désormais nous utilisons un script NodeJS natif pour générer le code
HTML pour le pré-rendu des pages. Cela est plus rapide et plus fiable
que la méthode précédente qui consistait un instrumentaliser un
navigateur (pupetter)
https://github.com/chrisvfritz/prerender-spa-plugin

Cela implique toutefois de faire attention à ne plus utiliser des
variables gloables du navigateur, comme `window`, `document` ou
`location` dans nos scripts. C'est plutôt une bonne pratique, mais il
faudrait sans doute configurer du typage pour détecter ces usages le
plus tôt possible et éviter de créer des erreurs inopinées avec le SSR.
2022-01-31 13:33:07 +01:00

209 lines
5.5 KiB
TypeScript

import { OverlayProvider } from '@react-aria/overlays'
import { ErrorBoundary } from '@sentry/react'
import { ThemeColorsProvider } from 'Components/utils/colors'
import { DisableAnimationOnPrintProvider } from 'Components/utils/DisableAnimationContext'
import { IsEmbeddedProvider } from 'Components/utils/embeddedContext'
import { SitePathProvider, SitePaths } from 'Components/utils/SitePathsContext'
import { GlobalStyle } from 'DesignSystem/global-style'
import { Container } from 'DesignSystem/layout'
import DesignSystemThemeProvider from 'DesignSystem/root'
import { H1 } from 'DesignSystem/typography/heading'
import { Link } from 'DesignSystem/typography/link'
import { Body, Intro } from 'DesignSystem/typography/paragraphs'
import { createBrowserHistory } from 'history'
import i18next from 'i18next'
import 'iframe-resizer'
import logo from 'Images/logo-monentreprise.svg'
import {
createContext,
default as React,
default as React,
useEffect,
useMemo,
useState,
} from 'react'
import { HelmetProvider } from 'react-helmet-async'
import { I18nextProvider } from 'react-i18next'
import { Provider as ReduxProvider } from 'react-redux'
import { Router } from 'react-router-dom'
import reducers, { RootState } from 'Reducers/rootReducer'
import {
applyMiddleware,
compose,
createStore,
Middleware,
PreloadedState,
Store,
} from 'redux'
// ATInternet Tracking
import { TrackingContext } from './ATInternetTracking'
import { createTracker } from './ATInternetTracking/Tracker'
import safeLocalStorage from './storage/safeLocalStorage'
import { inIframe } from './utils'
if (
!import.meta.env.SSR &&
import.meta.env.MODE === 'production' &&
'serviceWorker' in navigator &&
!inIframe()
) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then((registration) => {
// eslint-disable-next-line no-console
console.log('SW registered: ', registration)
})
.catch((registrationError) => {
// eslint-disable-next-line no-console
console.log('SW registration failed: ', registrationError)
})
})
}
type SiteName = 'mon-entreprise' | 'infrance' | 'publicodes'
export const SiteNameContext = createContext<SiteName | null>(null)
export type ProviderProps = {
basename: SiteName
children: React.ReactNode
sitePaths?: SitePaths
initialStore?: PreloadedState<RootState>
onStoreCreated?: (store: Store) => void
reduxMiddlewares?: Array<Middleware>
}
const HideLoader = () => {
const [CSS, setCSS] = useState<string>()
// Remove loader when page is load
useEffect(() => {
setCSS(`
#js {
animation: appear 0.5s;
opacity: 1;
}
#loading {
display: none !important;
}`)
}, [])
return <style>{CSS}</style>
}
export default function Provider({
basename,
reduxMiddlewares = [],
initialStore,
onStoreCreated,
children,
sitePaths = {} as SitePaths,
}: ProviderProps) {
const storeEnhancer = compose(applyMiddleware(...reduxMiddlewares))
// Hack: useMemo is used to persist the store across hot reloads.
const store = useMemo(() => {
return createStore(reducers, initialStore, storeEnhancer)
}, [])
onStoreCreated?.(store)
return (
<DesignSystemThemeProvider>
<HideLoader />
<GlobalStyle />
<ErrorBoundary
showDialog
fallback={
<Container>
<Link to={sitePaths.index}>
<img
src={logo}
alt="logo service mon-entreprise urssaf"
style={{
maxWidth: '200px',
width: '100%',
marginTop: '1rem',
}}
></img>
</Link>
<H1>Une erreur est survenue</H1>
<Intro>
L'équipe technique mon-entreprise a été automatiquement prévenue.
</Intro>
<Body>
Vous pouvez également nous contacter directement à l'adresse{' '}
<Link href="mailto:contact@mon-entreprise.beta.gouv.fr">
contact@mon-entreprise.beta.gouv.fr
</Link>{' '}
si vous souhaitez partager une remarque. Veuillez nous excuser
pour la gêne occasionnée.
</Body>
</Container>
}
>
<OverlayProvider>
<ReduxProvider store={store}>
<IsEmbeddedProvider>
<ThemeColorsProvider>
<DisableAnimationOnPrintProvider>
<SiteNameContext.Provider value={basename}>
<SitePathProvider value={sitePaths}>
<I18nextProvider i18n={i18next}>
<HelmetProvider>
<BrowserRouterProvider basename={basename}>
<>{children}</>
</BrowserRouterProvider>
</HelmetProvider>
</I18nextProvider>
</SitePathProvider>
</SiteNameContext.Provider>
</DisableAnimationOnPrintProvider>
</ThemeColorsProvider>
</IsEmbeddedProvider>
</ReduxProvider>
</OverlayProvider>
</ErrorBoundary>
</DesignSystemThemeProvider>
)
}
function BrowserRouterProvider({
children,
basename,
}: {
children: React.ReactNode
basename: string
}) {
// The server rouer is only provided in the entry-server file
if (import.meta.env.SSR) {
return <>{children}</>
}
// eslint-disable-next-line react-hooks/rules-of-hooks
const history = useMemo(
() =>
createBrowserHistory({
basename: import.meta.env.MODE === 'production' ? '' : basename,
}),
[basename]
)
const ATTracker = createTracker(
import.meta.env.VITE_AT_INTERNET_SITE_ID,
safeLocalStorage.getItem('tracking:do_not_track') === '1' ||
navigator.doNotTrack === '1'
)
return (
<TrackingContext.Provider
value={
new ATTracker({
language: i18next.language as 'fr' | 'en',
})
}
>
<Router history={history}>
<>{children}</>
</Router>
</TrackingContext.Provider>
)
}