feat: Add @mention support to comments (#5001)

* Refactor, remove confusing 'packages' language

* Basic notifications when mentioned in comment

* fix: Incorrect trimming of comments

* test
This commit is contained in:
Tom Moor
2023-03-06 22:19:49 -05:00
committed by GitHub
parent 28c4854985
commit d3b099819d
18 changed files with 301 additions and 220 deletions

View File

@@ -1,7 +1,38 @@
import Revision from "@server/models/Revision";
import { buildDocument } from "@server/test/factories";
import DocumentHelper from "./DocumentHelper";
describe("DocumentHelper", () => {
describe("parseMentions", () => {
it("should not parse normal links as mentions", async () => {
const document = await buildDocument({
text: `# Header
[link not mention](http://google.com)`,
});
const result = DocumentHelper.parseMentions(document);
expect(result.length).toBe(0);
});
it("should return an array of mentions", async () => {
const document = await buildDocument({
text: `# Header
@[Alan Kay](mention://2767ba0e-ac5c-4533-b9cf-4f5fc456600e/user/34095ac1-c808-45c0-8c6e-6c554497de64) :wink:
More text
@[Bret Victor](mention://34095ac1-c808-45c0-8c6e-6c554497de64/user/2767ba0e-ac5c-4533-b9cf-4f5fc456600e) :fire:`,
});
const result = DocumentHelper.parseMentions(document);
expect(result.length).toBe(2);
expect(result[0].id).toBe("2767ba0e-ac5c-4533-b9cf-4f5fc456600e");
expect(result[1].id).toBe("34095ac1-c808-45c0-8c6e-6c554497de64");
expect(result[0].modelId).toBe("34095ac1-c808-45c0-8c6e-6c554497de64");
expect(result[1].modelId).toBe("2767ba0e-ac5c-4533-b9cf-4f5fc456600e");
});
});
describe("toEmailDiff", () => {
it("should render a compact diff", async () => {
const before = new Revision({

View File

@@ -119,6 +119,17 @@ export default class DocumentHelper {
return output;
}
/**
* Parse a list of mentions contained in a document or revision
*
* @param document Document or Revision
* @returns An array of mentions in passed document or revision
*/
static parseMentions(document: Document | Revision) {
const node = DocumentHelper.toProsemirror(document);
return ProsemirrorHelper.parseMentions(node);
}
/**
* Generates a HTML diff between documents or revisions.
*

View File

@@ -20,8 +20,55 @@ export type HTMLOptions = {
centered?: boolean;
};
type MentionAttrs = {
type: string;
label: string;
modelId: string;
actorId: string | undefined;
id: string;
};
@trace()
export default class ProsemirrorHelper {
/**
* Returns the data as a Prosemirror Node.
*
* @param node The node to parse
* @returns The content as a Prosemirror Node
*/
static toProsemirror(data: Record<string, any>) {
return Node.fromJSON(schema, data);
}
/**
* Returns an array of attributes of all mentions in the node.
*
* @param node The node to parse mentions from
* @returns An array of mention attributes
*/
static parseMentions(node: Node) {
const mentions: MentionAttrs[] = [];
function findMentions(node: Node) {
if (
node.type.name === "mention" &&
!mentions.some((m) => m.id === node.attrs.id)
) {
mentions.push(node.attrs as MentionAttrs);
}
if (!node.content.size) {
return;
}
node.content.descendants(findMentions);
}
findMentions(node);
return mentions;
}
/**
* Returns the node as HTML. This is a lossy conversion and should only be used
* for export.