From 8f5c20beebc52536b170bbd846f753d2b634d127 Mon Sep 17 00:00:00 2001 From: Rizky Date: Tue, 24 Oct 2023 09:18:22 +0700 Subject: [PATCH] fix --- app/srv/ws/sync/actions-def.ts | 42 +++++++-------- app/srv/ws/sync/actions.ts | 20 ++++--- app/srv/ws/sync/actions/comp_redo.ts | 3 -- app/srv/ws/sync/actions/comp_undo.ts | 3 -- app/srv/ws/sync/actions/index.ts | 5 +- app/srv/ws/sync/actions/page_load.ts | 53 +++++++++++-------- app/srv/ws/sync/actions/page_redo.ts | 14 ----- app/srv/ws/sync/actions/page_undo.ts | 15 ------ app/srv/ws/sync/actions/yjs_diff_local.ts | 3 -- app/srv/ws/sync/actions/yjs_um.ts | 27 ++++++++++ app/srv/ws/sync/entity/docs.ts | 8 +++ app/srv/ws/sync/entity/snapshot.ts | 3 +- app/web/src/render/ed/logic/ed-global.ts | 18 +++++-- app/web/src/render/ed/logic/ed-undo.ts | 13 +++-- .../ed/panel/tree/node/item/action/clone.tsx | 16 +++++- .../ed/panel/tree/node/item/ctx-menu.tsx | 2 +- 16 files changed, 134 insertions(+), 111 deletions(-) delete mode 100644 app/srv/ws/sync/actions/comp_redo.ts delete mode 100644 app/srv/ws/sync/actions/comp_undo.ts delete mode 100644 app/srv/ws/sync/actions/page_redo.ts delete mode 100644 app/srv/ws/sync/actions/page_undo.ts create mode 100644 app/srv/ws/sync/actions/yjs_um.ts diff --git a/app/srv/ws/sync/actions-def.ts b/app/srv/ws/sync/actions-def.ts index 8c175c3b..cb201a5c 100644 --- a/app/srv/ws/sync/actions-def.ts +++ b/app/srv/ws/sync/actions-def.ts @@ -5,38 +5,32 @@ export const SyncActionDefinition = { "load": "2" }, "comp": { - "undo": "3", - "redo": "4", - "list": "5", - "group": "6", - "load": "7" + "list": "3", + "group": "4", + "load": "5" }, "page": { - "undo": "8", - "redo": "9", - "list": "10", - "load": "11" + "list": "6", + "load": "7" }, "yjs": { - "sv_local": "12", - "diff_local": "13", - "sv_remote": "14" + "um": "8", + "sv_local": "9", + "diff_local": "10", + "sv_remote": "11" } }; export const SyncActionPaths = { "0": "site.list", "1": "site.group", "2": "site.load", - "3": "comp.undo", - "4": "comp.redo", - "5": "comp.list", - "6": "comp.group", - "7": "comp.load", - "8": "page.undo", - "9": "page.redo", - "10": "page.list", - "11": "page.load", - "12": "yjs.sv_local", - "13": "yjs.diff_local", - "14": "yjs.sv_remote" + "3": "comp.list", + "4": "comp.group", + "5": "comp.load", + "6": "page.list", + "7": "page.load", + "8": "yjs.um", + "9": "yjs.sv_local", + "10": "yjs.diff_local", + "11": "yjs.sv_remote" }; diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index 56be8694..e0bd6d04 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -22,29 +22,33 @@ export const SyncActions = { load: async (id: string) => ({}) as ESite | void, }, comp: { - undo: async (id_comp: string) => {}, - redo: async (id_comp: string) => {}, list: () => ({}) as Record>, group: () => ({}) as Record, load: async (id: string) => ({}) as EComp | void, }, page: { - undo: async (id_page: string) => {}, - redo: async (id_page: string) => {}, list: (id_site: string) => ({}) as Record>, load: async (id: string) => ({}) as EPage | void, }, yjs: { - sv_local: async (mode: "page" | "comp", id: string, bin: Uint8Array) => - ({}) as { diff: Uint8Array; sv: Uint8Array } | void, + um: async ( + mode: "page" | "comp" | "site", + action: "undo" | "redo", + id: string + ) => {}, + sv_local: async ( + mode: "page" | "comp" | "site", + id: string, + bin: Uint8Array + ) => ({}) as { diff: Uint8Array; sv: Uint8Array } | void, diff_local: async ( - mode: "page" | "comp", + mode: "page" | "comp" | "site", id: string, bin: Uint8Array ) => {}, sv_remote: async ( - mode: "page" | "comp", + mode: "page" | "comp" | "site", id: string, sv: Uint8Array, diff: Uint8Array diff --git a/app/srv/ws/sync/actions/comp_redo.ts b/app/srv/ws/sync/actions/comp_redo.ts deleted file mode 100644 index ecd2ad01..00000000 --- a/app/srv/ws/sync/actions/comp_redo.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SyncConnection } from "../type"; - -export const comp_redo = async function (this: SyncConnection, id: string) {}; diff --git a/app/srv/ws/sync/actions/comp_undo.ts b/app/srv/ws/sync/actions/comp_undo.ts deleted file mode 100644 index 871f1250..00000000 --- a/app/srv/ws/sync/actions/comp_undo.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SyncConnection } from "../type"; - -export const comp_undo = async function (this: SyncConnection, id: string) {}; diff --git a/app/srv/ws/sync/actions/index.ts b/app/srv/ws/sync/actions/index.ts index 897e9cb6..ff296e90 100644 --- a/app/srv/ws/sync/actions/index.ts +++ b/app/srv/ws/sync/actions/index.ts @@ -2,10 +2,7 @@ export * from "./site_load"; export * from "./site_group"; export * from "./page_load"; export * from "./comp_load"; -export * from "./page_undo"; -export * from "./page_redo"; -export * from "./comp_undo"; -export * from "./comp_redo"; +export * from "./yjs_um"; export * from "./yjs_sv_local"; export * from "./yjs_diff_local"; export * from "./yjs_sv_remote"; diff --git a/app/srv/ws/sync/actions/page_load.ts b/app/srv/ws/sync/actions/page_load.ts index a849d791..5aac23b2 100644 --- a/app/srv/ws/sync/actions/page_load.ts +++ b/app/srv/ws/sync/actions/page_load.ts @@ -1,12 +1,12 @@ import { syncronize } from "y-pojo"; import { SAction } from "../actions"; +import { conns } from "../entity/conn"; import { Y, docs } from "../entity/docs"; import { snapshot } from "../entity/snapshot"; import { user } from "../entity/user"; import { gzipAsync } from "../entity/zlib"; -import { SyncConnection, SyncType } from "../type"; -import { conns } from "../entity/conn"; import { sendWS } from "../sync-handler"; +import { SyncConnection, SyncType } from "../type"; export const page_load: SAction["page"]["load"] = async function ( this: SyncConnection, @@ -17,27 +17,34 @@ export const page_load: SAction["page"]["load"] = async function ( if (this.conf) this.conf.page_id = id; - const createUndoManager = (root: Y.Map) => { - const um = new Y.UndoManager(root, { ignoreRemoteMapChanges: false }); + const createUndoManager = async (root: Y.Map) => { + const um = new Y.UndoManager(root, { + ignoreRemoteMapChanges: true, + }); + return um; }; - const attachOnUpdate = (doc: Y.Doc, um: Y.UndoManager) => { + const attachOnUpdate = async (doc: Y.Doc, um: Y.UndoManager) => { + snapshot.set("page", id, "id_doc", um.doc.clientID); + doc.on("update", async (update: Uint8Array, origin: any) => { - if (origin === um) { - } else { - const sv_local = await gzipAsync(update); - user.active.findAll({ page_id: id }).map((e) => { + const bin = Y.encodeStateAsUpdate(doc); + snapshot.set("page", id, "bin", bin); + + const sv_local = await gzipAsync(update); + user.active.findAll({ page_id: id }).map((e) => { + if (origin !== um) { if (e.client_id === origin) return; - const ws = conns.get(e.client_id)?.ws; - if (ws) - sendWS(ws, { - type: SyncType.Event, - event: "remote_svlocal", - data: { type: "page", sv_local, id }, - }); - }); - } + } + const ws = conns.get(e.client_id)?.ws; + if (ws) + sendWS(ws, { + type: SyncType.Event, + event: "remote_svlocal", + data: { type: "page", sv_local, id }, + }); + }); }); }; @@ -52,7 +59,7 @@ export const page_load: SAction["page"]["load"] = async function ( let root = doc.getMap("map"); syncronize(root, { id, root: page.content_tree }); - const um = createUndoManager(root); + const um = await createUndoManager(root); docs.page[id] = { doc: doc as any, id, @@ -60,7 +67,7 @@ export const page_load: SAction["page"]["load"] = async function ( }; const bin = Y.encodeStateAsUpdate(doc); - attachOnUpdate(doc, um); + await attachOnUpdate(doc, um); snapshot.update({ bin, @@ -69,6 +76,7 @@ export const page_load: SAction["page"]["load"] = async function ( name: page.name, ts: Date.now(), url: page.url, + id_doc: doc.clientID, id_site: page.id_site, }); @@ -89,11 +97,12 @@ export const page_load: SAction["page"]["load"] = async function ( } } else if (snap && !ydoc) { const doc = new Y.Doc(); + snapshot.set("page", id, "id_doc", doc.clientID); Y.applyUpdate(doc, snap.bin); let root = doc.getMap("map"); - const um = createUndoManager(root); - attachOnUpdate(doc, um); + const um = await createUndoManager(root); + await attachOnUpdate(doc, um); docs.page[id] = { doc: doc as any, diff --git a/app/srv/ws/sync/actions/page_redo.ts b/app/srv/ws/sync/actions/page_redo.ts deleted file mode 100644 index a5e78b94..00000000 --- a/app/srv/ws/sync/actions/page_redo.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { docs } from "../entity/docs"; -import { SyncConnection } from "../type"; - -export const page_redo = async function (this: SyncConnection, id: string) { - if (!docs.page[id]) { - return; - } - - const um = docs.page[id].um; - if (um.canRedo()) { - console.log("redoing"); - um.redo(); - } -}; diff --git a/app/srv/ws/sync/actions/page_undo.ts b/app/srv/ws/sync/actions/page_undo.ts deleted file mode 100644 index 65ee9bea..00000000 --- a/app/srv/ws/sync/actions/page_undo.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { docs } from "../entity/docs"; -import { SyncConnection } from "../type"; - -export const page_undo = async function (this: SyncConnection, id: string) { - if (!docs.page[id]) { - return; - } - - const um = docs.page[id].um; - if (um.canUndo()) { - console.log("undoing"); - - um.undo(); - } -}; diff --git a/app/srv/ws/sync/actions/yjs_diff_local.ts b/app/srv/ws/sync/actions/yjs_diff_local.ts index 4a4e9d00..f9f290fb 100644 --- a/app/srv/ws/sync/actions/yjs_diff_local.ts +++ b/app/srv/ws/sync/actions/yjs_diff_local.ts @@ -19,7 +19,4 @@ export const yjs_diff_local: SAction["yjs"]["diff_local"] = async function ( const um = docs[mode][id].um; um.addTrackedOrigin(this.client_id); Y.applyUpdate(doc, diff, this.client_id); - - const save = Y.encodeStateAsUpdate(doc); - snapshot.set(mode, id, "bin", save); }; diff --git a/app/srv/ws/sync/actions/yjs_um.ts b/app/srv/ws/sync/actions/yjs_um.ts new file mode 100644 index 00000000..2a7fbe06 --- /dev/null +++ b/app/srv/ws/sync/actions/yjs_um.ts @@ -0,0 +1,27 @@ +import { SAction } from "../actions"; +import { docs } from "../entity/docs"; +import { SyncConnection } from "../type"; + +export const yjs_um: SAction["yjs"]["um"] = async function ( + this: SyncConnection, + mode, + action, + id +) { + if (!docs[mode][id]) { + return; + } + + const um = docs[mode][id].um; + if (action === "redo") { + if (um.canRedo()) { + um.redo(); + } + } else { + if (um.undoStack.length > 1) { + if (um.canUndo()) { + um.undo(); + } + } + } +}; diff --git a/app/srv/ws/sync/entity/docs.ts b/app/srv/ws/sync/entity/docs.ts index 0b5f288b..e0e0533f 100644 --- a/app/srv/ws/sync/entity/docs.ts +++ b/app/srv/ws/sync/entity/docs.ts @@ -5,6 +5,14 @@ import { DPage } from "../../../../web/src/utils/types/root"; export * as Y from "yjs"; export const docs = { + site: {} as Record< + string, + { + id: string; + doc: DPage; + um: Y.UndoManager; + } + >, page: {} as Record< string, { diff --git a/app/srv/ws/sync/entity/snapshot.ts b/app/srv/ws/sync/entity/snapshot.ts index b676085c..a6475a83 100644 --- a/app/srv/ws/sync/entity/snapshot.ts +++ b/app/srv/ws/sync/entity/snapshot.ts @@ -3,9 +3,10 @@ import { RootDatabase, open } from "lmdb"; import { g } from "utils/global"; const emptySnapshot = { - type: "" as "" | "comp" | "page", + type: "" as "" | "comp" | "page" | "site", id: "", bin: new Uint8Array(), + id_doc: 0, name: "", ts: Date.now(), }; diff --git a/app/web/src/render/ed/logic/ed-global.ts b/app/web/src/render/ed/logic/ed-global.ts index 6ef12f54..073da2c1 100644 --- a/app/web/src/render/ed/logic/ed-global.ts +++ b/app/web/src/render/ed/logic/ed-global.ts @@ -1,19 +1,20 @@ import { NodeModel } from "@minoru/react-dnd-treeview"; +import { ReactElement } from "react"; import { clientStartSync } from "../../../utils/sync/ws-client"; -import { IContent, MContent } from "../../../utils/types/general"; import { IItem, MItem } from "../../../utils/types/item"; import { DComp, DPage, IRoot } from "../../../utils/types/root"; import { ISection } from "../../../utils/types/section"; import { IText, MText } from "../../../utils/types/text"; -import { ReactElement } from "react"; const EmptySite = { id: "", name: "", domain: "", - js: "", - js_compiled: "", config: { api_url: "" }, + snapshot: null as null | Uint8Array, + + // js: "", + // js_compiled: "", }; export type ESite = typeof EmptySite; export type EPage = typeof EmptyPage; @@ -95,7 +96,14 @@ export const EDGlobal = { open: {} as Record, }, popup: { - comp: null as null | ((comp_id: string) => void | Promise), + comp: null as null | true | ((comp_id: string) => void | Promise), + compGroup: null as + | null + | true + | { + event: React.MouseEvent; + pick: (group_id: string) => void | Promise; + }, }, }, }; diff --git a/app/web/src/render/ed/logic/ed-undo.ts b/app/web/src/render/ed/logic/ed-undo.ts index 053690c4..3d104d25 100644 --- a/app/web/src/render/ed/logic/ed-undo.ts +++ b/app/web/src/render/ed/logic/ed-undo.ts @@ -19,9 +19,9 @@ export const edUndoManager = async (p: PG) => { !evt.shiftKey ) { if (p.comp.cur.id) { - p.sync.comp.redo(p.comp.cur.id); + p.sync.yjs.um("comp", "redo", p.comp.cur.id); } else { - p.sync.page.redo(p.page.cur.id); + p.sync.yjs.um("page", "redo", p.page.cur.id); } return; } @@ -32,11 +32,10 @@ export const edUndoManager = async (p: PG) => { evt.shiftKey ) { if (p.comp.cur.id) { - p.sync.comp.redo(p.comp.cur.id); + p.sync.yjs.um("comp", "redo", p.comp.cur.id); } else { - p.sync.page.redo(p.page.cur.id); + p.sync.yjs.um("page", "redo", p.page.cur.id); } - return; } @@ -46,9 +45,9 @@ export const edUndoManager = async (p: PG) => { !evt.shiftKey ) { if (p.comp.cur.id) { - p.sync.comp.undo(p.comp.cur.id); + p.sync.yjs.um("comp", "undo", p.comp.cur.id); } else { - p.sync.page.undo(p.page.cur.id); + p.sync.yjs.um("page", "undo", p.page.cur.id); } } diff --git a/app/web/src/render/ed/panel/tree/node/item/action/clone.tsx b/app/web/src/render/ed/panel/tree/node/item/action/clone.tsx index bd3a785b..9f11e4c9 100644 --- a/app/web/src/render/ed/panel/tree/node/item/action/clone.tsx +++ b/app/web/src/render/ed/panel/tree/node/item/action/clone.tsx @@ -1,8 +1,22 @@ -import { IContent } from "../../../../../../../utils/types/general"; +import { syncronize } from "y-pojo"; +import { IContent, MContent } from "../../../../../../../utils/types/general"; +import { fillID } from "../../../../../../editor/tools/fill-id"; import { PG } from "../../../../../logic/ed-global"; +import { treeRebuild } from "../../../../../logic/tree/build"; export const edActionClone = (p: PG, item: IContent) => { const mitem = p.page.meta[item.id].mitem; if (mitem) { + mitem.doc?.transact(() => { + mitem.parent.forEach((e: MContent, idx) => { + if (e.get("id") === mitem.get("id")) { + const json = e.toJSON() as IContent; + const map = new Y.Map(); + syncronize(map, fillID(json)); + mitem.parent.insert(idx, [map]); + } + }); + }); + treeRebuild(p, { note: "clone" }); } }; diff --git a/app/web/src/render/ed/panel/tree/node/item/ctx-menu.tsx b/app/web/src/render/ed/panel/tree/node/item/ctx-menu.tsx index d37feaef..390ca90d 100644 --- a/app/web/src/render/ed/panel/tree/node/item/ctx-menu.tsx +++ b/app/web/src/render/ed/panel/tree/node/item/ctx-menu.tsx @@ -76,7 +76,7 @@ export const EdTreeCtxMenu = ({ onClick={() => edActionDetach(p, item)} /> )} - {type === "item" && comp?.id && ( + {type === "item" && !comp?.id && ( edActionNewComp(p, item)}