diff options
-rw-r--r-- | modern/src/App.jsx | 2 | ||||
-rw-r--r-- | modern/src/UpdateController.tsx | 58 | ||||
-rw-r--r-- | modern/src/common/attributes/useServerAttributes.js | 4 | ||||
-rw-r--r-- | modern/src/resources/l10n/en.json | 2 | ||||
-rw-r--r-- | modern/vite.config.js | 1 |
5 files changed, 66 insertions, 1 deletions
diff --git a/modern/src/App.jsx b/modern/src/App.jsx index 89785cfa..4fe34f64 100644 --- a/modern/src/App.jsx +++ b/modern/src/App.jsx @@ -8,6 +8,7 @@ import SocketController from './SocketController'; import CachingController from './CachingController'; import { useEffectAsync } from './reactHelper'; import { sessionActions } from './store'; +import UpdateController from './UpdateController'; const useStyles = makeStyles(() => ({ page: { @@ -48,6 +49,7 @@ const App = () => { <> <SocketController /> <CachingController /> + <UpdateController /> <div className={classes.page}> <Outlet /> </div> diff --git a/modern/src/UpdateController.tsx b/modern/src/UpdateController.tsx new file mode 100644 index 00000000..0b2b7985 --- /dev/null +++ b/modern/src/UpdateController.tsx @@ -0,0 +1,58 @@ +import { Snackbar, IconButton } from '@mui/material'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import React from 'react' +import { useSelector } from 'react-redux'; +import { useTranslation } from './common/components/LocalizationProvider'; +import { useRegisterSW } from 'virtual:pwa-register/react' + +// Based on https://vite-pwa-org.netlify.app/frameworks/react.html +function UpdateController() { + const t = useTranslation(); + + const swUpdateInterval = useSelector((state) => state.session.server.attributes.serviceWorkerUpdateInterval || 3600000); + + const { + needRefresh: [needRefresh], + updateServiceWorker, + } = useRegisterSW({ + onRegisteredSW(swUrl, swRegistration) { + if (swUpdateInterval > 0 && swRegistration) { + setInterval(async () => { + if (!(!swRegistration.installing && navigator)) { + return; + } + + if (('connection' in navigator) && !navigator.onLine) { + return; + } + + const newSW = await fetch(swUrl, { + cache: 'no-store', + headers: { + 'cache': 'no-store', + 'cache-control': 'no-cache', + }, + }); + + if (newSW?.status === 200) { + await swRegistration.update(); + } + }, swUpdateInterval); + } + } + }); + + return ( + <Snackbar + open={needRefresh} + message={t('settingsUpdateAvailable')} + action={( + <IconButton color="inherit" onClick={() => updateServiceWorker(true)}> + <RefreshIcon /> + </IconButton> + )} + /> + ); +} + +export default UpdateController; diff --git a/modern/src/common/attributes/useServerAttributes.js b/modern/src/common/attributes/useServerAttributes.js index 4339840e..e724c2b0 100644 --- a/modern/src/common/attributes/useServerAttributes.js +++ b/modern/src/common/attributes/useServerAttributes.js @@ -43,6 +43,10 @@ export default (t) => useMemo(() => ({ name: t('settingsTotpForce'), type: 'boolean', }, + serviceWorkerUpdateInterval: { + name: t('settingsServiceWorkerUpdateInterval'), + type: 'number', + }, 'ui.disableLoginLanguage': { name: t('attributeUiDisableLoginLanguage'), type: 'boolean', diff --git a/modern/src/resources/l10n/en.json b/modern/src/resources/l10n/en.json index ed9516b5..d2e9c7ca 100644 --- a/modern/src/resources/l10n/en.json +++ b/modern/src/resources/l10n/en.json @@ -226,6 +226,8 @@ "settingsDarkMode": "Dark Mode", "settingsTotpEnable": "Enable One-time Password", "settingsTotpForce": "Force One-time Password", + "settingsServiceWorkerUpdateInterval": "ServiceWorker Update Interval", + "settingsUpdateAvailable": "There is an update available.", "reportTitle": "Reports", "reportScheduled": "Scheduled Reports", "reportDevice": "Device", diff --git a/modern/vite.config.js b/modern/vite.config.js index 200579da..9f6df445 100644 --- a/modern/vite.config.js +++ b/modern/vite.config.js @@ -19,7 +19,6 @@ export default defineConfig(() => ({ svgr(), react(), VitePWA({ - registerType: 'autoUpdate', workbox: { navigateFallbackDenylist: [/^\/api/], }, |