feat: added moderation email notifications (#2525)

This commit is contained in:
Wyatt Johnson
2019-09-05 17:33:49 +00:00
committed by GitHub
parent e35978096f
commit 3c9ebcf1a1
10 changed files with 138 additions and 3 deletions
+14
View File
@@ -107,6 +107,20 @@ email-template-notificationOnStaffReply =
{ $organizationName } - <a data-l10n-name="storyLink">{ $storyTitle }</a><br /><br/>
{ $authorUsername } works for { $organizationName } and has replied to your comment: <a data-l10n-name="commentPermalink">View comment</a>
## On Comment Approved
email-subject-notificationOnCommentApproved = Your comment on { $organizationName } has been published
email-template-notificationOnCommentApproved =
{ $organizationName }<br /><br/>
Thank you for submitting your comment. Your comment has now been published: <a data-l10n-name="commentPermalink">View comment</a>
## On Comment Rejected
email-subject-notificationOnCommentRejected = Your comment on { $organizationName } was not published
email-template-notificationOnCommentRejected =
{ $organizationName }<br /><br/>
The language used in one of your comments did not comply with our community guidelines, and so the comment has been removed.
# Notification Digest
email-subject-notificationDigest = Your latest comment activity at { $organizationName }
@@ -8,3 +8,8 @@ export const PUBLISHED_STATUSES = [
GQLCOMMENT_STATUS.NONE,
GQLCOMMENT_STATUS.APPROVED,
];
export const MODERATOR_STATUSES = [
GQLCOMMENT_STATUS.PREMOD,
GQLCOMMENT_STATUS.SYSTEM_WITHHELD,
];
+5 -1
View File
@@ -1,7 +1,7 @@
import { GQLCOMMENT_STATUS } from "coral-server/graph/tenant/schema/__generated__/types";
import { Comment } from "./comment";
import { PUBLISHED_STATUSES } from "./constants";
import { MODERATOR_STATUSES, PUBLISHED_STATUSES } from "./constants";
import { Revision } from "./revision";
/**
@@ -25,6 +25,10 @@ export function hasPublishedStatus(comment: Pick<Comment, "status">): boolean {
return PUBLISHED_STATUSES.includes(comment.status);
}
export function hasModeratorStatus(comment: Pick<Comment, "status">): boolean {
return MODERATOR_STATUSES.includes(comment.status);
}
/**
* getLatestRevision will get the latest revision from a Comment.
*
@@ -45,10 +45,24 @@ export type OnFeaturedTemplate = NotificationContext<
}
>;
export type OnCommentRejectedTemplate = NotificationContext<
"notification/on-comment-rejected",
{}
>;
export type OnCommentApprovedTemplate = NotificationContext<
"notification/on-comment-approved",
{
commentPermalink: string;
}
>;
export type DigestibleTemplate =
| OnReplyTemplate
| OnStaffReplyTemplate
| OnFeaturedTemplate;
| OnFeaturedTemplate
| OnCommentRejectedTemplate
| OnCommentApprovedTemplate;
type DigestTemplate = NotificationContext<
"notification/digest",
@@ -187,6 +201,8 @@ type Templates =
| OnReplyTemplate
| OnStaffReplyTemplate
| OnFeaturedTemplate
| DigestTemplate;
| DigestTemplate
| OnCommentRejectedTemplate
| OnCommentApprovedTemplate;
export { Templates as EmailTemplate };
@@ -0,0 +1 @@
{% extends "layouts/notification.html" %}
@@ -0,0 +1 @@
{% extends "layouts/notification.html" %}
@@ -0,0 +1,4 @@
<div data-l10n-id="email-template-notificationOnCommentApproved" data-l10n-args="{{ context | dump }}">
{{ context.organizationName }}<br /><br/>
Thank you for submitting your comment. Your comment has now been published: <a data-l10n-name="commentPermalink" href="{{ context.commentPermalink }}">View comment</a>
</div>
@@ -0,0 +1,4 @@
<div data-l10n-id="email-template-notificationOnCommentRejected" data-l10n-args="{{ context | dump }}">
{{ context.organizationName }}<br /><br/>
The language used in one of your comments did not comply with our community guidelines, and so the comment has been removed.
</div>
@@ -1,5 +1,6 @@
import { NotificationCategory } from "./category";
import { featured } from "./featured";
import { moderation } from "./moderation";
import { reply } from "./reply";
import { staffReply } from "./staffReply";
@@ -10,6 +11,7 @@ const categories: NotificationCategory[] = [
...reply,
...staffReply,
...featured,
...moderation,
];
export default categories;
@@ -0,0 +1,84 @@
import { CommentStatusUpdatedInput } from "coral-server/graph/tenant/resolvers/Subscription/commentStatusUpdated";
import { SUBSCRIPTION_CHANNELS } from "coral-server/graph/tenant/resolvers/Subscription/types";
import { hasModeratorStatus } from "coral-server/models/comment";
import { GQLCOMMENT_STATUS } from "coral-server/graph/tenant/schema/__generated__/types";
import { getURLWithCommentID } from "coral-server/models/story";
import NotificationContext from "../context";
import { Notification } from "../notification";
import { NotificationCategory } from "./category";
async function processor(
ctx: NotificationContext,
input: CommentStatusUpdatedInput
): Promise<Notification | null> {
// Check to see if this comment was previously in a moderation status.
if (!hasModeratorStatus({ status: input.oldStatus })) {
return null;
}
// Load the comment in question.
const comment = await ctx.comments.load(input.commentID);
if (!comment) {
return null;
}
// Get the comment author.
const author = await ctx.users.load(comment.authorID);
if (!author) {
return null;
}
// Check to see if this user has notifications enabled.
if (!author.notifications.onModeration) {
return null;
}
// Generate the unsubscribe URL.
const unsubscribeURL = await ctx.generateUnsubscribeURL(author);
// Check to see which template we should use.
if (comment.status === GQLCOMMENT_STATUS.APPROVED) {
// Get the story that this was written on.
const story = await ctx.stories.load(comment.storyID);
if (!story) {
return null;
}
return {
userID: author.id,
template: {
name: "notification/on-comment-approved",
context: {
commentPermalink: getURLWithCommentID(story.url, comment.id),
organizationName: ctx.tenant.organization.name,
organizationURL: ctx.tenant.organization.url,
unsubscribeURL,
},
},
};
} else if (comment.status === GQLCOMMENT_STATUS.REJECTED) {
return {
userID: author.id,
template: {
name: "notification/on-comment-rejected",
context: {
organizationName: ctx.tenant.organization.name,
organizationURL: ctx.tenant.organization.url,
unsubscribeURL,
},
},
};
}
return null;
}
export const moderation: NotificationCategory[] = [
{
name: "moderation",
process: processor,
event: SUBSCRIPTION_CHANNELS.COMMENT_STATUS_UPDATED,
digestOrder: 30,
},
];