From fa3b3d9bb06282332d23e7512d1f96b76c8dc256 Mon Sep 17 00:00:00 2001 From: Giacomo Rebonato Date: Tue, 27 Jun 2023 17:45:30 +0100 Subject: [PATCH] deploy sw --- client/features/collab-editor.tsx | 2 +- client/features/reload-prompt.css | 29 +++++++++++++++++ client/features/reload-prompt.tsx | 53 +++++++++++++++++++++++++++++++ client/main.tsx | 3 ++ client/types/sw.d.ts | 14 ++++++++ server/plugins/vite-plugin.ts | 10 +++++- tsconfig.json | 2 +- 7 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 client/features/reload-prompt.css create mode 100644 client/features/reload-prompt.tsx create mode 100644 client/types/sw.d.ts diff --git a/client/features/collab-editor.tsx b/client/features/collab-editor.tsx index b6f83e3..ef4f442 100644 --- a/client/features/collab-editor.tsx +++ b/client/features/collab-editor.tsx @@ -1,8 +1,8 @@ import { HocuspocusProvider } from '@hocuspocus/provider' import Editor from '@monaco-editor/react' + import { IndexeddbPersistence } from 'y-indexeddb' import { MonacoBinding } from 'y-monaco' - import * as Y from 'yjs' const DOC_NAME = 'collab-editor' diff --git a/client/features/reload-prompt.css b/client/features/reload-prompt.css new file mode 100644 index 0000000..70fa268 --- /dev/null +++ b/client/features/reload-prompt.css @@ -0,0 +1,29 @@ +.ReloadPrompt-container { + padding: 0; + margin: 0; + width: 0; + height: 0; +} +.ReloadPrompt-toast { + position: fixed; + right: 0; + bottom: 0; + margin: 16px; + padding: 12px; + border: 1px solid #8885; + border-radius: 4px; + z-index: 1; + text-align: left; + box-shadow: 3px 4px 5px 0 #8885; + background-color: white; +} +.ReloadPrompt-toast-message { + margin-bottom: 8px; +} +.ReloadPrompt-toast-button { + border: 1px solid #8885; + outline: none; + margin-right: 5px; + border-radius: 2px; + padding: 3px 10px; +} diff --git a/client/features/reload-prompt.tsx b/client/features/reload-prompt.tsx new file mode 100644 index 0000000..a5214b9 --- /dev/null +++ b/client/features/reload-prompt.tsx @@ -0,0 +1,53 @@ +// eslint-disable-next-line import/no-unresolved +import { useRegisterSW } from 'virtual:pwa-register/react' +import './reload-prompt.css' + +export function ReloadPrompt() { + const { + offlineReady: [offlineReady, setOfflineReady], + needRefresh: [needRefresh, setNeedRefresh], + updateServiceWorker, + } = useRegisterSW({ + onRegistered(r) { + // eslint-disable-next-line prefer-template + console.log('SW Registered: ' + r) + }, + onRegisterError(error) { + console.log('SW registration error', error) + }, + }) + + const close = () => { + setOfflineReady(false) + setNeedRefresh(false) + } + + return ( +
+ {(offlineReady || needRefresh) && ( +
+
+ {offlineReady ? ( + App ready to work offline + ) : ( + + New content available, click on reload button to update. + + )} +
+ {needRefresh && ( + + )} + +
+ )} +
+ ) +} diff --git a/client/main.tsx b/client/main.tsx index eb2d7dd..9cfd11a 100644 --- a/client/main.tsx +++ b/client/main.tsx @@ -1,10 +1,13 @@ import React from 'react' import ReactDOM from 'react-dom/client' + import { CollabEditor } from './features/collab-editor.js' +import { ReloadPrompt } from './features/reload-prompt.js' import './main.css' ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + , ) diff --git a/client/types/sw.d.ts b/client/types/sw.d.ts new file mode 100644 index 0000000..f031f5c --- /dev/null +++ b/client/types/sw.d.ts @@ -0,0 +1,14 @@ +declare module 'virtual:pwa-register/react' { + // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error + // @ts-expect-error ignore when react is not installed + import type { Dispatch, SetStateAction } from 'react' + import type { RegisterSWOptions } from 'vite-plugin-pwa/types' + + export type { RegisterSWOptions } + + export function useRegisterSW(options?: RegisterSWOptions): { + needRefresh: [boolean, Dispatch>] + offlineReady: [boolean, Dispatch>] + updateServiceWorker: (reloadPage?: boolean) => Promise + } +} diff --git a/server/plugins/vite-plugin.ts b/server/plugins/vite-plugin.ts index 03ee1b6..75d82a4 100644 --- a/server/plugins/vite-plugin.ts +++ b/server/plugins/vite-plugin.ts @@ -1,9 +1,9 @@ -import { env } from '../env.js' import appRoot from 'app-root-path' import { FastifyInstance } from 'fastify' import Fs from 'node:fs/promises' import Path from 'node:path' import { ViteDevServer } from 'vite' +import { env } from '../env.js' const htmlFilePath = env.NODE_ENV === 'production' @@ -25,6 +25,14 @@ export const vitePlugin = async ( root, prefix: '/assets/', }) + + app.get('/manifest.webmanifest', function (req, reply) { + reply.sendFile('manifest.webmanifest', Path.join(appRoot.path, 'dist')) + }) + app.get('/sw.js', function (req, reply) { + reply.sendFile('sw.js', Path.join(appRoot.path, 'dist')) + }) + app.get('*', async (request, reply) => { const token = reply.generateCsrf() const template = htmlFile.replace('', token) diff --git a/tsconfig.json b/tsconfig.json index 97b94d1..da5fa30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "experimentalDecorators": false, "emitDecoratorMetadata": false, "jsx": "react-jsx", - "types": ["vite/client"], + "types": ["vite/client", "vite-plugin-pwa/react"], "declaration": true, "outDir": "./dist", "removeComments": true,