diff --git a/packages/core/src/message-editor/content.test.ts b/packages/core/src/message-editor/content.test.ts index c7aac6c249..d67a4401af 100644 --- a/packages/core/src/message-editor/content.test.ts +++ b/packages/core/src/message-editor/content.test.ts @@ -6,6 +6,7 @@ import { xmlToContent, xmlToPlainText, } from "./content"; +import { buildGithubRefPlaceholderChip } from "./githubIssueChip"; describe("xmlToContent", () => { it("parses a file tag into a file chip", () => { @@ -97,6 +98,26 @@ describe("xmlToContent", () => { ); }); + it("serializes an unresolved github_pr placeholder chip with an empty title", () => { + const content: EditorContent = { + segments: [ + { + type: "chip", + chip: buildGithubRefPlaceholderChip({ + kind: "pr", + owner: "org", + repo: "repo", + number: 71827, + normalizedUrl: "https://github.com/org/repo/pull/71827", + }), + }, + ], + }; + expect(contentToXml(content)).toBe( + '', + ); + }); + it("round-trips a github_pr chip", () => { const content: EditorContent = { segments: [ diff --git a/packages/core/src/message-editor/content.ts b/packages/core/src/message-editor/content.ts index 8bdf35b75b..4e03bebb0c 100644 --- a/packages/core/src/message-editor/content.ts +++ b/packages/core/src/message-editor/content.ts @@ -21,6 +21,9 @@ export interface MentionChip { skillName?: string; } +/** Chip title shown while a pasted GitHub ref's real title is being fetched. */ +export const GITHUB_REF_PLACEHOLDER_TITLE = "Loading..."; + export interface FileAttachment { id: string; label: string; @@ -83,7 +86,9 @@ export function contentToXml(content: EditorContent): string { case "github_pr": { const labelMatch = chip.label.match(/^#(\d+)(?:\s*-\s*(.*))?$/); const number = labelMatch?.[1] ?? ""; - const title = labelMatch?.[2] ?? ""; + const rawTitle = labelMatch?.[2] ?? ""; + // A chip sent before its title fetch resolves must not leak the placeholder + const title = rawTitle === GITHUB_REF_PLACEHOLDER_TITLE ? "" : rawTitle; return `<${chip.type} number="${escapeXmlAttr(number)}" title="${escapeXmlAttr(title)}" url="${escapedId}" />`; } default: diff --git a/packages/core/src/message-editor/githubIssueChip.ts b/packages/core/src/message-editor/githubIssueChip.ts index 76b1026573..99b91a8c20 100644 --- a/packages/core/src/message-editor/githubIssueChip.ts +++ b/packages/core/src/message-editor/githubIssueChip.ts @@ -1,5 +1,5 @@ import type { GithubRefState } from "@posthog/shared"; -import type { MentionChip } from "./content"; +import { GITHUB_REF_PLACEHOLDER_TITLE, type MentionChip } from "./content"; import type { ParsedGithubIssueUrl } from "./githubIssueUrl"; export interface GithubIssueChipSource { @@ -43,7 +43,7 @@ export function buildGithubRefPlaceholderChip( ): MentionChip { const source = { number: parsed.number, - title: "Loading...", + title: GITHUB_REF_PLACEHOLDER_TITLE, url: parsed.normalizedUrl, }; return parsed.kind === "pr"