This commit is contained in:
Rizky 2023-10-20 17:15:20 +07:00
parent 4de34a8a86
commit b5273222ba
13 changed files with 146 additions and 40 deletions

View File

@ -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"
};

View File

@ -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,
},

View File

@ -0,0 +1 @@
export * from "./site_load";

View File

@ -0,0 +1,3 @@
export const site_load = async () => {
return "moka";
};

View File

@ -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),
})
);
}
}
}
}
},

View File

@ -2,4 +2,6 @@ export enum SyncType {
ClientID,
UserID,
Event,
Action,
ActionResult,
}

View File

@ -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 />;
},
});

View File

@ -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) {

View File

@ -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 />;
};

View File

@ -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 };

View File

@ -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);
}
};

View File

@ -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.