diff --git a/app/srv/ws/sync/actions/comp_load.ts b/app/srv/ws/sync/actions/comp_load.ts index 5dba1504..3244fd25 100644 --- a/app/srv/ws/sync/actions/comp_load.ts +++ b/app/srv/ws/sync/actions/comp_load.ts @@ -1,5 +1,4 @@ import { IScopeComp } from "../../../../web/src/nova/ed/logic/ed-global"; -import { MItem } from "../../../../web/src/utils/types/item"; import { DComp } from "../../../../web/src/utils/types/root"; import { SAction } from "../actions"; import { loadComponent } from "../editor/load-component"; diff --git a/app/srv/ws/sync/actions/site_load.ts b/app/srv/ws/sync/actions/site_load.ts index 88d00180..13a6dc16 100644 --- a/app/srv/ws/sync/actions/site_load.ts +++ b/app/srv/ws/sync/actions/site_load.ts @@ -21,15 +21,14 @@ export const site_load: SAction["site"]["load"] = async function ( activity.site.room(site.id).join({ ws: this.ws }); - let layout = undefined; - const _layout = await db.page.findFirst({ + const layout = await db.page.findFirst({ where: { id_site: id, is_deleted: false, is_default_layout: true, }, + select: { id: true }, }); - if (_layout) layout = _layout.content_tree as IRoot; return { id: site.id, @@ -38,7 +37,7 @@ export const site_load: SAction["site"]["load"] = async function ( domain: site.domain, js: site.js || "", js_compiled: site.js_compiled || "", - layout, + layout: { id: layout?.id || "" }, }; } } diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index 99465a8b..269c544d 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -5,7 +5,7 @@ import { SAction } from "../../../../../srv/ws/sync/actions"; import { parseJs } from "../../../../../srv/ws/sync/editor/parser/parse-js"; import { clientStartSync } from "../../../utils/sync/ws-client"; import { IItem, MItem } from "../../../utils/types/item"; -import { DComp, DPage, IRoot } from "../../../utils/types/root"; +import { DComp, DPage } from "../../../utils/types/root"; import { ISection } from "../../../utils/types/section"; import { IText, MText } from "../../../utils/types/text"; @@ -16,7 +16,7 @@ export const EmptySite = { config: { api_url: "" }, js: "", js_compiled: "", - layout: undefined as undefined | IRoot, + layout: { snapshot: null as null | Uint8Array, id: "" }, }; export type ESite = typeof EmptySite; @@ -114,8 +114,14 @@ export const EDGlobal = { page: { cur: EmptyPage, doc: null as null | DPage, - doc_on_update: async (bin: Uint8Array, origin: any) => {}, - list: {} as Record, + list: {} as Record< + string, + { + page: EPage; + doc: DPage; + on_update?: (bin: Uint8Array, origin: any) => Promise; + } + >, building: false, meta: {} as Record, entry: [] as string[], diff --git a/app/web/src/nova/ed/logic/ed-route.ts b/app/web/src/nova/ed/logic/ed-route.ts index 88fb4748..67c4a820 100644 --- a/app/web/src/nova/ed/logic/ed-route.ts +++ b/app/web/src/nova/ed/logic/ed-route.ts @@ -3,6 +3,7 @@ import { IItem } from "../../../utils/types/item"; import { DComp } from "../../../utils/types/root"; import { PG } from "./ed-global"; import { treeRebuild } from "./tree/build"; +import { loadSite } from "./ed-site"; export const edRoute = async (p: PG) => { if (p.status === "ready" || p.status === "init") { @@ -15,7 +16,7 @@ export const edRoute = async (p: PG) => { return; } - p.site = site; + await loadSite(p, site); } if ( @@ -23,22 +24,23 @@ export const edRoute = async (p: PG) => { !p.page.cur.snapshot || !p.page.list[p.page.cur.id] ) { - if (p.page.list[params.page_id] && p.page.doc) { - p.page.doc.off("update", p.page.doc_on_update); + const page = p.page.list[params.page_id]; + if (page && p.page.doc && page.on_update) { + p.page.doc.off("update", page.on_update); const cur = p.page.list[params.page_id]; p.page.cur = cur.page; p.page.doc = cur.doc; } - await reloadPage(p); + await reloadPage(p, params.page_id); } } }; -export const reloadPage = async (p: PG) => { +export const reloadPage = async (p: PG, page_id: string) => { p.status = "loading"; - const remotePage = await p.sync.page.load(params.page_id); + const remotePage = await p.sync.page.load(page_id); if (!remotePage) { p.status = "page-not-found"; @@ -66,7 +68,17 @@ export const reloadPage = async (p: PG) => { const doc = new Y.Doc(); Y.applyUpdate(doc, decompress(remotePage.snapshot)); - p.page.doc_on_update = async (bin: Uint8Array, origin: any) => { + let page = p.page.list[remotePage.id]; + if (!page) { + p.page.list[remotePage.id] = {} as any; + page = p.page.list[remotePage.id]; + } + + if (page.on_update && page.doc) { + page.doc.off("update", page.on_update); + } + + page.on_update = async (bin: Uint8Array, origin: any) => { if (origin === "sv_remote" || origin === "local") return; const res = await p.sync.yjs.sv_local( @@ -93,14 +105,12 @@ export const reloadPage = async (p: PG) => { } }; - doc.on("update", p.page.doc_on_update); + doc.on("update", page.on_update); p.page.doc = doc as any; if (p.page.doc) { - p.page.list[remotePage.id] = { - page: p.page.cur, - doc: p.page.doc, - }; + page.page = p.page.cur; + page.doc = p.page.doc; } if (p.page.doc) { diff --git a/app/web/src/nova/ed/logic/ed-site.ts b/app/web/src/nova/ed/logic/ed-site.ts new file mode 100644 index 00000000..013c5130 --- /dev/null +++ b/app/web/src/nova/ed/logic/ed-site.ts @@ -0,0 +1,19 @@ +import { ESite, PG } from "./ed-global"; +import { reloadPage } from "./ed-route"; + +export const loadSite = async (p: PG, site: ESite) => { + const old_layout_id = p.site.layout.id; + const layout_changed = p.site.layout.id !== site.layout.id; + p.site = site; + if (layout_changed) { + const old_layout = p.page.list[old_layout_id]; + + if (old_layout && old_layout.on_update) { + old_layout.doc.off("update", old_layout.on_update); + } + + if (site.layout.id) { + await reloadPage(p, site.layout.id); + } + } +}; diff --git a/app/web/src/nova/ed/logic/ed-sync.tsx b/app/web/src/nova/ed/logic/ed-sync.tsx index 61a87d7b..db3ba78b 100644 --- a/app/web/src/nova/ed/logic/ed-sync.tsx +++ b/app/web/src/nova/ed/logic/ed-sync.tsx @@ -8,6 +8,7 @@ import { EmptySite, PG } from "./ed-global"; import { treeRebuild } from "./tree/build"; import { evalCJS } from "../../view/logic/load-code"; import { reloadPage } from "./ed-route"; +import { loadSite } from "./ed-site"; const decoder = new TextDecoder(); @@ -28,9 +29,9 @@ export const edInitSync = (p: PG) => { p.site = deepClone(EmptySite); p.site.id = "--loading--"; p.ui.popup.code.init = false; - p.sync.site.load(params.site_id).then((site) => { + p.sync.site.load(params.site_id).then(async (site) => { if (site) { - p.site = site; + await loadSite(p, site); p.render(); } else { alert("Site not found. redirecting..."); @@ -124,7 +125,7 @@ export const edInitSync = (p: PG) => { }, async editor_start(e) { if (p.ui.syncing) { - await reloadPage(p); + await reloadPage(p, params.page_id); if (p.page.doc) { p.page.doc.transact(() => { p.page.doc?.getMap("map").set("ts", Date.now()); diff --git a/app/web/src/nova/ed/logic/tree/build.tsx b/app/web/src/nova/ed/logic/tree/build.tsx index f9bae148..9194f202 100644 --- a/app/web/src/nova/ed/logic/tree/build.tsx +++ b/app/web/src/nova/ed/logic/tree/build.tsx @@ -20,7 +20,9 @@ export const treeRebuild = async (p: PG, arg?: { note?: string }) => { const loaded = new Set(); await Promise.all( sections.map((e) => { - return syncWalkLoad(p, e, loaded); + return syncWalkLoad(p, e, loaded, (id) => { + return loadComponent(p, id); + }); }) ); } @@ -32,36 +34,61 @@ export const treeRebuild = async (p: PG, arg?: { note?: string }) => { let root_id = "root"; if (p.site.layout) { - const loaded = new Set(); - await Promise.all( - p.site.layout.childs.map((e) => - walkLoad(p.comp, e, loaded, (id) => loadComponent(p, id)) - ) - ); - p.site.layout.childs.map((e) => { - p.page.entry.push(e.id); - walkMap( - { meta: p.page.meta, comps: p.comp.map }, - { - isLayout: true, - item: e, - parent_item: { id: "root" }, - portal, - each(meta) { - if (meta.item.name === "content") { - root_id = meta.item.id; - } - }, - } - ); - }); + const ldoc = p.page.list[p.site.layout.id]; + if (ldoc) { + const lroot = ldoc.doc.getMap("map").get("root"); + if (lroot) { + const sections = lroot.get("childs"); + if (sections) { + const loaded = new Set(); + await Promise.all( + sections.map((e) => { + return syncWalkLoad(p, e, loaded, (id) => { + return loadComponent(p, id); + }); + }) + ); - // if root_id is root, it means content is not found. - // if content is not found, do not use layout. - if (root_id === "root") { - p.page.entry = []; - p.page.tree = []; - p.page.meta = {}; + sections.map((e) => { + if (root_id === "root") { + p.page.entry.push(e.get("id")); + } + syncWalkMap(p, { + isLayout: false, + mitem: e, + parent_item: { id: root_id }, + tree_root_id: root_id, + portal, + each(meta) { + if (meta.item.name === "content") { + root_id = meta.item.id; + } + }, + }); + }); + + for (const [k, portal_out] of Object.entries(portal.out)) { + const name = k.replace(/⮕/gi, "").trim(); + const portal_in = portal.in[`⬅${name}`]; + if (portal_in) { + for (const key of Object.keys(portal_in)) { + delete (portal_in as any)[key]; + } + for (const [k, v] of Object.entries(portal_out)) { + (portal_in as any)[k] = v; + } + } + } + } + + // if root_id is root, it means content is not found. + // if content is not found, do not use layout. + if (root_id === "root") { + p.page.entry = []; + p.page.tree = []; + p.page.meta = {}; + } + } } } 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 e41b2b9f..cd0b3af4 100644 --- a/app/web/src/nova/ed/logic/tree/sync-walk.tsx +++ b/app/web/src/nova/ed/logic/tree/sync-walk.tsx @@ -16,7 +16,8 @@ import { export const syncWalkLoad = async ( p: PG, mitem: MItem, - loaded: Set + loaded: Set, + loadComponent: (id: string) => Promise ) => { const mcomp = mitem.get("component"); if (mcomp) { @@ -26,14 +27,14 @@ export const syncWalkLoad = async ( const isFirstLoaded = !loaded.has(id); loaded.add(id); if (!p.comp.list[id]) { - await loadComponent(p, comp.id); + await loadComponent(comp.id); } const pcomp = p.comp.list[id]; if (pcomp) { const pitem = pcomp.doc.getMap("map").get("root"); if (pitem && isFirstLoaded) { - await syncWalkLoad(p, pitem, loaded); + await syncWalkLoad(p, pitem, loaded, loadComponent); } } } @@ -44,7 +45,7 @@ export const syncWalkLoad = async ( if (mprop) { const mcontent = ensurePropContent(mprop, propName); if (mcontent) { - await syncWalkLoad(p, mcontent, loaded); + await syncWalkLoad(p, mcontent, loaded, loadComponent); } } } @@ -52,7 +53,7 @@ export const syncWalkLoad = async ( } for (const e of mitem.get("childs")?.map((e) => e) || []) { - await syncWalkLoad(p, e, loaded); + await syncWalkLoad(p, e, loaded, loadComponent); } }; @@ -69,6 +70,7 @@ export const syncWalkMap = ( parent_mcomp?: EdMeta["parent_mcomp"]; skip_add_tree?: boolean; tree_root_id: string; + each?: (meta: EdMeta) => void; } ) => { const { mitem, parent_item, parent_mcomp } = arg; @@ -153,6 +155,8 @@ export const syncWalkMap = ( if (item.name.startsWith("⮕")) { arg.portal.out[item.name] = meta; } + + if (arg.each) arg.each(meta); p.page.meta[item.id] = meta; if (!skip_tree) { @@ -231,6 +235,8 @@ export const syncWalkMap = ( if (item.name.startsWith("⮕")) { arg.portal.out[item.name] = meta; } + + if (arg.each) arg.each(meta); p.page.meta[item.id] = meta; if (!skip_tree) {