From b52975ebfd6ac512df8a5ac14851ac0dd40637b7 Mon Sep 17 00:00:00 2001 From: Rizky Date: Fri, 26 Jan 2024 22:01:52 +0700 Subject: [PATCH] wip fix time --- app/srv/ws/sync/actions-def.ts | 36 ++++++----- app/srv/ws/sync/actions.ts | 2 + app/srv/ws/sync/actions/index.ts | 1 + app/srv/ws/sync/actions/page_cache.ts | 58 +++++++++++++++++ app/web/src/base/load/api/api-proxy-def.tsx | 2 +- app/web/src/nova/ed/logic/ed-global.ts | 8 ++- app/web/src/nova/ed/logic/tree/build.tsx | 24 ++++++- app/web/src/nova/vi/preview.tsx | 59 +++++++++++++++++- app/web/src/nova/vi/render/global.tsx | 8 ++- .../src/nova/vi/render/script/eval-prop.tsx | 13 +++- .../src/nova/vi/render/script/eval-script.tsx | 9 +++ .../src/nova/vi/render/script/extract-nav.tsx | 58 +++++++++++++++++ app/web/src/nova/vi/vi.tsx | 22 ++++++- dockerzip | Bin 4620 -> 4620 bytes 14 files changed, 275 insertions(+), 25 deletions(-) create mode 100644 app/srv/ws/sync/actions/page_cache.ts create mode 100644 app/web/src/nova/vi/render/script/extract-nav.tsx diff --git a/app/srv/ws/sync/actions-def.ts b/app/srv/ws/sync/actions-def.ts index 94d691ec..2ab52416 100644 --- a/app/srv/ws/sync/actions-def.ts +++ b/app/srv/ws/sync/actions-def.ts @@ -13,21 +13,22 @@ export const SyncActionDefinition = { }, "page": { "list": "8", - "load": "9" + "load": "9", + "cache": "10" }, "yjs": { - "um": "10", - "sv_local": "11", - "diff_local": "12", - "sv_remote": "13" + "um": "11", + "sv_local": "12", + "diff_local": "13", + "sv_remote": "14" }, "client": { - "info": "14" + "info": "15" }, "code": { - "load": "15", - "edit": "16", - "parse": "17" + "load": "16", + "edit": "17", + "parse": "18" } }; export const SyncActionPaths = { @@ -41,12 +42,13 @@ export const SyncActionPaths = { "7": "comp.load", "8": "page.list", "9": "page.load", - "10": "yjs.um", - "11": "yjs.sv_local", - "12": "yjs.diff_local", - "13": "yjs.sv_remote", - "14": "client.info", - "15": "code.load", - "16": "code.edit", - "17": "code.parse" + "10": "page.cache", + "11": "yjs.um", + "12": "yjs.sv_local", + "13": "yjs.diff_local", + "14": "yjs.sv_remote", + "15": "client.info", + "16": "code.load", + "17": "code.edit", + "18": "code.parse" }; diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index 424a7606..89241a2d 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -52,6 +52,8 @@ export const SyncActions = { list: async (id_site: string) => ({}) as Record>, load: async (id: string) => ({}) as EPage | void, + cache: async (site_id: string, urls: string[], exclude_page_id: string[]) => + ({}) as { gzip: Uint8Array } | null, }, yjs: { um: async ( diff --git a/app/srv/ws/sync/actions/index.ts b/app/srv/ws/sync/actions/index.ts index d11a6a14..018da2ce 100644 --- a/app/srv/ws/sync/actions/index.ts +++ b/app/srv/ws/sync/actions/index.ts @@ -8,6 +8,7 @@ export * from "./comp_group"; export * from "./comp_load"; export * from "./page_list"; export * from "./page_load"; +export * from "./page_cache"; export * from "./yjs_um"; export * from "./yjs_sv_local"; export * from "./yjs_diff_local"; diff --git a/app/srv/ws/sync/actions/page_cache.ts b/app/srv/ws/sync/actions/page_cache.ts new file mode 100644 index 00000000..377ddeec --- /dev/null +++ b/app/srv/ws/sync/actions/page_cache.ts @@ -0,0 +1,58 @@ +import { RadixRouter, createRouter } from "radix3"; +import { SAction } from "../actions"; +import { SyncConnection } from "../type"; +import { gzipAsync } from "../entity/zlib"; + +const cache = {} as Record< + string, + { ts: number; router: RadixRouter<{ url: string; id: string }> } +>; + +const encoder = new TextEncoder(); +export const page_cache: SAction["page"]["cache"] = async function ( + this: SyncConnection, + site_id, + urls, + exclude_page_id +) { + let result = null as unknown as Awaited>; + + if ( + !cache[site_id] || + (cache[site_id] && Date.now() - cache[site_id].ts > 5000) + ) { + const pages = await db.page.findMany({ + where: { id_site: site_id, is_deleted: false }, + select: { id: true, url: true }, + }); + const router = createRouter<{ url: string; id: string }>(); + for (const page of pages) { + router.insert(page.url, page); + } + cache[site_id] = { + router, + ts: Date.now(), + }; + } + + const router = cache[site_id]?.router; + if (router) { + const result: Record = {}; + for (const url of urls) { + const found = router.lookup(url); + if (found && !exclude_page_id.includes(found.id)) { + const row = await db.page.findFirst({ + where: { id: found.id }, + select: { content_tree: true }, + }); + if (row) { + result[found.id] = row.content_tree; + } + } + } + const gzip = await gzipAsync(encoder.encode(JSON.stringify(result))); + return { gzip }; + } + + return null; +}; diff --git a/app/web/src/base/load/api/api-proxy-def.tsx b/app/web/src/base/load/api/api-proxy-def.tsx index bbf4354f..aaf09c12 100644 --- a/app/web/src/base/load/api/api-proxy-def.tsx +++ b/app/web/src/base/load/api/api-proxy-def.tsx @@ -25,7 +25,7 @@ export const loadApiProxyDef = async (_url: string, with_types: boolean) => { const ts = localStorage.getItem("api-ts-" + url); - if (with_types) { + if (with_types) { script.src = `${base}/_prasi/load.js?url=${url}&v3&dev=1&ts=${ts}`; } else { script.src = `${base}/_prasi/load.js?url=${url}&v3&ts=${ts}`; diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index a35a9036..cc3b2913 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 } from "../../../utils/types/item"; -import { DCode, DComp, DPage } from "../../../utils/types/root"; +import { DCode, DComp, DPage, IRoot } from "../../../utils/types/root"; import { GenMetaP, IMeta as LogicMeta } from "../../vi/utils/types"; export type IMeta = LogicMeta; @@ -147,6 +147,12 @@ export const EDGlobal = { | "page-not-found" | "ready", preview: { + url_cache: new Set(), + page_cache: {} as Record, + meta_cache: {} as Record< + string, + { entry: string[]; meta: Record } + >, show_loading: false, }, sync: null as unknown as Awaited>, diff --git a/app/web/src/nova/ed/logic/tree/build.tsx b/app/web/src/nova/ed/logic/tree/build.tsx index b9116d3c..66d5d954 100644 --- a/app/web/src/nova/ed/logic/tree/build.tsx +++ b/app/web/src/nova/ed/logic/tree/build.tsx @@ -1,10 +1,32 @@ -import { createId } from "@paralleldrive/cuid2"; +import { IContent } from "../../../../utils/types/general"; import { IItem, MItem } from "../../../../utils/types/item"; import { genMeta } from "../../../vi/meta/meta"; import { IMeta, PG, active } from "../ed-global"; import { assignMitem } from "./assign-mitem"; import { pushTreeNode } from "./build/push-tree"; +export const treeCacheBuild = async (p: PG, page_id: string) => { + const root = p.preview.page_cache[page_id]; + + const meta_cache = { + meta: {} as Record, + entry: [] as string[], + }; + for (const item of root.childs) { + meta_cache.entry.push(item.id); + genMeta( + { + note: "tree-rebuild", + comps: p.comp.loaded, + meta: meta_cache.meta, + mode: "page", + }, + { item: item as IContent } + ); + } + p.preview.meta_cache[page_id] = meta_cache; +}; + export const treeRebuild = async (p: PG, arg?: { note?: string }) => { if (document.activeElement) { const a = document.activeElement; diff --git a/app/web/src/nova/vi/preview.tsx b/app/web/src/nova/vi/preview.tsx index b21e5d03..d1fd3b6f 100644 --- a/app/web/src/nova/vi/preview.tsx +++ b/app/web/src/nova/vi/preview.tsx @@ -4,9 +4,12 @@ import { EDGlobal, PG, active } from "../ed/logic/ed-global"; import { reloadPage } from "../ed/logic/ed-route"; import { loadSite } from "../ed/logic/ed-site"; import { Vi } from "./vi"; -import init from "wasm-gzip"; +import init, { decompress } from "wasm-gzip"; import { w } from "../../utils/types/general"; +import { IRoot } from "../../utils/types/root"; +import { treeCacheBuild } from "../ed/logic/tree/build"; +const decoder = new TextDecoder(); export const ViPreview = (arg: { pathname: string }) => { const p = useGlobal(EDGlobal, "EDITOR"); @@ -49,6 +52,13 @@ export const ViPreview = (arg: { pathname: string }) => { const mode = p.mode; + if (!w.isEditor) { + p.preview.meta_cache[params.page_id] = { + meta: p.page.meta, + entry: p.page.entry, + }; + } + return (
{ db={p.script.db} render_stat="disabled" script={{ init_local_effect: p.script.init_local_effect }} + on_nav_loaded={async ({ urls }) => { + const load_urls: string[] = []; + if (p.preview.url_cache) { + for (const url of urls) { + if (!p.preview.url_cache.has(url)) { + load_urls.push(url); + p.preview.url_cache.add(url); + } + } + } + + if (load_urls.length > 0) { + console.log(load_urls); + const res = await p.sync.page.cache( + p.site.id, + load_urls, + Object.keys(p.preview.page_cache) + ); + if (res) { + const pages = JSON.parse( + decoder.decode(decompress(res.gzip)) || "{}" + ); + + for (const [id, page] of Object.entries(pages)) { + p.preview.page_cache[id] = page as IRoot; + treeCacheBuild(p, params.page_id); + } + } + } + }} />
@@ -130,6 +170,23 @@ const viRoute = async (p: PG) => { } p.script.init_local_effect = {}; + + if (!w.isEditor) { + const page_cache = p.preview.meta_cache[params.page_id]; + + if (page_cache) { + p.page.meta = page_cache.meta; + p.page.entry = page_cache.entry; + + if (p.page.cur.id !== params.page_id) { + p.page.cur = { id: params.page_id } as any; + } + p.status = "ready"; + p.render(); + return; + } + } + await reloadPage(p, params.page_id, "load-route"); } } diff --git a/app/web/src/nova/vi/render/global.tsx b/app/web/src/nova/vi/render/global.tsx index c9c520f1..b5f206c3 100644 --- a/app/web/src/nova/vi/render/global.tsx +++ b/app/web/src/nova/vi/render/global.tsx @@ -1,4 +1,3 @@ -import { IItem } from "../../../utils/types/item"; import { IMeta } from "../../ed/logic/ed-global"; import { viParts } from "./parts"; @@ -25,6 +24,13 @@ export const ViGlobal = { | undefined | ((meta: IMeta, parts: ReturnType) => void), on_status_changes: undefined as void | ((status: ViStatus) => void), + page: { + cur: { id: "" }, + navs: {} as Record>, + }, + on_nav_loaded: undefined as + | undefined + | ((arg: { urls: string[] }) => Promise), }; export type VG = typeof ViGlobal & { render: () => void }; diff --git a/app/web/src/nova/vi/render/script/eval-prop.tsx b/app/web/src/nova/vi/render/script/eval-prop.tsx index 72e1033a..29173816 100644 --- a/app/web/src/nova/vi/render/script/eval-prop.tsx +++ b/app/web/src/nova/vi/render/script/eval-prop.tsx @@ -3,9 +3,16 @@ import { VG } from "../global"; import { ViRender } from "../render"; import { viScriptArg } from "./arg"; import { replaceWithObject, replacement } from "./eval-script"; +import { extractNavigate } from "./extract-nav"; export const viEvalProps = ( - vi: { meta: VG["meta"]; site: { db: any; api: any } }, + vi: { + mode: VG["mode"]; + meta: VG["meta"]; + site: { db: any; api: any }; + page: VG["page"]; + on_nav_loaded?: VG["on_nav_loaded"]; + }, meta: IMeta, passprop: any ) => { @@ -85,6 +92,10 @@ export const viEvalProps = ( continue; } + if (prop.value) { + extractNavigate(vi, prop.value); + } + const js = prop.valueBuilt || ""; const src = replaceWithObject(js, replacement) || ""; const fn = new Function( diff --git a/app/web/src/nova/vi/render/script/eval-script.tsx b/app/web/src/nova/vi/render/script/eval-script.tsx index e000f950..13bb2f33 100644 --- a/app/web/src/nova/vi/render/script/eval-script.tsx +++ b/app/web/src/nova/vi/render/script/eval-script.tsx @@ -8,13 +8,18 @@ import { viScriptArg } from "./arg"; import { updatePropScope } from "./eval-prop"; import { createViLocal } from "./local"; import { createViPassProp } from "./passprop"; +import { extractNavigate } from "./extract-nav"; +import { w } from "../../../../utils/types/general"; export const viEvalScript = ( vi: { + page: VG["page"]; + mode: VG["mode"]; site: { db: any; api: any }; meta: VG["meta"]; visit?: VG["visit"]; script?: { init_local_effect: any }; + on_nav_loaded?: VG["on_nav_loaded"]; }, meta: IMeta, passprop: any @@ -66,6 +71,10 @@ export const viEvalScript = ( } } + if (!w.isEditor && meta.item.adv?.js) { + extractNavigate(vi, meta.item.adv.js); + } + const js = meta.item.adv?.jsBuilt || ""; const src = replaceWithObject(js, replacement) || ""; const fn = new Function( diff --git a/app/web/src/nova/vi/render/script/extract-nav.tsx b/app/web/src/nova/vi/render/script/extract-nav.tsx new file mode 100644 index 00000000..4eb8f342 --- /dev/null +++ b/app/web/src/nova/vi/render/script/extract-nav.tsx @@ -0,0 +1,58 @@ +import { VG } from "../global"; + +const nav = { timeout: null as any }; + +export const extractNavigate = ( + vi: { page: VG["page"]; on_nav_loaded?: VG["on_nav_loaded"] }, + str: string +) => { + const found_nav = [ + ...findBetween(str, `navigate(`, `)`), + ...findBetween(str, `href = `, `;`), + ]; + + const page_id = vi.page.cur.id; + if (!vi.page.navs[page_id]) { + vi.page.navs[page_id] = new Set(); + } + + for (const url of found_nav) { + vi.page.navs[page_id].add(url); + } + + clearTimeout(nav.timeout); + nav.timeout = setTimeout(() => { + if (vi.on_nav_loaded) { + vi.on_nav_loaded({ + urls: Array.from(vi.page.navs[page_id]), + }); + } + }, 100); +}; + +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/vi/vi.tsx b/app/web/src/nova/vi/vi.tsx index 4f7a7d54..570f4b8a 100644 --- a/app/web/src/nova/vi/vi.tsx +++ b/app/web/src/nova/vi/vi.tsx @@ -1,4 +1,4 @@ -import { FC, Suspense, useState } from "react"; +import { FC, Suspense, useEffect, useState } from "react"; import { useGlobal } from "web-utils"; import { IMeta } from "../ed/logic/ed-global"; import { viLoad } from "./load/load"; @@ -6,6 +6,7 @@ import { VG, ViGlobal } from "./render/global"; import { ViRoot } from "./root"; import { ErrorBox } from "./utils/error-box"; import { render_stat } from "./render/render"; +import { IRoot } from "../../utils/types/root"; const w = window as any; export const Vi: FC<{ @@ -22,6 +23,7 @@ export const Vi: FC<{ visit?: VG["visit"]; render_stat?: "enabled" | "disabled"; on_status_changed?: (status: VG["status"]) => void; + on_nav_loaded?: (arg: { urls: string[] }) => Promise; }> = ({ meta, entry, @@ -32,15 +34,18 @@ export const Vi: FC<{ db, visit, script, + page_id, render_stat: rs, on_status_changed, + on_nav_loaded, }) => { const vi = useGlobal(ViGlobal, "VI"); vi.mode = mode; + vi.on_nav_loaded = on_nav_loaded; w.isMobile = mode === "mobile"; w.isDesktop = mode === "desktop"; - + vi.page.cur.id = page_id; vi.on_status_changes = on_status_changed; if (rs === "disabled") { @@ -61,6 +66,19 @@ export const Vi: FC<{ viLoad(vi, { api_url, site_id }); } + if (on_nav_loaded) { + useEffect(() => { + setTimeout(() => { + const nav = vi.page.navs[vi.page.cur.id]; + if (nav) { + on_nav_loaded({ + urls: Array.from(nav), + }); + } + }, 500); + }, [vi.page.cur.id]); + } + return ( diff --git a/dockerzip b/dockerzip index 3b55090e2c118290dde0a73dee49d421dfb1ef84..46389ec4585c82a801381ec49e4f08e80753f357 100644 GIT binary patch delta 540 zcmeBC=~3Yc@MdP=VgP}>r4xCi+5T_aoce#;WJ3Yri4Gi0c}pihWEGp3z=@PX zVq%XrRB+>7PZp3$4puo#Ws^NwwP2zK914?XvbsS`m4}G}t%4dkS(y!H=H?JKB}S0K z3U(PJg-|zbXIDZJgz5gnt_Kq};BWzHF5^&yi6Ggsi$fPGy7?^!2iTVHoN8DUI&c}m zL@T(ifDH8GR)%RU=bi`FD$c9P1P(A+kb*>BT@bHP0L)v@Yl$Ss$)|%P7sh9SB$vo5 zGx;>1Ad>uFK4iV&{8~t=7x5b)sr<}ugCrLqppPWCK)@PF?vH>8l3bvm0}^kOpdm!1 J@Z=wYA^?L{*gXIM delta 540 zcmeBC=~3Yc@MdP=VgLb+@QFOqY_oMYr_R=$Y$zZ+(Sd_WBYg5hRXWC5wdL)IJ4uZ5&~5x)VF%Fp~ZNOA!J`bcsM1gw$d{s@>L$ps2JAn`T{8bVYG JPyQh&0sz;3ojd>l