Files
outline/server/queues/tasks/ReactionCreatedNotificationsTask.ts
T
Tom Moor 42959d66db chore: Add cron task partitioning (#10736)
* wip

* Implementation complete

* tidying

* test

* Address feedback

* Remove duplicative retry logic from UpdateDocumentsPopularityScoreTask.
Now that we're split across many runs this is not neccessary

* Refactor to subclass, config to instance

* Refactor BaseTask to named export

* fix: Missing partition

* tsc

* Feedback
2025-11-27 16:57:52 +01:00

96 lines
2.5 KiB
TypeScript

import { NotificationEventType } from "@shared/types";
import { Comment, Document, Notification, User } from "@server/models";
import { CommentReactionEvent } from "@server/types";
import { canUserAccessDocument } from "@server/utils/permissions";
import { BaseTask, TaskPriority } from "./base/BaseTask";
export default class ReactionCreatedNotificationsTask extends BaseTask<CommentReactionEvent> {
public async perform(event: CommentReactionEvent) {
const { emoji } = event.data;
// Only handle add_reaction events, not remove_reaction
if (event.name !== "comments.add_reaction") {
return;
}
const [document, comment] = await Promise.all([
Document.scope("withCollection").findOne({
where: {
id: event.documentId,
},
}),
Comment.findByPk(event.modelId),
]);
if (!document || !comment) {
return;
}
// Get the user who reacted (the actor)
const actor = await User.findByPk(event.actorId);
if (!actor) {
return;
}
// Get the comment author (the recipient of the notification)
const recipient = await User.findByPk(comment.createdById);
if (!recipient) {
return;
}
// Don't notify if the user reacted to their own comment
if (actor.id === recipient.id) {
return;
}
// Check if the comment author has this notification type enabled
if (
!recipient.subscribedToEventType(NotificationEventType.ReactionsCreate)
) {
return;
}
// Check if the comment author can access the document
if (!(await canUserAccessDocument(recipient, document.id))) {
return;
}
const existing = await Notification.findOne({
where: {
event: NotificationEventType.ReactionsCreate,
userId: recipient.id,
commentId: comment.id,
},
});
if (existing) {
// If a notification already exists for this reaction, update it
// as we have a unique constraint on userId, commentId, and event.
await existing.update({
viewedAt: null,
archivedAt: null,
actorId: actor.id,
data: { emoji },
});
return;
}
// Create the notification
await Notification.create({
event: NotificationEventType.ReactionsCreate,
userId: recipient.id,
actorId: actor.id,
teamId: document.teamId,
commentId: comment.id,
documentId: document.id,
data: { emoji },
});
}
public get options() {
return {
priority: TaskPriority.Background,
};
}
}