diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index 3de7ec74..4167acb9 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -2,7 +2,7 @@ import { component, page } from "dbgen"; import { EPage, ESite, - IScopeComp + IScopeComp, } from "../../../web/src/nova/ed/logic/ed-global"; import { IItem } from "../../../web/src/utils/types/item"; import { site_group } from "./actions/site_group"; @@ -44,7 +44,7 @@ export const SyncActions = { ({}) as Record>, group: async (id_site: string) => ({}) as Record, - load: async (id: string) => ({}) as IScopeComp | void, + load: async (ids: string[]) => ({}) as Record, }, page: { list: async (id_site: string) => diff --git a/app/srv/ws/sync/actions/comp_load.ts b/app/srv/ws/sync/actions/comp_load.ts index 3244fd25..5efd76b2 100644 --- a/app/srv/ws/sync/actions/comp_load.ts +++ b/app/srv/ws/sync/actions/comp_load.ts @@ -9,14 +9,18 @@ import { SyncConnection } from "../type"; export const comp_load: SAction["comp"]["load"] = async function ( this: SyncConnection, - id: string + ids: string[] ) { - const root = await loadComponent(id, this); + const result: Record = {}; + for (const id of ids) { + const root = await loadComponent(id, this); - let ref = docs.comp[id]; - if (ref) { - return scanMeta(id, ref.doc, this); + let ref = docs.comp[id]; + if (ref) { + result[id] = await scanMeta(id, ref.doc, this); + } } + return result; }; const scanMeta = async (id: string, doc: DComp, sync: SyncConnection) => { diff --git a/app/web/src/nova/ed/logic/ed-route.ts b/app/web/src/nova/ed/logic/ed-route.ts index e9274c17..877408d8 100644 --- a/app/web/src/nova/ed/logic/ed-route.ts +++ b/app/web/src/nova/ed/logic/ed-route.ts @@ -32,12 +32,12 @@ export const edRoute = async (p: PG) => { p.page.doc = cur.doc; } - await reloadPage(p, params.page_id); + await reloadPage(p, params.page_id, "load-route"); } } }; -export const reloadPage = async (p: PG, page_id: string) => { +export const reloadPage = async (p: PG, page_id: string, note: string) => { p.status = "loading"; const remotePage = await p.sync.page.load(page_id); @@ -51,7 +51,6 @@ export const reloadPage = async (p: PG, page_id: string) => { if (remotePage.scope_comps) { for (const [id_comp, c] of Object.entries(remotePage.scope_comps)) { if (c && c.snapshot) { - await loadCompSnapshot( p, id_comp, @@ -93,7 +92,7 @@ export const reloadPage = async (p: PG, page_id: string) => { decompress(res.sv) ); Y.applyUpdate(doc as any, decompress(res.diff), "local"); - await treeRebuild(p); + await treeRebuild(p, { note: "page-on-update" }); await p.sync.yjs.diff_local( "page", @@ -114,7 +113,7 @@ export const reloadPage = async (p: PG, page_id: string) => { } if (p.page.doc) { - await treeRebuild(p); + await treeRebuild(p, { note: "reload-page-init" }); } } p.status = "ready"; diff --git a/app/web/src/nova/ed/logic/ed-site.ts b/app/web/src/nova/ed/logic/ed-site.ts index 013c5130..3bc51417 100644 --- a/app/web/src/nova/ed/logic/ed-site.ts +++ b/app/web/src/nova/ed/logic/ed-site.ts @@ -13,7 +13,7 @@ export const loadSite = async (p: PG, site: ESite) => { } if (site.layout.id) { - await reloadPage(p, site.layout.id); + await reloadPage(p, site.layout.id, "load-layout"); } } }; diff --git a/app/web/src/nova/ed/logic/ed-sync.tsx b/app/web/src/nova/ed/logic/ed-sync.tsx index db3ba78b..b80eef57 100644 --- a/app/web/src/nova/ed/logic/ed-sync.tsx +++ b/app/web/src/nova/ed/logic/ed-sync.tsx @@ -125,7 +125,7 @@ export const edInitSync = (p: PG) => { }, async editor_start(e) { if (p.ui.syncing) { - await reloadPage(p, params.page_id); + 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()); @@ -154,28 +154,31 @@ export const edInitSync = (p: PG) => { p.render(); }, async remote_svlocal(data) { - if (p[data.type].cur.id === data.id) { - const doc = p[data.type].doc as Y.Doc; + 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); + 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" }); - } + 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" }); } } }, diff --git a/app/web/src/nova/ed/logic/tree/build.tsx b/app/web/src/nova/ed/logic/tree/build.tsx index 1ac1a497..aab047b9 100644 --- a/app/web/src/nova/ed/logic/tree/build.tsx +++ b/app/web/src/nova/ed/logic/tree/build.tsx @@ -1,5 +1,10 @@ import { EdMeta, PG } from "../ed-global"; -import { loadComponent, syncWalkLoad, syncWalkMap } from "./sync-walk"; +import { + component, + loadComponent, + syncWalkLoad, + syncWalkMap, +} from "./sync-walk"; export const compLoaded = new Set(); @@ -16,24 +21,13 @@ export const treeRebuild = async (p: PG, arg?: { note?: string }) => { p.page.tree = []; p.page.meta = {}; - const sections = root.get("childs"); - if (sections) { - await Promise.all( - sections.map((e) => { - return syncWalkLoad(p, e, compLoaded, (id) => { - return loadComponent(p, id, compLoaded); - }); - }) - ); - } - const portal = { in: {} as Record, out: {} as Record, }; let root_id = "root"; - if (p.site.layout) { + if (p.site.layout && p.site.layout.id !== p.page.cur.id) { const ldoc = p.page.list[p.site.layout.id]; if (ldoc) { const lroot = ldoc.doc.getMap("map").get("root"); @@ -49,6 +43,8 @@ export const treeRebuild = async (p: PG, arg?: { note?: string }) => { }) ); + if (component.pending) await component.pending; + sections.map((e) => { if (root_id === "root") { p.page.entry.push(e.get("id")); @@ -100,6 +96,19 @@ export const treeRebuild = async (p: PG, arg?: { note?: string }) => { } } + const sections = root.get("childs"); + if (sections) { + await Promise.all( + sections.map((e) => { + return syncWalkLoad(p, e, compLoaded, (id) => { + return loadComponent(p, id, compLoaded); + }); + }) + ); + + if (component.pending) await component.pending; + } + doc.transact(async () => { const sections = root.get("childs"); if (sections) { diff --git a/app/web/src/nova/ed/logic/tree/sync-walk.tsx b/app/web/src/nova/ed/logic/tree/sync-walk.tsx index ac3b04a3..e3f4c8b4 100644 --- a/app/web/src/nova/ed/logic/tree/sync-walk.tsx +++ b/app/web/src/nova/ed/logic/tree/sync-walk.tsx @@ -13,7 +13,6 @@ import { ensureMProp, ensurePropContent, } from "./sync-walk-utils"; -import { waitUntil } from "web-utils"; export const syncWalkLoad = async ( p: PG, @@ -28,8 +27,8 @@ export const syncWalkLoad = async ( if (id) { const isFirstLoaded = !loaded.has(id); loaded.add(id); - if (!p.comp.list[id]) { - await loadComponent(comp.id); + if (!p.comp.list[id] && isFirstLoaded) { + loadComponent(comp.id); } const pcomp = p.comp.list[id]; @@ -320,22 +319,45 @@ export const loadCompSnapshot = async ( } }; +const loadcomp = { timeout: 0 as any, pending: new Set() }; +export const component = { + pending: null as null | Promise, + resolve: null as null | (() => void), +}; export const loadComponent = async ( p: PG, id_comp: string, loaded: Set ) => { - const comps = await p.sync.comp.load(id_comp); - - if (comps) { - for (const cur of Object.values(comps)) { - if (cur && cur.snapshot) { - await loadCompSnapshot(p, id_comp, loaded, cur.snapshot, cur.scope); - } - } - return true; + if (!component.pending) { + component.pending = new Promise((resolve) => { + component.resolve = resolve; + }); } - return false; + + return new Promise((resolve) => { + loadcomp.pending.add(id_comp); + clearTimeout(loadcomp.timeout); + loadcomp.timeout = setTimeout(async () => { + const comps = await p.sync.comp.load([...loadcomp.pending]); + let result = Object.entries(comps); + loadcomp.pending.clear(); + + for (const [id_comp, comp] of result) { + for (const cur of Object.values(comp)) { + if (cur && cur.snapshot) { + await loadCompSnapshot(p, id_comp, loaded, cur.snapshot, cur.scope); + } + } + } + resolve(result.length > 0); + if (component.resolve) { + component.resolve(); + component.pending = null; + component.resolve = null; + } + }, 150); + }); }; const mapItem = (mitem: MContent, item: any) => {