prasi-bun/app/web/src/nova/ed/logic/ed-sync.tsx

191 lines
5.6 KiB
TypeScript

import { compress, decompress } from "wasm-gzip";
import { deepClone } from "web-utils";
import * as Y from "yjs";
import { clientStartSync } from "../../../utils/sync/ws-client";
import { w } from "../../../utils/types/general";
import { Loading } from "../../../utils/ui/loading";
import { EmptySite, PG } from "./ed-global";
import { treeRebuild } from "./tree/build";
import { evalCJS } from "../../view/logic/load-code-new";
import { reloadPage } from "./ed-route";
import { loadSite } from "./ed-site";
const decoder = new TextDecoder();
export const edInitSync = (p: PG) => {
const session = JSON.parse(
localStorage.getItem("prasi-session") || "null"
) as { data: { user: { id: string; username: string } } };
if (!session) {
navigate("/login");
return <Loading note="logging in" />;
}
p.user.id = session.data.user.id;
p.user.username = session.data.user.username;
if (p.sync) {
if (p.site.id === "--loading--") return false;
if (params.site_id !== p.site.id) {
p.site = deepClone(EmptySite);
p.site.id = "--loading--";
p.ui.popup.code.init = false;
p.sync.site.load(params.site_id).then(async (site) => {
if (site) {
await loadSite(p, site);
p.render();
} else {
alert("Site not found. redirecting...");
location.href = `/ed/`;
}
});
return false;
}
if (!params.page_id && params.site_id) {
db.page
.findFirst({
where: {
is_deleted: false,
is_default_layout: false,
id_site: params.site_id,
},
select: { id: true },
})
.then((e) => {
if (e) navigate(`/ed/${params.site_id}/${e.id}`);
});
return false;
}
}
if (!p.sync) {
clientStartSync({
user_id: session.data.user.id,
site_id: params.site_id,
page_id: params.page_id,
events: {
code(arg) {
p.ui.popup.code.error = false;
if (arg.event === "code-loading") {
p.ui.popup.code.log = "";
p.ui.popup.code.loading = true;
p.render();
} else if (arg.event === "code-done") {
if (typeof arg.content === "string") {
if (arg.content !== "OK") {
p.ui.popup.code.error = true;
}
p.ui.popup.code.log += arg.content;
}
p.ui.popup.code.loading = false;
if (arg.src) {
const w = window as any;
const module = evalCJS(decoder.decode(decompress(arg.src)));
if (typeof module === "object") {
for (const [k, v] of Object.entries(module)) {
w[k] = v;
}
}
}
p.render();
} else {
if (typeof arg.content === "string")
p.ui.popup.code.log += arg.content;
p.render();
}
},
activity(arg) {},
opened() {
if (w.offline) {
console.log("reconnected!");
w.offline = false;
p.ui.syncing = true;
p.sync.activity("site", { type: "join", id: params.site_id });
p.render();
} else {
w.offline = false;
p.render();
}
},
shakehand(client_id) {
p.user.client_id = client_id;
},
disconnected() {
console.log("offline, reconnecting...");
w.offline = true;
p.render();
return {
reconnect: true,
};
},
async editor_start(e) {
if (p.ui.syncing) {
await reloadPage(p, params.page_id, "editor-start");
if (p.page.doc) {
p.page.doc.transact(() => {
p.page.doc?.getMap("map").set("ts", Date.now());
}, `sync`);
}
}
if (params.site_id !== e.site_id || params.page_id !== e.page_id) {
p.site.id = e.site_id;
p.page.cur.id = e.page_id;
navigate(`/ed/${e.site_id}/${e.page_id}`);
} else {
p.site.id = e.site_id;
p.page.cur.id = e.page_id;
p.render();
}
},
site_updated(site) {
for (const [k, v] of Object.entries(site)) {
if (k === "js" || k === "js_compiled") {
p.site[k] = decoder.decode(decompress(v as any));
} else {
(p.site as any)[k] = v;
}
}
p.render();
},
async remote_svlocal(data) {
let doc = null as any;
if (data.type === "page" && p.page.cur.id === data.id) {
doc = p.page.doc as Y.Doc;
} else if (data.type === "comp" && p.comp.list[data.id]) {
doc = p.comp.list[data.id].doc;
}
if (doc) {
const diff_remote = Y.encodeStateAsUpdate(
doc,
decompress(data.sv_local)
);
const sv_remote = Y.encodeStateVector(doc);
const sv = Buffer.from(compress(sv_remote));
const diff = Buffer.from(compress(diff_remote));
const res = await p.sync.yjs.sv_remote(
data.type,
data.id,
sv,
diff
);
if (res) {
Y.applyUpdate(doc, decompress(res.diff), "sv_remote");
await treeRebuild(p, { note: "sv_remote" });
}
}
},
},
}).then((e) => {
p.sync = e;
});
return false;
}
return true;
};