From b5273222ba46a7d03b52182b0b6e78ccbadcee78 Mon Sep 17 00:00:00 2001 From: Rizky Date: Fri, 20 Oct 2023 17:15:20 +0700 Subject: [PATCH] fix sync --- app/srv/ws/sync/actions-def.ts | 12 ++-- app/srv/ws/sync/actions.ts | 6 +- app/srv/ws/sync/actions/index.ts | 1 + app/srv/ws/sync/actions/site_load.ts | 3 + app/srv/ws/sync/sync-handler.ts | 17 ++++++ app/srv/ws/sync/type.ts | 2 + app/web/src/base/page/ed.tsx | 26 +++++++-- app/web/src/index.tsx | 2 +- app/web/src/render/ed/ed.tsx | 12 ++++ app/web/src/render/ed/logic/global.ts | 21 ++++++- app/web/src/render/ed/logic/route.ts | 8 +++ app/web/src/utils/sync/client.ts | 76 ++++++++++++++++++-------- undefined/lmdb/user-conf.lmdb-lock | Bin 8200 -> 8200 bytes 13 files changed, 146 insertions(+), 40 deletions(-) create mode 100644 app/srv/ws/sync/actions/index.ts create mode 100644 app/srv/ws/sync/actions/site_load.ts create mode 100644 app/web/src/render/ed/ed.tsx create mode 100644 app/web/src/render/ed/logic/route.ts diff --git a/app/srv/ws/sync/actions-def.ts b/app/srv/ws/sync/actions-def.ts index 7df0a90e..cf922cff 100644 --- a/app/srv/ws/sync/actions-def.ts +++ b/app/srv/ws/sync/actions-def.ts @@ -1,26 +1,26 @@ export const SyncActionDefinition = { "site": { - "all": "0", + "list": "0", "group": "1", "load": "2" }, "comp": { - "all": "3", + "list": "3", "group": "4", "doc": "5" }, "page": { - "all": "6", + "list": "6", "load": "7" } }; export const SyncActionPaths = { - "0": "site.all", + "0": "site.list", "1": "site.group", "2": "site.load", - "3": "comp.all", + "3": "comp.list", "4": "comp.group", "5": "comp.doc", - "6": "page.all", + "6": "page.list", "7": "page.load" }; \ No newline at end of file diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index 210340e7..6fca0243 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -3,7 +3,7 @@ import { component, site, page } from "dbgen"; export const SyncActions = { site: { - all: () => + list: () => ({}) as Promise< Record >, @@ -11,12 +11,12 @@ export const SyncActions = { load: (id: string) => ({}) as Promise, }, comp: { - all: () => ({}) as Record>, + list: () => ({}) as Record>, group: () => ({}) as Record, doc: (id: string) => ({}) as Uint8Array, }, page: { - all: (id_site: string) => + list: (id_site: string) => ({}) as Record>, load: (id: string) => ({}) as Uint8Array, }, diff --git a/app/srv/ws/sync/actions/index.ts b/app/srv/ws/sync/actions/index.ts new file mode 100644 index 00000000..fde8b50f --- /dev/null +++ b/app/srv/ws/sync/actions/index.ts @@ -0,0 +1 @@ +export * from "./site_load"; diff --git a/app/srv/ws/sync/actions/site_load.ts b/app/srv/ws/sync/actions/site_load.ts new file mode 100644 index 00000000..368a98d3 --- /dev/null +++ b/app/srv/ws/sync/actions/site_load.ts @@ -0,0 +1,3 @@ +export const site_load = async () => { + return "moka"; +}; diff --git a/app/srv/ws/sync/sync-handler.ts b/app/srv/ws/sync/sync-handler.ts index 6d3b626c..cbb0bca0 100644 --- a/app/srv/ws/sync/sync-handler.ts +++ b/app/srv/ws/sync/sync-handler.ts @@ -5,6 +5,8 @@ import { WSData } from "../../../../pkgs/core/server/create"; import { ClientEvent } from "../../../web/src/utils/sync/client"; import { loadUserConf } from "./editor/load"; import { SyncType } from "./type"; +import { SyncActionPaths } from "./actions-def"; +import * as actions from "./actions/index"; const packr = new Packr({ structuredClone: true }); const conns = new Map< @@ -56,6 +58,21 @@ export const syncHandler: WebSocketHandler = { data: conf, }); } + if (msg.type === SyncType.Action) { + const code = msg.code as keyof typeof SyncActionPaths; + const actionName = SyncActionPaths[code].replace(/\./gi, "_"); + if (actionName) { + const action = (actions as any)[actionName]; + + ws.sendBinary( + packr.pack({ + type: SyncType.ActionResult, + argid: msg.argid, + val: await action(...msg.args), + }) + ); + } + } } } }, diff --git a/app/srv/ws/sync/type.ts b/app/srv/ws/sync/type.ts index 74c80749..75cd976d 100644 --- a/app/srv/ws/sync/type.ts +++ b/app/srv/ws/sync/type.ts @@ -2,4 +2,6 @@ export enum SyncType { ClientID, UserID, Event, + Action, + ActionResult, } \ No newline at end of file diff --git a/app/web/src/base/page/ed.tsx b/app/web/src/base/page/ed.tsx index 86f6db4a..f6436031 100644 --- a/app/web/src/base/page/ed.tsx +++ b/app/web/src/base/page/ed.tsx @@ -2,6 +2,7 @@ import { page, useGlobal } from "web-utils"; import { EDGlobal } from "../../render/ed/logic/global"; import { clientStartSync } from "../../utils/sync/client"; import { Loading } from "../../utils/ui/loading"; +import { Ed } from "../../render/ed/ed"; export default page({ url: "/ed/:site_id/:page_id", @@ -13,15 +14,22 @@ export default page({ ) as { data: { user: { id: string } } }; if (!session) { navigate("/login"); - return ; + return ; } if (!p.sync) { - p.sync = clientStartSync({ + clientStartSync({ user_id: session.data.user.id, events: { editor_start(e) { - if (params.site_id !== "_" && params.page_id !== "_") { + if ( + !!params.site_id && + !!params.page_id && + params.site_id !== "_" && + params.page_id !== "_" + ) { + p.site.id = params.site_id; + p.page.id = params.page_id; p.render(); } else { if (e.site_id && e.page_id) { @@ -29,11 +37,17 @@ export default page({ } } }, + site_loaded({ site }) { + p.site = site; + p.render(); + }, }, - }); - return ; + }).then((e) => (p.sync = e)); + return ; } - return
; + if (!p.site.id && p.page.id) return ; + + return ; }, }); diff --git a/app/web/src/index.tsx b/app/web/src/index.tsx index d105f2fa..a816efcd 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 (true || !["localhost", "127.0.0.1"].includes(location.hostname)) { + if (!["localhost", "127.0.0.1"].includes(location.hostname)) { const sw = await registerServiceWorker(); navigator.serviceWorker.addEventListener("message", (e) => { if (react.root) { diff --git a/app/web/src/render/ed/ed.tsx b/app/web/src/render/ed/ed.tsx new file mode 100644 index 00000000..0576a00b --- /dev/null +++ b/app/web/src/render/ed/ed.tsx @@ -0,0 +1,12 @@ +import { useGlobal } from "web-utils"; +import { Loading } from "../../utils/ui/loading"; +import { EDGlobal } from "./logic/global"; +import { edRoute } from "./logic/route"; + +export const Ed = () => { + const p = useGlobal(EDGlobal, "EDITOR"); + + edRoute(p); + + return ; +}; diff --git a/app/web/src/render/ed/logic/global.ts b/app/web/src/render/ed/logic/global.ts index d684de82..dfe0c341 100644 --- a/app/web/src/render/ed/logic/global.ts +++ b/app/web/src/render/ed/logic/global.ts @@ -1,5 +1,22 @@ import { clientStartSync } from "../../../utils/sync/client"; -export const EDGlobal = { - sync: null as unknown as ReturnType, +const EmptySite = { + id: "", + name: "", + domain: "", + js: "", + js_compiled: "", + config: { api_url: "" }, }; +export type ESite = typeof EmptySite; +const EmptyPage = { + id: "", +}; + +export const EDGlobal = { + sync: null as unknown as Awaited>, + site: EmptySite, + page: EmptyPage, +}; + +export type PG = typeof EDGlobal & { render: () => void }; diff --git a/app/web/src/render/ed/logic/route.ts b/app/web/src/render/ed/logic/route.ts new file mode 100644 index 00000000..d8af2a14 --- /dev/null +++ b/app/web/src/render/ed/logic/route.ts @@ -0,0 +1,8 @@ +import { PG } from "./global"; + +export const edRoute = async (p: PG) => { + if (!p.site.domain && !p.site.name) { + const res = await p.sync.site.load(p.site.id); + console.log(res); + } +}; diff --git a/app/web/src/utils/sync/client.ts b/app/web/src/utils/sync/client.ts index 60c688a5..c22715c6 100644 --- a/app/web/src/utils/sync/client.ts +++ b/app/web/src/utils/sync/client.ts @@ -8,6 +8,7 @@ import { SyncActionDefinition } from "../../../../srv/ws/sync/actions-def"; import { initIDB } from "./idb"; import { SyncType } from "../../../../srv/ws/sync/type"; import { w } from "../types/general"; +import { ESite } from "../../render/ed/logic/global"; const packr = new Packr({ structuredClone: true }); const conf = { ws: null as null | WebSocket, @@ -16,6 +17,15 @@ const conf = { event: null as null | ClientEventObject, }; +const runtime = { + action: { + pending: {} as Record< + string, + { ts: number; resolve: (value: any) => void } + >, + }, +}; + type User = { id: string; name: string; @@ -32,6 +42,7 @@ export const clientStartSync = async (arg: { site_id?: string; page_id?: string; }) => void; + site_loaded: (arg: { site: ESite }) => void; }; }) => { const { user_id, events } = arg; @@ -47,11 +58,17 @@ export const clientStartSync = async (arg: { path.push(key); if (typeof value === "string") { - if (path[0] === "then") path.shift(); + for (let i = 0; i < path.length; i++) { + if (path[i] !== "then") { + path.splice(0, i); + break; + } + } return (...args: any[]) => new Promise((resolve) => { doAction({ path: path.join("."), + code: value, resolve, args, }); @@ -75,7 +92,7 @@ const connect = (user_id: string, event: ClientEventObject) => { return new Promise(async (resolve) => { resolve(); const eventName = "editor_start"; - const data = await loadOfflineMsg("ev", eventName); + const data = await loadEventOffline(eventName); if (event[eventName]) { event[eventName](data); @@ -112,10 +129,20 @@ const connect = (user_id: string, event: ClientEventObject) => { if (event[eventName]) { if (offlineEvents.includes(eventName)) { - saveOfflineMsg("ev", eventName, msg.data); + saveEventOffline(eventName, msg.data); } event[eventName](msg.data); } + } else if (msg.type === SyncType.ActionResult) { + const pending = runtime.action.pending[msg.argid]; + if (pending) { + delete runtime.action.pending[msg.argid]; + const idb = conf.idb; + if (idb) { + await set(msg.argid, msg.val, idb); + } + pending.resolve(msg.val); + } } }; } @@ -124,41 +151,46 @@ const connect = (user_id: string, event: ClientEventObject) => { }; const offlineEvents: ClientEvent[] = ["editor_start"]; -const saveOfflineMsg = async (type: "ev", name: ClientEvent, data: any) => { +const saveEventOffline = async (name: ClientEvent, data: any) => { const idb = conf.idb; if (idb) { - const hargs = await xxhash32(`${type}-${name}`); + const hargs = await xxhash32(`ev-${name}`); await set(hargs, data, idb); } }; -const loadOfflineMsg = async (type: "ev", name: ClientEvent) => { +const loadEventOffline = async (name: ClientEvent) => { const idb = conf.idb; if (idb) { - const hargs = await xxhash32(`${type}-${name}`); + const hargs = await xxhash32(`ev-${name}`); return await get(hargs, idb); } }; -const doAction = (arg: { +const doAction = async (arg: { path: string; + code: string; resolve: (value: any) => void; args: any[]; }) => { - 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}`); + const { path, args, code, resolve } = arg; + const ws = conf.ws; + const idb = conf.idb; + if (idb) { + const sargs = stringify(args); + const argid = await xxhash32(`op-${path}-${sargs}`); - if (w.offline || (ws && ws.readyState === ws.OPEN)) { - // online - } else { - // offline - const cache = await get(hargs, idb); - resolve(cache as T); - } + if (ws && ws.readyState === ws.OPEN) { + // online + runtime.action.pending[argid] = { + ts: Date.now(), + resolve, + }; + ws.send(packr.pack({ type: SyncType.Action, code, args, argid })); + } else { + // offline + const cache = await get(argid, idb); + resolve(cache as T); } - }); + } }; diff --git a/undefined/lmdb/user-conf.lmdb-lock b/undefined/lmdb/user-conf.lmdb-lock index 1969be0b06bf8c636953d2a5d416cbc61e2d31c4..55179533715c3f571bc28a9bcb041a7bf02eb937 100644 GIT binary patch delta 40 mcmeBh=x~@I#%MQjVWH@MD3DEKfPl&B&lxu^OqZW%zybh!iV)}k delta 40 kcmeBh=x~@I#%MEfVWH@MC}4nr$?DG;H!e(1W0C92