This commit is contained in:
Rizky 2023-10-21 22:23:22 +07:00
parent 7ba2c91dc0
commit 55a6b2564c
12 changed files with 193 additions and 35 deletions

View File

@ -9,8 +9,11 @@ import { svLocal } from "./edit/action/sv-local";
import { svdiffRemote } from "./edit/action/svdiff-remote"; import { svdiffRemote } from "./edit/action/svdiff-remote";
import { redo, undo } from "./edit/action/undo-redo"; import { redo, undo } from "./edit/action/undo-redo";
import { eg } from "./edit/edit-global"; import { eg } from "./edit/edit-global";
import { sendWS } from "./edit/send";import { syncHandler } from "./sync/sync-handler"; import { sendWS } from "./edit/send";
import { syncHandler } from "./sync/sync-handler";
import * as Y from "yjs";
(globalThis as any).Y = Y;
eg.edit = { eg.edit = {
site: {}, site: {},
comp: {}, comp: {},

View File

@ -7,7 +7,7 @@ export const SyncActionDefinition = {
"comp": { "comp": {
"list": "3", "list": "3",
"group": "4", "group": "4",
"doc": "5" "load": "5"
}, },
"page": { "page": {
"list": "6", "list": "6",
@ -20,7 +20,7 @@ export const SyncActionPaths = {
"2": "site.load", "2": "site.load",
"3": "comp.list", "3": "comp.list",
"4": "comp.group", "4": "comp.group",
"5": "comp.doc", "5": "comp.load",
"6": "page.list", "6": "page.list",
"7": "page.load" "7": "page.load"
}; };

View File

@ -1,5 +1,5 @@
import { component, page } from "dbgen"; import { component, page } from "dbgen";
import { EPage, ESite } from "../../../web/src/render/ed/logic/ed-global"; import { EComp, EPage, ESite } from "../../../web/src/render/ed/logic/ed-global";
/* /*
WARNING: WARNING:
@ -20,7 +20,7 @@ export const SyncActions = {
comp: { comp: {
list: () => ({}) as Record<string, Exclude<component, "content_tree">>, list: () => ({}) as Record<string, Exclude<component, "content_tree">>,
group: () => ({}) as Record<string, string[]>, group: () => ({}) as Record<string, string[]>,
doc: (id: string) => ({}) as Uint8Array, load: async (id: string) => ({}) as EComp | void,
}, },
page: { page: {
list: (id_site: string) => list: (id_site: string) =>

View File

@ -0,0 +1,49 @@
import { syncronize } from "y-pojo";
import { docs } from "../entity/docs";
import { snapshot } from "../entity/snapshot";
import { ActionCtx } from "../type";
export const comp_load = async function (this: ActionCtx, id: string) {
let snap = snapshot.get("comp", id);
let ydoc = docs.comp[id];
if (!snap || !ydoc) {
const comp = await db.component.findFirst({ where: { id } });
if (comp) {
const doc = new Y.Doc();
let root = doc.getMap("map");
syncronize(root, { id, item: comp.content_tree });
const um = new Y.UndoManager(root, { ignoreRemoteMapChanges: true });
docs.comp[id] = {
doc: doc as any,
id,
um,
};
const bin = Y.encodeStateAsUpdate(doc);
snapshot.set({
bin,
id,
type: "comp",
name: comp.name,
url: "",
ts: Date.now(),
});
return {
id: id,
name: comp.name,
snapshot: bin,
};
}
}
if (snap) {
return {
id: snap.id,
name: snap.name,
snapshot: snap.bin,
};
}
};

View File

@ -1,3 +1,4 @@
export * from "./site_load"; export * from "./site_load";
export * from "./site_group"; export * from "./site_group";
export * from "./page_load"; export * from "./page_load";
export * from "./comp_load";

View File

@ -8,10 +8,10 @@ export const page_load: SAction["page"]["load"] = async function (
this: ActionCtx, this: ActionCtx,
id: string id: string
) { ) {
let ss = snapshot.get("page", id); let snap = snapshot.get("page", id);
let ydoc = docs.page[id]; let ydoc = docs.page[id];
if (!ss || !ydoc) { if (!snap || !ydoc) {
const page = await db.page.findFirst({ where: { id } }); const page = await db.page.findFirst({ where: { id } });
if (page) { if (page) {
const doc = new Y.Doc(); const doc = new Y.Doc();
@ -44,12 +44,12 @@ export const page_load: SAction["page"]["load"] = async function (
} }
} }
if (ss) { if (snap) {
return { return {
id: ss.id, id: snap.id,
url: ss.url, url: snap.url,
name: ss.name, name: snap.name,
snapshot: ss.bin, snapshot: snap.bin,
}; };
} }
}; };

View File

@ -8,7 +8,7 @@ const emptySnapshot = {
bin: new Uint8Array(), bin: new Uint8Array(),
url: "", url: "",
name: "", name: "",
ts: Date.now(), ts: Date.now()
}; };
export type DocSnapshot = typeof emptySnapshot; export type DocSnapshot = typeof emptySnapshot;

View File

@ -81,7 +81,7 @@ export const syncHandler: WebSocketHandler<WSData> = {
if (actionName) { if (actionName) {
const baseAction = (actions as any)[actionName]; const baseAction = (actions as any)[actionName];
if (!baseAction) { if (!baseAction) {
console.log(`app/ws/edit/sync/${actionName}.ts not found}`); console.log(`app/ws/edit/sync/${actionName}.ts not found`);
} }
if (baseAction) { if (baseAction) {
const action = baseAction.bind({ const action = baseAction.bind({

View File

@ -2,9 +2,8 @@ import { NodeModel } from "@minoru/react-dnd-treeview";
import { clientStartSync } from "../../../utils/sync/client"; import { clientStartSync } from "../../../utils/sync/client";
import { IContent, MContent } from "../../../utils/types/general"; import { IContent, MContent } from "../../../utils/types/general";
import { IItem, MItem } from "../../../utils/types/item"; import { IItem, MItem } from "../../../utils/types/item";
import { DPage, IRoot } from "../../../utils/types/root"; import { DComp, DPage, IRoot } from "../../../utils/types/root";
import { IText, MText } from "../../../utils/types/text"; import { IText, MText } from "../../../utils/types/text";
import { NodeMeta } from "../../editor/logic/global";
const EmptySite = { const EmptySite = {
id: "", id: "",
@ -16,6 +15,7 @@ const EmptySite = {
}; };
export type ESite = typeof EmptySite; export type ESite = typeof EmptySite;
export type EPage = typeof EmptyPage; export type EPage = typeof EmptyPage;
export type EComp = typeof EmptyComp;
const EmptyPage = { const EmptyPage = {
id: "", id: "",
@ -24,9 +24,18 @@ const EmptyPage = {
snapshot: null as null | Uint8Array, snapshot: null as null | Uint8Array,
}; };
const EmptyComp = {
id: "",
snapshot: null as null | Uint8Array,
};
export type EdMeta = { export type EdMeta = {
item: IItem | IText; item: IItem | IText;
mitem?: MItem | MText; mitem?: MItem | MText;
parent_comp?: {
ref_ids: Record<string, string>;
mcomp: MItem;
};
}; };
export const EDGlobal = { export const EDGlobal = {
@ -39,12 +48,19 @@ export const EDGlobal = {
sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>, sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>,
site: EmptySite, site: EmptySite,
page: { page: {
current: EmptyPage, cur: EmptyPage,
doc: null as null | DPage, doc: null as null | DPage,
root: null as null | IRoot, root: null as null | IRoot,
entry: [] as string[], entry: [] as string[],
tree: [] as NodeModel<EdMeta>[], tree: [] as NodeModel<EdMeta>[],
meta: {} as Record<string, { item: IContent; mitem?: MContent }>, meta: {} as Record<string, { item: IContent; mitem?: MContent }>,
list: {} as Record<string, EPage>,
},
comp: {
cur: EmptyComp,
doc: null as null | DComp,
item: null as null | IItem,
list: {} as Record<string, { cur: EComp; doc: DComp }>,
}, },
}; };

View File

@ -16,7 +16,7 @@ export const edRoute = async (p: PG) => {
p.site = site; p.site = site;
} }
if (p.page.current.id !== params.page_id || !p.page.current.snapshot) { if (p.page.cur.id !== params.page_id || !p.page.cur.snapshot) {
p.status = "loading"; p.status = "loading";
const page = await p.sync.page.load(params.page_id); const page = await p.sync.page.load(params.page_id);
@ -26,7 +26,7 @@ export const edRoute = async (p: PG) => {
return; return;
} }
p.page.current = page; p.page.cur = page;
if (page.snapshot) { if (page.snapshot) {
const doc = new Y.Doc(); const doc = new Y.Doc();
Y.applyUpdate(doc, page.snapshot); Y.applyUpdate(doc, page.snapshot);

View File

@ -1,7 +1,8 @@
import { createId } from "@paralleldrive/cuid2";
import { MContent } from "../../../../utils/types/general";
import { IItem, MItem } from "../../../../utils/types/item"; import { IItem, MItem } from "../../../../utils/types/item";
import { MRoot } from "../../../../utils/types/root"; import { DComp } from "../../../../utils/types/root";
import { MSection } from "../../../../utils/types/section"; import { MSection } from "../../../../utils/types/section";
import { walk } from "../../../editor/logic/tree-logic";
import { EdMeta, PG } from "../ed-global"; import { EdMeta, PG } from "../ed-global";
export const treeRebuild = async (p: PG) => { export const treeRebuild = async (p: PG) => {
@ -24,13 +25,7 @@ export const treeRebuild = async (p: PG) => {
} }
}; };
const walkMap = async ( const mapItem = (mitem: MContent, item: any) => {
p: PG,
arg: { mitem: MItem | MSection; tree_parent_id: string }
) => {
const { mitem, tree_parent_id } = arg;
const item = {} as unknown as IItem;
mitem.forEach((e, k) => { mitem.forEach((e, k) => {
if (k !== "childs") { if (k !== "childs") {
let val = e; let val = e;
@ -39,15 +34,104 @@ const walkMap = async (
val = e.toJSON() as any; val = e.toJSON() as any;
} }
} }
(item as any)[k] = val; item[k] = val;
} else { } else {
item[k] = []; item[k] = [];
} }
}); });
};
const walkMap = async (
p: PG,
arg: {
mitem: MItem | MSection;
tree_parent_id: string;
parent_comp?: EdMeta["parent_comp"];
}
) => {
const { mitem, tree_parent_id, parent_comp } = arg;
const item = {} as unknown as IItem;
mapItem(mitem, item);
// sesuaikan item instance id dengan parent comp
if (parent_comp) {
if (!parent_comp["ref_ids"][item.id]) {
parent_comp["ref_ids"][item.id] = createId();
}
if (parent_comp["ref_ids"][item.id]) {
item.id = parent_comp["ref_ids"][item.id];
}
}
const metaNotFound = () => {
p.page.tree.push({
id: item.id,
parent: tree_parent_id,
text: item.name,
});
};
const item_comp = item.component;
if (item_comp && item_comp.id) {
if (!p.comp.list[item_comp.id]) {
let found = false;
const cur = await p.sync.comp.load(item_comp.id);
if (cur && cur.snapshot) {
const doc = new Y.Doc() as DComp;
if (cur.snapshot) {
Y.applyUpdate(doc as any, cur.snapshot);
p.comp.list[item_comp.id] = { cur, doc };
found = true;
}
}
if (!found) {
metaNotFound();
return;
}
}
const ref_comp = p.comp.list[item_comp.id];
if (ref_comp) {
const mcomp = ref_comp.doc.getMap("map").get("item");
if (mcomp) {
const ref_ids: Record<string, string> = {};
if (parent_comp) {
let old_id = item.id;
mapItem(mcomp, item);
ref_ids[item.id] = old_id;
item.id = old_id;
} else {
mapItem(mcomp, item);
ref_ids[item.id] = createId();
item.id = ref_ids[item.id];
}
await Promise.all(
mcomp.get("childs")?.map(async (e) => {
await walkMap(p, {
mitem: e,
tree_parent_id: item.id,
parent_comp: { ref_ids, mcomp },
});
}) || []
);
return;
}
}
metaNotFound();
return;
}
const meta: EdMeta = { const meta: EdMeta = {
item, item,
mitem: mitem as MItem, mitem: mitem as MItem,
parent_comp,
}; };
p.page.meta[item.id] = meta; p.page.meta[item.id] = meta;
@ -59,10 +143,13 @@ const walkMap = async (
data: meta, data: meta,
}); });
const mchilds = mitem.get("childs");
if (mchilds) {
await Promise.all( await Promise.all(
mitem.get("childs")?.map(async (e, k) => { mchilds.map(async (e, k) => {
item.childs.push(e.get("id")); item.childs.push(e.get("id"));
await walkMap(p, { mitem: e, tree_parent_id: item.id }); await walkMap(p, { mitem: e, tree_parent_id: item.id });
}) || [] }) || []
); );
}
}; };

View File

@ -1,5 +1,6 @@
import { TypedArray, TypedDoc, TypedMap } from "yjs-types"; import { TypedArray, TypedDoc, TypedMap } from "yjs-types";
import { ISection } from "./section"; import { ISection } from "./section";
import { MItem } from "./item";
export type IRoot = { export type IRoot = {
id: "root"; id: "root";
@ -14,3 +15,4 @@ export type MRoot = TypedMap<{
}>; }>;
export type DPage = TypedDoc<{ map: TypedMap<{ id: string; root: MRoot }> }>; export type DPage = TypedDoc<{ map: TypedMap<{ id: string; root: MRoot }> }>;
export type DComp = TypedDoc<{ map: TypedMap<{ id: string; item: MItem }> }>;