diff --git a/app/srv/ws/sync/sync-handler.ts b/app/srv/ws/sync/sync-handler.ts index b658b1d8..2feafc95 100644 --- a/app/srv/ws/sync/sync-handler.ts +++ b/app/srv/ws/sync/sync-handler.ts @@ -49,7 +49,6 @@ export const syncHandler: WebSocketHandler = { conn.user_id = user_id; conn.user = await db.user.findFirst({ where: { id: user_id } }); - let conf = await user.conf.getOrCreate(user_id); if (site_id) { const newconf = await loadSitePage(user_id, site_id, page_id); diff --git a/app/web/src/base/page/ed.tsx b/app/web/src/base/page/ed.tsx index ef10d4a5..27713c90 100644 --- a/app/web/src/base/page/ed.tsx +++ b/app/web/src/base/page/ed.tsx @@ -1,9 +1,8 @@ import { page, useGlobal } from "web-utils"; import { EdBase } from "../../nova/ed/ed-base"; import { EDGlobal } from "../../nova/ed/logic/ed-global"; -import { Loading } from "../../utils/ui/loading"; -import { initSync } from "wasm-gzip"; import { edInitSync } from "../../nova/ed/logic/ed-sync"; +import { Loading } from "../../utils/ui/loading"; export default page({ url: "/ed/:site_id/:page_id", @@ -11,9 +10,9 @@ export default page({ const p = useGlobal(EDGlobal, "EDITOR"); if (!edInitSync(p)) { - return ; + return ; } - + return ; }, }); diff --git a/app/web/src/nova/ed/ed-base.tsx b/app/web/src/nova/ed/ed-base.tsx index 9ae87ed6..2c1215dc 100644 --- a/app/web/src/nova/ed/ed-base.tsx +++ b/app/web/src/nova/ed/ed-base.tsx @@ -19,7 +19,7 @@ export const EdBase = () => { edUndoManager(p); if (p.status === "init") { - edInit(p) + edInit(p); } edRoute(p); diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index f4a474cd..9f560e67 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -1,5 +1,5 @@ import { NodeModel } from "@minoru/react-dnd-treeview"; -import { ReactElement } from "react"; +import { FC, ReactElement } from "react"; import { deepClone } from "web-utils"; import { SAction } from "../../../../../srv/ws/sync/actions"; import { clientStartSync } from "../../../utils/sync/ws-client"; @@ -61,6 +61,10 @@ export type EdMeta = { mcomp: MItem; }; el?: ReactElement; + memoize?: Record; + PassProp: FC; + }>; }; export const EDGlobal = { @@ -75,7 +79,10 @@ export const EDGlobal = { | "ready", sync: null as unknown as Awaited>, site: deepClone(EmptySite), - script: { siteTypes: {} as Record }, + script: { + siteTypes: {} as Record, + loaded: false, + }, page: { cur: EmptyPage, doc: null as null | DPage, diff --git a/app/web/src/nova/ed/logic/ed-init.ts b/app/web/src/nova/ed/logic/ed-init.ts index e417d89f..5dbd2f51 100644 --- a/app/web/src/nova/ed/logic/ed-init.ts +++ b/app/web/src/nova/ed/logic/ed-init.ts @@ -3,8 +3,10 @@ import { PG } from "./ed-global"; import { jscript } from "../../../utils/script/jscript"; export const edInit = async (p: PG) => { + p.status = "ready"; + await init(); jscript.init(p.render); - p.status = "ready"; + p.script.loaded = true; p.render(); }; diff --git a/app/web/src/nova/ed/logic/ed-route.ts b/app/web/src/nova/ed/logic/ed-route.ts index 295ce83a..8660accd 100644 --- a/app/web/src/nova/ed/logic/ed-route.ts +++ b/app/web/src/nova/ed/logic/ed-route.ts @@ -91,3 +91,4 @@ export const edRoute = async (p: PG) => { } } }; + diff --git a/app/web/src/nova/ed/panel/header/left/api.tsx b/app/web/src/nova/ed/panel/header/left/api.tsx index e0fa8d3b..053f7b20 100644 --- a/app/web/src/nova/ed/panel/header/left/api.tsx +++ b/app/web/src/nova/ed/panel/header/left/api.tsx @@ -13,9 +13,8 @@ export const EdApi = () => { className="flex-1 min-h-[26px] flex items-center justify-center" dangerouslySetInnerHTML={{ __html: ` - - - + + `, }} > diff --git a/app/web/src/nova/view/logic/global.ts b/app/web/src/nova/view/logic/global.ts index 4c5e3400..957699b0 100644 --- a/app/web/src/nova/view/logic/global.ts +++ b/app/web/src/nova/view/logic/global.ts @@ -1,9 +1,6 @@ import { ReactElement } from "react"; import { IContent } from "../../../utils/types/general"; -import { IItem } from "../../../utils/types/item"; -import { ISection } from "../../../utils/types/section"; -import { IText } from "../../../utils/types/text"; -import { EdMeta } from "../../ed/logic/ed-global"; +import { EdMeta, PG } from "../../ed/logic/ed-global"; export const ViewGlobal = { mode: "" as "desktop" | "mobile", @@ -12,6 +9,15 @@ export const ViewGlobal = { meta: {} as Record, entry: [] as string[], bodyCache: null as null | ReactElement, + component: { + map: {} as PG["comp"]["map"], + load: async (id_comp: string) => {}, + }, + script: { + api_url: "", + db: null as any, + api: null as any, + }, view: { hidden: undefined as undefined | ((item: IContent) => boolean), active: undefined as diff --git a/app/web/src/nova/view/logic/router.ts b/app/web/src/nova/view/logic/router.ts new file mode 100644 index 00000000..62d0c3b6 --- /dev/null +++ b/app/web/src/nova/view/logic/router.ts @@ -0,0 +1,39 @@ +import { VG } from "./global"; + +export const preload = async (p: VG, pathname: string) => { + //TODO +}; + +export const extractNavigate = (str: string) => { + return [ + ...findBetween(str, `navigate(`, `)`), + ...findBetween(str, `href = `, `;`), + ]; +}; + +const findBetween = (text: string, opener: string, closer: string) => { + let i = 0; + let last = 0; + const founds: string[] = []; + while (true) { + const startIndex = text.indexOf(opener, i); + last = i; + if (startIndex >= 0) { + const char = text[startIndex + opener.length]; + if (char === '"' || char === "'" || char === "`") { + const end = text.indexOf( + `${char}${closer}`, + startIndex + opener.length + 1 + ); + const found = text.substring(startIndex + opener.length + 1, end); + i = end + 2 + closer.length; + founds.push(found); + } + } + + if (last === i) { + break; + } + } + return founds; +}; diff --git a/app/web/src/nova/view/logic/types.ts b/app/web/src/nova/view/logic/types.ts index ef419214..57cbe91a 100644 --- a/app/web/src/nova/view/logic/types.ts +++ b/app/web/src/nova/view/logic/types.ts @@ -14,5 +14,5 @@ export type VLoad = export type VLoadComponent = { map: PG["comp"]["map"]; - load: (id_comp: string) => void; + load: (id_comp: string) => Promise; }; diff --git a/app/web/src/nova/view/render/meta/meta.tsx b/app/web/src/nova/view/render/meta/meta.tsx index 5d3d7eaf..b8e67995 100644 --- a/app/web/src/nova/view/render/meta/meta.tsx +++ b/app/web/src/nova/view/render/meta/meta.tsx @@ -4,10 +4,15 @@ import { ViewGlobal } from "../../logic/global"; import { ViewMetaRender } from "./render"; import { ViewMetaScript } from "./script"; -export const ViewMeta: FC<{ id: string }> = ({ id }) => { +export const ViewMeta: FC<{ id: string; scopeIndex?: Record }> = ({ + id, + scopeIndex, +}) => { const v = useGlobal(ViewGlobal, "VIEW"); const meta = v.meta[id]; + if (!meta) return null; + const item = meta.item; if (item.hidden && v.view.hidden) { @@ -17,8 +22,8 @@ export const ViewMeta: FC<{ id: string }> = ({ id }) => { } if (item.adv) { - if (item.adv.js) { - return ; + if (item.adv.js && item.adv.jsBuilt) { + return ; } } diff --git a/app/web/src/nova/view/render/meta/script.tsx b/app/web/src/nova/view/render/meta/script.tsx index edf5248c..5d2f0f30 100644 --- a/app/web/src/nova/view/render/meta/script.tsx +++ b/app/web/src/nova/view/render/meta/script.tsx @@ -1,13 +1,49 @@ +import hash_sum from "hash-sum"; import { FC } from "react"; +import { createAPI, createDB } from "../../../../utils/script/init-api"; import { IItem } from "../../../../utils/types/item"; import { ISection } from "../../../../utils/types/section"; import { IText } from "../../../../utils/types/text"; import { VG } from "../../logic/global"; +import { extractNavigate, preload } from "../../logic/router"; import { ViewMetaRender } from "./render"; +import { createLocal } from "./script/create-local"; +import { createPassProp } from "./script/create-pass-prop"; export const ViewMetaScript: FC<{ v: VG; item: IItem | IText | ISection; -}> = ({ item, v }) => { + scopeIndex?: Record; +}> = ({ item, v, scopeIndex }) => { + const js = item.adv?.jsBuilt; + const meta = v.meta[item.id]; + if (js && meta) { + if (!meta.memoize) { + meta.memoize = {}; + } + const memoizeKey = hash_sum(scopeIndex) || "default"; + if (!meta.memoize[memoizeKey]) { + meta.memoize[memoizeKey] = { + Local: createLocal(v, item.id, scopeIndex), + PassProp: createPassProp(v, item.id, scopeIndex), + }; + } + + const _js = item.adv?.js; + if (typeof _js === "string") { + const navs = extractNavigate(_js || ""); + if (navs.length > 0) { + navs.map((nav) => preload(v, nav)); + } + } + + if (v.script.api_url) { + if (!v.script.db) v.script.db = createDB(v.script.api_url); + if (!v.script.api) v.script.api = createAPI(v.script.api_url); + } + + + } + return ; }; diff --git a/app/web/src/nova/view/render/meta/script/create-local.tsx b/app/web/src/nova/view/render/meta/script/create-local.tsx new file mode 100644 index 00000000..f79e60b1 --- /dev/null +++ b/app/web/src/nova/view/render/meta/script/create-local.tsx @@ -0,0 +1,11 @@ +import { VG } from "../../../logic/global"; + +export const createLocal = ( + v: VG, + id: string, + scopeIndex?: Record +) => { + return () => { + return <>; + }; +}; diff --git a/app/web/src/nova/view/render/meta/script/create-pass-prop.tsx b/app/web/src/nova/view/render/meta/script/create-pass-prop.tsx new file mode 100644 index 00000000..ba76f3c1 --- /dev/null +++ b/app/web/src/nova/view/render/meta/script/create-pass-prop.tsx @@ -0,0 +1,11 @@ +import { VG } from "../../../logic/global"; + +export const createPassProp = ( + v: VG, + id: string, + scopeIndex?: Record +) => { + return () => { + return <>; + }; +}; diff --git a/app/web/src/nova/view/view.tsx b/app/web/src/nova/view/view.tsx index 2b744bd0..f3c8c696 100644 --- a/app/web/src/nova/view/view.tsx +++ b/app/web/src/nova/view/view.tsx @@ -1,12 +1,12 @@ import { FC } from "react"; import { useGlobal } from "web-utils"; +import { IContent } from "../../utils/types/general"; import { Loading } from "../../utils/ui/loading"; -import { VG, ViewGlobal } from "./logic/global"; +import { ViewGlobal } from "./logic/global"; import { vInit } from "./logic/init"; import { vLoadCode } from "./logic/load-code"; import { VLoad, VLoadComponent } from "./logic/types"; import { VEntry } from "./render/entry"; -import { IContent } from "../../utils/types/general"; export const View: FC<{ load: VLoad; @@ -18,16 +18,17 @@ export const View: FC<{ hidden?: (item: IContent) => boolean; hover?: { get: (item: IContent) => boolean; set: (id: string) => void }; active?: { get: (item: IContent) => boolean; set: (id: string) => void }; -}> = ({ load, site_id, page_id, bind, hover, active, hidden }) => { +}> = ({ load, site_id, page_id, bind, hover, active, hidden, component }) => { const v = useGlobal(ViewGlobal, "VIEW"); if (hidden) v.view.hidden = hidden; if (hover) v.view.hover = hover; if (active) v.view.active = active; - if (v.current.page_id !== page_id || v.current.site_id !== site_id) { v.status = "init"; } + v.component.map = component.map; + v.component.load = component.load; if (bind) { bind({ @@ -48,7 +49,7 @@ export const View: FC<{ if (v.status === "load-code" || v.status === "loading-code") { vLoadCode(v); if (v.status === "load-code" || v.status === "loading-code") { - return ; + return ; } }