diff --git a/app/web/src/base/page/all.tsx b/app/web/src/base/page/all.tsx index 4f5797f9..6cb6e8e5 100644 --- a/app/web/src/base/page/all.tsx +++ b/app/web/src/base/page/all.tsx @@ -18,7 +18,7 @@ export default page({ } else { navigate("/login"); } - }, []); + }); return ; }, diff --git a/app/web/src/base/page/ed.tsx b/app/web/src/base/page/ed.tsx index cf88e154..86f6db4a 100644 --- a/app/web/src/base/page/ed.tsx +++ b/app/web/src/base/page/ed.tsx @@ -21,8 +21,12 @@ export default page({ user_id: session.data.user.id, events: { editor_start(e) { - if (e.site_id && e.page_id) { - navigate(`/ed/${e.site_id}/${e.page_id}`); + if (params.site_id !== "_" && params.page_id !== "_") { + p.render(); + } else { + if (e.site_id && e.page_id) { + navigate(`/ed/${e.site_id}/${e.page_id}`); + } } }, }, diff --git a/app/web/src/index.tsx b/app/web/src/index.tsx index b2fec886..d105f2fa 100644 --- a/app/web/src/index.tsx +++ b/app/web/src/index.tsx @@ -10,7 +10,7 @@ const start = async () => { let react = { root: null as null | ReactRoot, }; - if (!["localhost", "127.0.0.1"].includes(location.hostname)) { + if (true || !["localhost", "127.0.0.1"].includes(location.hostname)) { const sw = await registerServiceWorker(); navigator.serviceWorker.addEventListener("message", (e) => { if (react.root) { @@ -125,9 +125,11 @@ const start = async () => { const swc = navigator.serviceWorker.controller; if (swc) { - swc.postMessage({ - type: "add-cache", - url: location.href, + [location.href, "", "/", "/ed", "/ed/_/_", "/login"].forEach((url) => { + swc.postMessage({ + type: "add-cache", + url: url, + }); }); if (w.prasiApi && w.prasiApi[base] && w.prasiApi[base].apiEntry) { const routes = Object.entries(w.prasiApi[base].apiEntry).map( diff --git a/app/web/src/utils/sync/client.ts b/app/web/src/utils/sync/client.ts index 9f8336dd..60c688a5 100644 --- a/app/web/src/utils/sync/client.ts +++ b/app/web/src/utils/sync/client.ts @@ -1,17 +1,19 @@ import { DeepProxy } from "@qiwi/deep-proxy"; import { xxhash32 } from "hash-wasm"; -import { UseStore, get } from "idb-keyval"; +import { UseStore, get, set } from "idb-keyval"; import { Packr } from "msgpackr"; import { stringify } from "safe-stable-stringify"; import { SyncActions } from "../../../../srv/ws/sync/actions"; import { SyncActionDefinition } from "../../../../srv/ws/sync/actions-def"; import { initIDB } from "./idb"; import { SyncType } from "../../../../srv/ws/sync/type"; +import { w } from "../types/general"; const packr = new Packr({ structuredClone: true }); const conf = { ws: null as null | WebSocket, client_id: "", idb: null as null | UseStore, + event: null as null | ClientEventObject, }; type User = { @@ -48,7 +50,7 @@ export const clientStartSync = async (arg: { if (path[0] === "then") path.shift(); return (...args: any[]) => new Promise((resolve) => { - operation({ + doAction({ path: path.join("."), resolve, args, @@ -68,52 +70,95 @@ export const clientStartSync = async (arg: { }; const connect = (user_id: string, event: ClientEventObject) => { - return new Promise((resolve) => { - if (!conf.ws) { - const url = new URL(location.href); - url.pathname = "/sync"; - url.protocol = url.protocol === "http:" ? "ws:" : "wss:"; + conf.event = event; + if (w.offline) { + return new Promise(async (resolve) => { + resolve(); + const eventName = "editor_start"; + const data = await loadOfflineMsg("ev", eventName); - const ws = new WebSocket(url.toString()); - conf.ws = ws; - ws.onopen = () => { - ws.send(packr.pack({ type: SyncType.UserID, user_id })); - }; + if (event[eventName]) { + event[eventName](data); + } + }); + } else { + return new Promise((resolve) => { + if (!conf.ws) { + const url = new URL(location.href); + url.pathname = "/sync"; + url.protocol = url.protocol === "http:" ? "ws:" : "wss:"; - ws.onmessage = async (e) => { - const raw = e.data as Blob; - const msg = packr.unpack(Buffer.from(await raw.arrayBuffer())); - if (msg.type === SyncType.ClientID) { - conf.client_id = msg.client_id; - resolve(ws); - } else if (msg.type === SyncType.Event) { - const eventName = msg.event as keyof ClientEventObject; - if (event[eventName]) { - event[eventName](msg.data); + const ws = new WebSocket(url.toString()); + + ws.onopen = () => { + ws.send(packr.pack({ type: SyncType.UserID, user_id })); + conf.ws = ws; + }; + ws.onclose = async () => { + w.offline = true; + if (!conf.ws) { + await connect(user_id, event); + resolve(); } - } - }; - } - }); + }; + ws.onmessage = async (e) => { + const raw = e.data as Blob; + const msg = packr.unpack(Buffer.from(await raw.arrayBuffer())); + if (msg.type === SyncType.ClientID) { + conf.client_id = msg.client_id; + resolve(); + } else if (msg.type === SyncType.Event) { + const eventName = msg.event as ClientEvent; + + if (event[eventName]) { + if (offlineEvents.includes(eventName)) { + saveOfflineMsg("ev", eventName, msg.data); + } + event[eventName](msg.data); + } + } + }; + } + }); + } }; -const operation = async (arg: { +const offlineEvents: ClientEvent[] = ["editor_start"]; +const saveOfflineMsg = async (type: "ev", name: ClientEvent, data: any) => { + const idb = conf.idb; + if (idb) { + const hargs = await xxhash32(`${type}-${name}`); + await set(hargs, data, idb); + } +}; + +const loadOfflineMsg = async (type: "ev", name: ClientEvent) => { + const idb = conf.idb; + if (idb) { + const hargs = await xxhash32(`${type}-${name}`); + return await get(hargs, idb); + } +}; + +const doAction = (arg: { path: string; resolve: (value: any) => void; args: any[]; }) => { - const ws = conf.ws; - const idb = conf.idb; - if (idb) { - const sargs = stringify(arg.args); - const hargs = await xxhash32(`${arg.path}-${sargs}`); + return new Promise(async (resolve) => { + const ws = conf.ws; + const idb = conf.idb; + if (idb) { + const sargs = stringify(arg.args); + const hargs = await xxhash32(`op-${arg.path}-${sargs}`); - if (ws && ws.readyState === ws.OPEN) { - // online - } else { - // offline - const cache = await get(hargs, idb); - console.log(cache); + if (w.offline || (ws && ws.readyState === ws.OPEN)) { + // online + } else { + // offline + const cache = await get(hargs, idb); + resolve(cache as T); + } } - } + }); }; diff --git a/pkgs/web-utils/src/define-window.ts b/pkgs/web-utils/src/define-window.ts index e8552616..01b6191a 100644 --- a/pkgs/web-utils/src/define-window.ts +++ b/pkgs/web-utils/src/define-window.ts @@ -84,6 +84,13 @@ export const defineWindow = async (awaitServerUrl = true) => { if (typeof window === "object") { window.addEventListener("popstate", () => { + const sw = navigator.serviceWorker.controller; + if (sw) { + sw.postMessage({ + type: "add-cache", + url: location.href, + }); + } if (w.preventPopRender) { w.preventPopRender = false; return; diff --git a/undefined/lmdb/user-conf.lmdb-lock b/undefined/lmdb/user-conf.lmdb-lock index aaf8fc45..1969be0b 100644 Binary files a/undefined/lmdb/user-conf.lmdb-lock and b/undefined/lmdb/user-conf.lmdb-lock differ