From 559a352bc197bf01f69c6a150db6b562bf92d069 Mon Sep 17 00:00:00 2001 From: thislight Date: Tue, 15 Oct 2024 20:30:08 +0800 Subject: [PATCH] service worker: use injectManifest - a RPC framework is added for further use - fix an error that the service worker is not registered until the settings opened - added theme-color - settings: added a item to indicate the offline availablity --- bun.lockb | Bin 240951 -> 241087 bytes package.json | 5 +- src/App.tsx | 56 +++++++++- src/platform/host.ts | 47 +++++++-- src/serviceworker/main.ts | 35 +++++++ src/serviceworker/services.ts | 177 ++++++++++++++++++++++++++++++++ src/serviceworker/tsconfig.json | 5 + src/settings/Settings.tsx | 15 ++- src/settings/i18n/en.json | 6 +- src/settings/i18n/zh-Hans.json | 6 +- vite.config.ts | 8 +- 11 files changed, 341 insertions(+), 19 deletions(-) create mode 100644 src/serviceworker/main.ts create mode 100644 src/serviceworker/services.ts create mode 100644 src/serviceworker/tsconfig.json diff --git a/bun.lockb b/bun.lockb index 3dc8a47b07cbc6f635b99e9a2042c30951072bd4..b7b99988a877e035a9b6b1cd52af4c89b522cb11 100755 GIT binary patch delta 46052 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 T@(Nt85^`qpM(_N+`~v$wFc7;u delta 45909 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 S0`s)!*0Sp}Ea$4&Md3er<7BM> diff --git a/package.json b/package.json index e5a7f53..8575c58 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "vite-plugin-pwa": "^0.20.5", "vite-plugin-solid": "^2.10.2", "vite-plugin-solid-styled": "^0.11.1", + "workbox-build": "^7.1.1", "wrangler": "^3.78.2" }, "dependencies": { @@ -49,7 +50,9 @@ "solid-js": "^1.8.22", "solid-styled": "^0.11.1", "stacktrace-js": "^2.0.2", - "web-animations-js": "^2.3.2" + "web-animations-js": "^2.3.2", + "workbox-core": "^7.1.0", + "workbox-precaching": "^7.1.0" }, "packageManager": "bun@1.1.21" } diff --git a/src/App.tsx b/src/App.tsx index f532882..6a73749 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { createEffect, createMemo, createRenderEffect, + createSignal, ErrorBoundary, lazy, onCleanup, @@ -17,6 +18,14 @@ import { import { $accounts, updateAcctInf } from "./accounts/stores.js"; import { useStore } from "@nanostores/solid"; import { DateFnScope, useLanguage } from "./platform/i18n.jsx"; +import { useRegisterSW } from "virtual:pwa-register/solid"; +import { + isJSONRPCResult, + ResultDispatcher, + type JSONRPC, +} from "./serviceworker/services.js"; +import { makeEventListener } from "@solid-primitives/event-listener"; +import { ServiceWorkerProvider } from "./platform/host.js"; const AccountSignIn = lazy(() => import("./accounts/SignIn.js")); const AccountMastodonOAuth2Callback = lazy( @@ -58,6 +67,43 @@ const App: Component = () => { const theme = useRootTheme(); const accts = useStore($accounts); const lang = useLanguage(); + const [serviceWorker, setServiceWorker] = createSignal(); + const dispatcher = new ResultDispatcher(); + + let checkAge = 0; + const untilServiceWorkerAlive = async ( + worker: ServiceWorker, + expectedAge: number, + ) => { + const [call, ret] = dispatcher.createTypedCall("ping", undefined); + worker.postMessage(await call); + const result = await ret; + console.assert(!result.error, result); + if (expectedAge === checkAge) { + setServiceWorker(worker); + } + }; + + makeEventListener(window, "message", (event: MessageEvent) => { + if (isJSONRPCResult(event.data)) { + dispatcher.dispatch(event.data.id, event.data); + } + }); + + const { + needRefresh: [needRefresh], + offlineReady: [offlineReady], + } = useRegisterSW({ + onRegisteredSW(scriptUrl, reg) { + console.info("service worker is registered from %s", scriptUrl); + const active = reg?.active; + if (!active) { + console.warn("No service is in activating or activated"); + return; + } + untilServiceWorkerAlive(active, checkAge++); + }, + }); const clients = createMemo(() => { return accts().map((x) => ({ @@ -103,7 +149,15 @@ const App: Component = () => { - + + + diff --git a/src/platform/host.ts b/src/platform/host.ts index f1a2e27..9745742 100644 --- a/src/platform/host.ts +++ b/src/platform/host.ts @@ -1,12 +1,37 @@ +import { createContext, useContext, type Accessor } from "solid-js"; +import { useRegisterSW } from "virtual:pwa-register/solid"; + export function isiOS() { - return [ - 'iPad Simulator', - 'iPhone Simulator', - 'iPod Simulator', - 'iPad', - 'iPhone', - 'iPod' - ].includes(navigator.platform) - // iPad on iOS 13 detection - || (navigator.userAgent.includes("Mac") && "ontouchend" in document) -} \ No newline at end of file + return ( + [ + "iPad Simulator", + "iPhone Simulator", + "iPod Simulator", + "iPad", + "iPhone", + "iPod", + ].includes(navigator.platform) || + // iPad on iOS 13 detection + (navigator.userAgent.includes("Mac") && "ontouchend" in document) + ); +} + +export type ServiceWorkerService = { + needRefresh: Accessor; + offlineReady: Accessor; + serviceWorker: Accessor; +}; + +const ServiceWorkerContext = /* @__PURE__ */ createContext< + ServiceWorkerService +>(({ + needRefresh: () => false, + offlineReady: () => false, + serviceWorker: () => undefined +})); + +export const ServiceWorkerProvider = ServiceWorkerContext.Provider; + +export function useServiceWorker(): ServiceWorkerService { + return useContext(ServiceWorkerContext); +} diff --git a/src/serviceworker/main.ts b/src/serviceworker/main.ts new file mode 100644 index 0000000..457875e --- /dev/null +++ b/src/serviceworker/main.ts @@ -0,0 +1,35 @@ +import { cleanupOutdatedCaches, precacheAndRoute } from "workbox-precaching"; +import { clientsClaim } from "workbox-core"; +import type { AnyCall } from "./services"; + +function checkServiceWorker( + self: WorkerGlobalScope, +): self is ServiceWorkerGlobalScope { + return !!(self as unknown as ServiceWorkerGlobalScope).registration; +} + +if (checkServiceWorker(self)) { + cleanupOutdatedCaches(); + precacheAndRoute(self.__WB_MANIFEST, { + cleanURLs: false + }); + + // auto update + self.skipWaiting(); + clientsClaim(); +} else { + throw new TypeError("This entry point must be run in a service worker"); +} + +self.addEventListener("message", (event: MessageEvent) => { + if (event.data.method === "ping") { + event.source.postMessage({id: event.data.id, jsonrpc: "2.0", result: undefined}) + } else { + event.source.postMessage({ + error: { + code: -32601, + message: "Method not found" + } + }) + } +}) diff --git a/src/serviceworker/services.ts b/src/serviceworker/services.ts new file mode 100644 index 0000000..61f70ec --- /dev/null +++ b/src/serviceworker/services.ts @@ -0,0 +1,177 @@ +export type JSONRPC = { + jsonrpc: "2.0"; + id?: string | number; +}; + +export type Call = JSONRPC & { + method: string; + params: T; +}; + +export type RemoteError = { data: E } & ( + | { + code: number; + message: string; + } + | { + code: -32700; + message: "Parse Error"; + } + | { + code: -32600; + message: "Invalid Request"; + } + | { + code: -32601; + message: "Method not found"; + } + | { + code: -32602; + message: "Invalid params"; + } + | { + code: -32603; + message: "Internal error"; + } +); + +export type Result = JSONRPC & { id: string | number } & ( + | { + result: T; + error: undefined; + } + | { error: RemoteError; result: undefined } + ); + +export function isJSONRPCResult( + object: Record, +): object is Result { + return object["jsonrpc"] === "2.0" && object["id"] && !object["method"]; +} + +export function isJSONRPCCall( + object: Record, +): object is Call { + return object["jsonrpc"] === "2.0" && !!object["method"]; +} + +export class ResultDispatcher { + private map: Map< + number | string, + ((value: Result) => void) | true // `true` = a call is generated, but the promise not created + >; + private nextId: number = Number.MIN_SAFE_INTEGER; + + constructor() { + this.map = new Map(); + } + + private rollId() { + let id = 0; + while (this.map.get((id = this.nextId++))) { + if (this.nextId >= Number.MAX_SAFE_INTEGER) { + this.nextId = Number.MIN_SAFE_INTEGER; + } + } + return id; + } + + createCall( + method: string, + params: T, + ): [Promise>, Promise>] { + const id = this.rollId(); + const p = new Promise>((resolve) => + this.map.set(id, resolve), + ); + this.map.set(id, true); + const call: Call = { + jsonrpc: "2.0", + id, + method, + params, + }; + + return [ + new Promise((resolve) => { + const waitUntilTheIdSet = () => { + // We must do this check to make sure the id is set before the call made. + // or the dispatching may lost the callback + if (this.map.get(id)) { + resolve(call); + } else { + setTimeout(waitUntilTheIdSet, 0); + } + }; + + waitUntilTheIdSet(); + }), + p, + ]; + } + + dispatch(id: string | number, message: Result) { + { + const callback = this.map.get(id); + if (!callback) return; + if (typeof callback !== "boolean") { + callback(message); + this.map.delete(id); + return Promise.resolve(); + } + } + + return new Promise((resolve) => { + let retried = 0; + + const checkAndDispatch = () => { + const callback = this.map.get(id); + if (typeof callback !== "boolean") { + callback(message); + this.map.delete(id); + resolve(); + return; + } + setTimeout(checkAndDispatch, 0); + if (++retried > 3) { + console.warn( + `retried ${retried} time(s) but the callback is still disappeared, id is "${id}"`, + ); + } + }; + + // start the loop + checkAndDispatch(); + }); + } + + createTypedCall< + K extends keyof RequestParams, + P extends RequestParams[K], + R extends ResponseResults[K], + E extends ResponseErrorDatas[K], + >(method: K, params: P): [Promise>, Promise>] { + return this.createCall(method, params) as [ + Promise>, + Promise>, + ]; + } +} + +interface RequestParams { + ping: void; +} + +interface ResponseResults { + ping: void; +} + +interface ResponseErrorDatas { + [key: string]: void; +} + +export type AnyCall = + JSONRPC & { + method: K; + params: RequestParams[K]; + }; diff --git a/src/serviceworker/tsconfig.json b/src/serviceworker/tsconfig.json new file mode 100644 index 0000000..bc88f57 --- /dev/null +++ b/src/serviceworker/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "lib": ["ESNext", "WebWorker"], + }, +} \ No newline at end of file diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 826fea9..8a02813 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -45,6 +45,7 @@ import { } from "../platform/i18n.jsx"; import { type Template } from "@solid-primitives/i18n"; import BottomSheet from "../material/BottomSheet.jsx"; +import { useServiceWorker } from "../platform/host.js"; type Strings = { ["lang.auto"]: Template<{ detected: string }>; @@ -60,9 +61,7 @@ const Settings: ParentComponent = (props) => { ); const navigate = useNavigate(); const settings$ = useStore($settings); - const { - needRefresh: [needRefresh], - } = useRegisterSW(); + const { needRefresh, offlineReady } = useServiceWorker(); const dateFnLocale = useDateFnLocale(); const [profiles] = useSignedInProfiles(); @@ -236,6 +235,16 @@ const Settings: ParentComponent = (props) => { + + + {t("availability")} + + + diff --git a/src/settings/i18n/en.json b/src/settings/i18n/en.json index 98424be..d264cdc 100644 --- a/src/settings/i18n/en.json +++ b/src/settings/i18n/en.json @@ -32,5 +32,9 @@ "motions.gifs": "GIFs", "motions.gifs.autoplay": "Auto-play GIFs", "motions.vids": "Videos", - "motions.vids.autoplay": "Auto-play Videos" + "motions.vids.autoplay": "Auto-play Videos", + + "availability": "Availability", + "availability.offline": "Offline ready", + "availability.online": "Online only" } \ No newline at end of file diff --git a/src/settings/i18n/zh-Hans.json b/src/settings/i18n/zh-Hans.json index 481d57c..8725c46 100644 --- a/src/settings/i18n/zh-Hans.json +++ b/src/settings/i18n/zh-Hans.json @@ -32,5 +32,9 @@ "motions.gifs": "动图", "motions.gifs.autoplay": "自动播放动图", "motions.vids": "视频", - "motions.vids.autoplay": "自动播放视频" + "motions.vids.autoplay": "自动播放视频", + + "availability": "离线可用程度", + "availability.offline": "可以离线使用", + "availability.online": "需要联网使用" } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 7ebb905..bd0b6d1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,10 +16,16 @@ export default defineConfig(({ mode }) => ({ }, }), VitePWA({ + strategies: "injectManifest", registerType: "autoUpdate", devOptions: { - enabled: mode === "staging", + enabled: mode === "staging" || mode === "dev", }, + srcDir: "src/serviceworker", + filename: "main.ts", + manifest: { + theme_color: "#673ab7" + } }), version(), ],