diff --git a/apps/roam/src/utils/__tests__/roamToCrossAppConverters.test.ts b/apps/roam/src/utils/__tests__/roamToCrossAppConverters.test.ts index 5596eb602..31b1f9ae2 100644 --- a/apps/roam/src/utils/__tests__/roamToCrossAppConverters.test.ts +++ b/apps/roam/src/utils/__tests__/roamToCrossAppConverters.test.ts @@ -9,7 +9,11 @@ vi.mock("roamjs-components/queries/getPageViewType", () => ({ })); vi.mock("~/utils/pageToMarkdown", () => ({ toMarkdown: () => "" })); -import { nodeUidsWithTypeToCrossApp } from "~/utils/roamToCrossAppConverters"; +import { + nodeSchemaToCrossApp, + nodeUidsWithTypeToCrossApp, +} from "~/utils/roamToCrossAppConverters"; +import type { DiscourseNode } from "~/utils/getDiscourseNodes"; const USER_ROW = { ":db/id": 5, ":user/uid": "user-1" }; @@ -60,3 +64,48 @@ describe("nodeUidsWithTypeToCrossApp timestamps", () => { expect(node.modifiedAt).toEqual(new Date(1000)); }); }); + +const nodeSchema = (): DiscourseNode => ({ + text: "Evidence", + type: "_EVD-node", + shortcut: "e", + format: "[[EVD]] - {content}", + specification: [], + backedBy: "user", + canvasSettings: {}, +}); + +// For the timestamp tests: what Roam holds about one node type page. +const convertSchemaPull = (pullResult: Record | null) => { + (globalThis as { window: unknown }).window = { + roamAlphaAPI: { + pull: () => pullResult, + }, + }; + return nodeSchemaToCrossApp(nodeSchema()); +}; + +const schemaPull = { + ":create/time": 1000, + ":create/user": { ":user/uid": "user-1" }, +}; + +describe("nodeSchemaToCrossApp timestamps", () => { + it("takes the page edit time, as written when a block below it changes", () => { + const schema = convertSchemaPull({ + ...schemaPull, + ":edit/time": 2000, + ":page/edit-time": 4000, + }); + expect(schema?.modifiedAt).toEqual(new Date(4000)); + }); + + it("falls back to the create time when neither exists", () => { + const schema = convertSchemaPull(schemaPull); + expect(schema?.modifiedAt).toEqual(new Date(1000)); + }); + + it("is null without an author, rather than a concept that cannot be inserted", () => { + expect(convertSchemaPull({ ":create/time": 1000 })).toBeNull(); + }); +}); diff --git a/apps/roam/src/utils/roamToCrossAppConverters.ts b/apps/roam/src/utils/roamToCrossAppConverters.ts index a3c33f399..cd38f6aee 100644 --- a/apps/roam/src/utils/roamToCrossAppConverters.ts +++ b/apps/roam/src/utils/roamToCrossAppConverters.ts @@ -185,20 +185,25 @@ export const nodeSchemaToCrossApp = ( s: DiscourseNode, ): CrossAppNodeSchema | null => { const relData = window.roamAlphaAPI.pull( - "[:create/time :edit/time {:create/user [:user/uid]}]", + "[:create/time :page/edit-time {:create/user [:user/uid]}]", `[:block/uid "${s.type}"]`, ) as unknown as { ":create/time": number; - ":edit/time": number; + ":page/edit-time"?: number; ":create/user": { ":user/uid": string }; }; if (!relData) return null; const userUid = (relData[":create/user"] ?? {})[":user/uid"]; if (!userUid) return null; + const createdTime = relData[":create/time"] || Date.now(); + // A node type's settings live either in the page's props or in blocks below it, + // but :page/edit-time reflects both. + const pageEditTime = relData[":page/edit-time"] || createdTime; return { localId: s.type, label: s.text, authorId: userUid, - createdAt: new Date(relData[":create/time"] || Date.now()), + createdAt: new Date(createdTime), + modifiedAt: new Date(Math.max(pageEditTime, createdTime)), }; };