fix: Edits that only include a mention below edit distance do not trigger mention (#11434)

This commit is contained in:
Tom Moor
2026-02-13 18:02:47 -05:00
committed by GitHub
parent 129e872578
commit 797c28a12e
2 changed files with 80 additions and 11 deletions
@@ -1,3 +1,5 @@
import type { DeepPartial } from "utility-types";
import type { ProsemirrorData } from "@shared/types";
import { v4 as uuidv4 } from "uuid";
import { MentionType, NotificationEventType } from "@shared/types";
import { createContext } from "@server/context";
@@ -522,6 +524,71 @@ describe("revisions.create", () => {
expect(spy).not.toHaveBeenCalled();
});
test("should send a mention notification even when change is below threshold", async () => {
const spy = jest.spyOn(Notification, "create");
const actor = await buildUser();
const mentioned = await buildUser({ teamId: actor.teamId, name: "Kim" });
// Build a document with some initial content
let document = await buildDocument({
teamId: actor.teamId,
userId: actor.id,
});
await Revision.createFromDocument(createContext({ user: actor }), document);
// Now add a mention the only change is the mention node itself, which
// renders as "@<label>" in plain text and may be below the 5-char
// threshold that gates generic update notifications.
const mentionContent: DeepPartial<ProsemirrorData> = {
type: "doc",
content: [
...(document.content?.content ?? []),
{
type: "paragraph",
content: [
{
type: "mention",
attrs: {
type: MentionType.User,
label: mentioned.name,
modelId: mentioned.id,
actorId: actor.id,
id: "test-mention-id",
},
},
],
},
],
};
document.content = mentionContent as ProsemirrorData;
document.updatedAt = new Date();
await document.save();
const revision = await Revision.createFromDocument(
createContext({ user: actor }),
document
);
const task = new RevisionCreatedNotificationsTask();
await task.perform({
name: "revisions.create",
documentId: document.id,
teamId: document.teamId,
actorId: actor.id,
modelId: revision.id,
ip,
});
expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
event: NotificationEventType.MentionedInDocument,
userId: mentioned.id,
actorId: actor.id,
documentId: document.id,
})
);
});
test("should not send a notification for group mentions when disableMentions is true", async () => {
const spy = jest.spyOn(Notification, "create");
const actor = await buildUser();
@@ -35,16 +35,8 @@ export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionE
const before = await revision.before();
// If the content looks the same, don't send notifications
if (!DocumentHelper.isChangeOverThreshold(before, revision, 5)) {
Logger.info(
"processor",
`suppressing notifications as update has insignificant changes`
);
return;
}
// Send notifications to mentioned users first
// Send notifications to mentioned users first these must be processed
// regardless of the change threshold as even a small edit can add a mention.
const oldMentions = before
? [...DocumentHelper.parseMentions(before, { type: MentionType.User })]
: [];
@@ -84,7 +76,7 @@ export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionE
}
}
// send notifications to users in mentioned groups
// Send notifications to users in mentioned groups
const oldGroupMentions = before
? DocumentHelper.parseMentions(before, { type: MentionType.Group })
: [];
@@ -148,6 +140,16 @@ export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionE
mentionedGroup.push(group.modelId);
}
// If the content change is insignificant, don't send generic update
// notifications (mention notifications above are still sent).
if (!DocumentHelper.isChangeOverThreshold(before, revision, 5)) {
Logger.info(
"processor",
`suppressing update notifications as change has insignificant edits`
);
return;
}
const recipients = (
await NotificationHelper.getDocumentNotificationRecipients({
document,