fix sync
This commit is contained in:
parent
4de34a8a86
commit
b5273222ba
|
|
@ -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"
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@ import { component, site, page } from "dbgen";
|
|||
|
||||
export const SyncActions = {
|
||||
site: {
|
||||
all: () =>
|
||||
list: () =>
|
||||
({}) as Promise<
|
||||
Record<string, { id: string; name: string; domain: string }>
|
||||
>,
|
||||
|
|
@ -11,12 +11,12 @@ export const SyncActions = {
|
|||
load: (id: string) => ({}) as Promise<site>,
|
||||
},
|
||||
comp: {
|
||||
all: () => ({}) as Record<string, Exclude<component, "content_tree">>,
|
||||
list: () => ({}) as Record<string, Exclude<component, "content_tree">>,
|
||||
group: () => ({}) as Record<string, string[]>,
|
||||
doc: (id: string) => ({}) as Uint8Array,
|
||||
},
|
||||
page: {
|
||||
all: (id_site: string) =>
|
||||
list: (id_site: string) =>
|
||||
({}) as Record<string, Exclude<page, "content_tree">>,
|
||||
load: (id: string) => ({}) as Uint8Array,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export * from "./site_load";
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export const site_load = async () => {
|
||||
return "moka";
|
||||
};
|
||||
|
|
@ -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<WSData> = {
|
|||
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),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,4 +2,6 @@ export enum SyncType {
|
|||
ClientID,
|
||||
UserID,
|
||||
Event,
|
||||
Action,
|
||||
ActionResult,
|
||||
}
|
||||
|
|
@ -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 <Loading />;
|
||||
return <Loading note="logging in" />;
|
||||
}
|
||||
|
||||
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 <Loading />;
|
||||
}).then((e) => (p.sync = e));
|
||||
return <Loading note="editor start" />;
|
||||
}
|
||||
|
||||
return <div></div>;
|
||||
if (!p.site.id && p.page.id) return <Loading note="waiting page" />;
|
||||
|
||||
return <Ed />;
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 <Loading />;
|
||||
};
|
||||
|
|
@ -1,5 +1,22 @@
|
|||
import { clientStartSync } from "../../../utils/sync/client";
|
||||
|
||||
export const EDGlobal = {
|
||||
sync: null as unknown as ReturnType<typeof clientStartSync>,
|
||||
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<ReturnType<typeof clientStartSync>>,
|
||||
site: EmptySite,
|
||||
page: EmptyPage,
|
||||
};
|
||||
|
||||
export type PG = typeof EDGlobal & { render: () => void };
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -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<void>(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 = <T>(arg: {
|
||||
const doAction = async <T>(arg: {
|
||||
path: string;
|
||||
code: string;
|
||||
resolve: (value: any) => void;
|
||||
args: any[];
|
||||
}) => {
|
||||
return new Promise<T>(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue