Skip to content

Commit

Permalink
fix: fixed weird service worker blank page after install
Browse files Browse the repository at this point in the history
  • Loading branch information
mbret committed May 21, 2024
1 parent 71b8ad5 commit 4f8a232
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 45 deletions.
44 changes: 8 additions & 36 deletions packages/web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { FC, Suspense, useEffect, useState } from "react"
import { FC, Suspense, useState } from "react"
import { AppNavigator } from "./navigation/AppNavigator"
import { Theme, StyledEngineProvider, Fade, Box } from "@mui/material"
import { BlockingBackdrop } from "./common/BlockingBackdrop"
import { TourProvider } from "./app-tour/TourProvider"
import { ManageBookCollectionsDialog } from "./books/ManageBookCollectionsDialog"
import { plugins } from "./plugins/configure"
import * as serviceWorkerRegistration from "./serviceWorkerRegistration"
import { UpdateAvailableDialog } from "./UpdateAvailableDialog"
import { UpdateAvailableDialog } from "./workers/UpdateAvailableDialog"
import { RxDbProvider } from "./rxdb"
import { useObservers } from "./rxdb/sync/useObservers"
import { PreloadQueries } from "./PreloadQueries"
Expand All @@ -17,7 +16,6 @@ import "./i18n"
import { ErrorBoundary } from "@sentry/react"
import { ManageBookTagsDialog } from "./books/ManageBookTagsDialog"
import { ManageTagBooksDialog } from "./tags/ManageTagBooksDialog"
import { useRef } from "react"
import { Effects } from "./Effects"
import {
usePersistSignals,
Expand All @@ -32,6 +30,7 @@ import { profileStorageSignal } from "./profile/storage"
import { authSignalStorageAdapter } from "./auth/storage"
import { authStateSignal } from "./auth/authState"
import { DialogProvider } from "./common/dialogs/DialogProvider"
import { useRegisterServiceWorker } from "./workers/useRegisterServiceWorker"

declare module "@mui/styles/defaultTheme" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand All @@ -44,9 +43,7 @@ export function App() {
const [loading, setLoading] = useState({
isPreloadingQueries: true
})
const [newServiceWorker, setNewServiceWorker] = useState<
ServiceWorker | undefined
>(undefined)
const { waitingWorker } = useRegisterServiceWorker()
const profileSignalStorageAdapter = useSignalValue(profileStorageSignal)

const { isHydrated: isProfileHydrated } = usePersistSignals({
Expand Down Expand Up @@ -87,15 +84,17 @@ export function App() {
<Box height="100%">
<DialogProvider>
<TourProvider>
<AppNavigator isProfileHydrated={isProfileHydrated} />
<AppNavigator
isProfileHydrated={isProfileHydrated}
/>
<FirstTimeExperienceTours />
<ManageBookCollectionsDialog />
<ManageBookTagsDialog />
<ManageTagBooksDialog />
<AuthorizeActionDialog />
</TourProvider>
<UpdateAvailableDialog
serviceWorker={newServiceWorker}
serviceWorker={waitingWorker}
/>
<ReplicateRemoteDb />
<BlockingBackdrop />
Expand All @@ -120,9 +119,6 @@ export function App() {
</QueryClientProvider>
</ThemeProvider>
</StyledEngineProvider>
<ServiceWorkerRegistration
onUpdateAvailable={(sw) => setNewServiceWorker(sw)}
/>
<BlurContainer />
</ErrorBoundary>
)
Expand All @@ -133,27 +129,3 @@ const ReplicateRemoteDb: FC = () => {

return null
}

const ServiceWorkerRegistration: FC<{
onUpdateAvailable: (sw: ServiceWorker) => void
}> = ({ onUpdateAvailable }) => {
const firstTime = useRef(true)

useEffect(() => {
if (firstTime.current) {
firstTime.current = false
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register({
onSuccess: () => console.warn("onSuccess"),
onUpdate: (reg) => reg.waiting && onUpdateAvailable(reg.waiting),
onWaitingServiceWorkerFound: async (reg) => {
reg.waiting && onUpdateAvailable(reg.waiting)
}
})
}
}, [onUpdateAvailable])

return null
}
6 changes: 4 additions & 2 deletions packages/web/src/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ if (import.meta.env.PROD) {
}

// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
// pre-cache, in this case same-origin .png requests like those from in public/
if (import.meta.env.PROD) {
registerRoute(
// Add in any other file extensions or routing criteria as needed.
Expand All @@ -84,7 +84,9 @@ if (import.meta.env.PROD) {
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener("message", (event) => {
if (event.data && event.data.type === "SKIP_WAITING") {
self.skipWaiting()
self.skipWaiting().then(() => {
event.source?.postMessage("SKIP_WAITING_READY")
})
}
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@ import {
DialogContentText,
DialogTitle
} from "@mui/material"
import { FC } from "react"
import { useLock } from "./common/BlockingBackdrop"
import { FC, useEffect } from "react"
import { useLock } from "../common/BlockingBackdrop"
import { filter, first, fromEvent, tap } from "rxjs"
import { Report } from "../debug/report.shared"

export const UpdateAvailableDialog: FC<{
serviceWorker?: ServiceWorker
}> = ({ serviceWorker }) => {
const hasUpdate = !!serviceWorker
const [lock] = useLock()

useEffect(() => {
if (import.meta.env.MODE === "development" && !!serviceWorker) {
serviceWorker?.postMessage({ type: "SKIP_WAITING" })
Report.warn("service worker updated")
}
}, [serviceWorker])

if (import.meta.env.MODE === "development") return null

return (
<Dialog open={hasUpdate}>
<DialogTitle>Yay! A new version is here</DialogTitle>
Expand All @@ -28,9 +39,21 @@ export const UpdateAvailableDialog: FC<{
<DialogActions>
<Button
onClick={() => {
if (!serviceWorker) return

lock()

fromEvent<MessageEvent>(navigator.serviceWorker, "message")
.pipe(
filter((event) => event.data === "SKIP_WAITING_READY"),
first(),
tap(() => {
window.location.reload()
})
)
.subscribe()

serviceWorker?.postMessage({ type: "SKIP_WAITING" })
window.location.reload()
}}
color="primary"
autoFocus
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This optional code is used to register a service worker.
// register() is not called by default.

import { Report } from "./debug/report.shared"
import { Report } from "../debug/report.shared"

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
Expand Down Expand Up @@ -87,6 +87,7 @@ function registerValidSW(swUrl: string, config?: Config) {
type: import.meta.env.MODE === "production" ? "classic" : "module"
})
.then((registration) => {
console.log("registered", registration.waiting)
if (registration.waiting) {
if (config?.onWaitingServiceWorkerFound) {
config?.onWaitingServiceWorkerFound(registration)
Expand All @@ -101,7 +102,7 @@ function registerValidSW(swUrl: string, config?: Config) {
installingWorker.onstatechange = () => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// At this point, the updated pre-cached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
Expand All @@ -114,7 +115,7 @@ function registerValidSW(swUrl: string, config?: Config) {
config.onUpdate(registration)
}
} else {
// At this point, everything has been precached.
// At this point, everything has been pre-cached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log("Content is cached for offline use.")
Expand Down
24 changes: 24 additions & 0 deletions packages/web/src/workers/useRegisterServiceWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useRef, useState } from "react"
import * as serviceWorkerRegistration from "./serviceWorkerRegistration"

export const useRegisterServiceWorker = () => {
const [waitingWorker, setWaitingWorker] = useState<ServiceWorker | undefined>(
undefined
)
const firstTime = useRef(true)

useEffect(() => {
if (firstTime.current) {
firstTime.current = false
serviceWorkerRegistration.register({
onSuccess: () => console.warn("onSuccess"),
onUpdate: (reg) => reg.waiting && setWaitingWorker(reg.waiting),
onWaitingServiceWorkerFound: async (reg) => {
reg.waiting && setWaitingWorker(reg.waiting)
}
})
}
}, [])

return { waitingWorker }
}
2 changes: 1 addition & 1 deletion packages/web/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default defineConfig(({ mode }) => ({
VitePWA({
base: "/",
minify: false,
injectRegister: null,
injectRegister: false,
strategies: "injectManifest",
injectManifest: {
rollupFormat: "iife",
Expand Down

0 comments on commit 4f8a232

Please sign in to comment.