From 15eceb3eeefa23961fc730db81e8a55bf6deab90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Rialland?= Date: Thu, 30 Jun 2022 19:32:57 +0200 Subject: [PATCH] Add PWA --- site/.gitignore | 3 +- site/package.json | 4 +- site/source/Provider.tsx | 25 +- site/source/ServiceWorker.tsx | 103 + site/source/components/Banner.tsx | 4 +- site/source/design-system/buttons/Button.tsx | 5 +- .../design-system/field/Select/index.tsx | 1 + site/source/design-system/message/index.tsx | 23 +- site/source/public/favicon/site.webmanifest | 1 - site/source/public/manifest.webmanifest | 21 - site/source/public/sw.js | 8 - site/source/template.html | 36 +- site/vite.config.ts | 118 +- yarn.lock | 1746 +++++++++++++++-- 14 files changed, 1911 insertions(+), 187 deletions(-) create mode 100644 site/source/ServiceWorker.tsx delete mode 100644 site/source/public/favicon/site.webmanifest delete mode 100644 site/source/public/manifest.webmanifest delete mode 100644 site/source/public/sw.js diff --git a/site/.gitignore b/site/.gitignore index c7775c85a..4cc84f862 100644 --- a/site/.gitignore +++ b/site/.gitignore @@ -6,4 +6,5 @@ cypress/screenshots cypress/downloads .deps.json netlify*.toml -source/public/sitemap.*.txt \ No newline at end of file +source/public/sitemap.*.txt +dev-dist \ No newline at end of file diff --git a/site/package.json b/site/package.json index 2482cb2ec..ccff7e18e 100644 --- a/site/package.json +++ b/site/package.json @@ -142,9 +142,11 @@ "ts-morph": "^13.0.3", "ts-node": "^10.8.0", "typescript": "^4.7.2", - "vite": "^2.9.9", + "vite": "^2.9.13", + "vite-plugin-pwa": "^0.12.1", "vite-plugin-shim-react-pdf": "^1.0.5", "vitest": "^0.9.4", + "workbox-window": "^6.5.3", "xml2js": "^0.4.23", "yaml": "^1.9.2" } diff --git a/site/source/Provider.tsx b/site/source/Provider.tsx index 163e7b92b..83fde98e2 100644 --- a/site/source/Provider.tsx +++ b/site/source/Provider.tsx @@ -22,6 +22,7 @@ import { I18nextProvider } from 'react-i18next' import { Provider as ReduxProvider } from 'react-redux' import { BrowserRouter } from 'react-router-dom' import { CompatRouter } from 'react-router-dom-v5-compat' +import { ServiceWorker } from './ServiceWorker' import * as safeLocalStorage from './storage/safeLocalStorage' import { store } from './store' import { inIframe } from './utils' @@ -30,26 +31,6 @@ import { inIframe } from './utils' import { TrackingContext } from './ATInternetTracking' import { createTracker } from './ATInternetTracking/Tracker' -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(null) @@ -100,6 +81,10 @@ export default function Provider({ } > + {!import.meta.env.SSR && + import.meta.env.MODE === 'production' && + 'serviceWorker' in navigator && + !inIframe() && } diff --git a/site/source/ServiceWorker.tsx b/site/source/ServiceWorker.tsx new file mode 100644 index 000000000..0b331c37c --- /dev/null +++ b/site/source/ServiceWorker.tsx @@ -0,0 +1,103 @@ +import { Trans, useTranslation } from 'react-i18next' +import styled from 'styled-components' +import { useRegisterSW } from 'virtual:pwa-register/react' +import { Message } from './design-system' +import { HideButton } from './design-system/banner' +import { Button } from './design-system/buttons' +import { Body } from './design-system/typography/paragraphs' + +const PromptContainer = styled.div` + position: fixed; + bottom: 0; + right: 0; + z-index: 10000; + min-height: initial !important; +` + +const StyledSmallBody = styled(Body)` + margin: 0.5rem 0.75rem 0.5rem 0; +` + +const StyledMessage = styled(Message)` + margin: 0 0.5rem 0.5rem 0.5rem !important; + max-width: 450px; + + ${Message.Wrapper} { + display: flex; + flex-direction: column; + align-items: stretch; + } +` + +const StyledHideButton = styled.div` + position: absolute; + top: 0.375rem; + right: 0.375rem; +` + +export const ServiceWorker = () => { + const { t } = useTranslation() + + const { + offlineReady: [offlineReady, setOfflineReady], + needRefresh: [needRefresh, setNeedRefresh], + updateServiceWorker, + } = useRegisterSW({ + onRegistered: (r) => { + // eslint-disable-next-line no-console + console.log('=> SW Registered: ', r) + }, + onRegisterError: (error) => { + // eslint-disable-next-line no-console + console.log('SW registration error', error) + }, + }) + + return ( + + {offlineReady && ( + + + L'application est prête à fonctionner hors ligne. + + + + setOfflineReady(false)} + aria-label={t('Fermer')} + > + × + + + + )} + + {needRefresh && ( + + + + Nouveau contenu disponible, cliquez sur recharger pour mettre à + jour la page. + {' '} + + + + + setNeedRefresh(false)} + aria-label={t('Fermer')} + > + × + + + + )} + + ) +} diff --git a/site/source/components/Banner.tsx b/site/source/components/Banner.tsx index b9f94aa4b..287af0efc 100644 --- a/site/source/components/Banner.tsx +++ b/site/source/components/Banner.tsx @@ -39,7 +39,9 @@ export default function Banner({ - {children} + + {children} + ) : null diff --git a/site/source/design-system/buttons/Button.tsx b/site/source/design-system/buttons/Button.tsx index 97cfdf7cf..05b6f6ac1 100644 --- a/site/source/design-system/buttons/Button.tsx +++ b/site/source/design-system/buttons/Button.tsx @@ -7,7 +7,7 @@ import { wrapperDebounceEvents } from '@/utils' import React, { ForwardedRef, forwardRef } from 'react' import styled, { css } from 'styled-components' -type Size = 'XL' | 'MD' | 'XS' +type Size = 'XL' | 'MD' | 'XS' | 'XXS' type Color = 'primary' | 'secondary' | 'tertiary' type ButtonProps = GenericButtonOrLinkProps & { @@ -69,6 +69,9 @@ export const StyledButton = styled.button` if ($size === 'XS') { return '0.5rem 2rem' } + if ($size === 'XXS') { + return '0.25rem 1rem' + } }}; @media (max-width: ${({ theme }) => theme.breakpointsWidth.sm}) { width: 100%; diff --git a/site/source/design-system/field/Select/index.tsx b/site/source/design-system/field/Select/index.tsx index 87750f493..6459968aa 100644 --- a/site/source/design-system/field/Select/index.tsx +++ b/site/source/design-system/field/Select/index.tsx @@ -35,6 +35,7 @@ export const Label = styled.label` } `} ` + interface ButtonProps { isFocusVisible?: boolean } diff --git a/site/source/design-system/message/index.tsx b/site/source/design-system/message/index.tsx index d3e68e2bb..43f578205 100644 --- a/site/source/design-system/message/index.tsx +++ b/site/source/design-system/message/index.tsx @@ -14,6 +14,7 @@ type MessageProps = { border?: boolean type?: MessageType light?: boolean + className?: string } export function Message({ @@ -22,6 +23,7 @@ export function Message({ border = true, light = false, children, + className, }: MessageProps) { if (typeof children !== 'object') { children = {children} @@ -29,7 +31,12 @@ export function Message({ return ( ({ ...theme, darkMode: false })}> - + {icon && (type === 'success' ? ( ))} -
- {children} -
+ {children}
) @@ -111,3 +112,9 @@ const StyledIcon = styled.img` margin-top: calc(${theme.spacings.lg}); `} ` + +const Wrapper = styled.div` + flex: 1; +` + +Message.Wrapper = Wrapper diff --git a/site/source/public/favicon/site.webmanifest b/site/source/public/favicon/site.webmanifest deleted file mode 100644 index 45dc8a206..000000000 --- a/site/source/public/favicon/site.webmanifest +++ /dev/null @@ -1 +0,0 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/site/source/public/manifest.webmanifest b/site/source/public/manifest.webmanifest deleted file mode 100644 index c99900fc3..000000000 --- a/site/source/public/manifest.webmanifest +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Mon entreprise", - "short_name": "Mon entreprise", - "description": "L'assistant officiel du créateur d'entreprise", - "display": "standalone", - "lang": "fr", - "orientation": "portrait-primary", - "theme_color": "#2975d1", - "icons": [ - { - "src": "/favicon/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/favicon/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/site/source/public/sw.js b/site/source/public/sw.js deleted file mode 100644 index 7d928cb3d..000000000 --- a/site/source/public/sw.js +++ /dev/null @@ -1,8 +0,0 @@ -// A simple, no-op service worker that takes immediate control. - -self.addEventListener('install', () => { - // Skip over the "waiting" lifecycle state, to ensure that our - // new service worker is activated immediately, even if there's - // another tab open controlled by our older service worker code. - self.skipWaiting() -}) diff --git a/site/source/template.html b/site/source/template.html index 2e029e380..25dd19a29 100644 --- a/site/source/template.html +++ b/site/source/template.html @@ -3,30 +3,46 @@ - + + - - + + + + + + + {{ title }} + @@ -35,8 +51,6 @@ - -