diff --git a/apps/web/pages/api/jobs/notify/[type].ts b/apps/web/pages/api/jobs/notify/[type].ts
index 1b3d4be80..443ee614c 100644
--- a/apps/web/pages/api/jobs/notify/[type].ts
+++ b/apps/web/pages/api/jobs/notify/[type].ts
@@ -12,6 +12,7 @@ import {
createNewMemberNotifications,
NewMemberJoinNotification,
} from '@acter/jobs/new-member-notifications'
+import { createPostMentionNotifications } from '@acter/jobs/post-mention-notifications'
import {
createPostNotifications,
PostJobVariables,
@@ -51,6 +52,10 @@ const notificationTypeMap: Record<
checks: (body: PostJobVariables) => !!body.id,
fn: createPostNotifications,
},
+ [NotificationQueueType.NEW_MENTION]: {
+ checks: (body: PostJobVariables) => !!body.id,
+ fn: createPostMentionNotifications,
+ },
}
const l = getLogger('notifyHandler')
diff --git a/packages/components/user/profile/settings/index.tsx b/packages/components/user/profile/settings/index.tsx
index c51822be1..3cc82b545 100644
--- a/packages/components/user/profile/settings/index.tsx
+++ b/packages/components/user/profile/settings/index.tsx
@@ -77,6 +77,10 @@ export const ProfileSettings: FC = () => {
+
({
+ getJobData: async (job) => {
+ const postMention = await prisma.postMention.findFirst({
+ where: {
+ id: job.id,
+ },
+ })
+ const post = await prisma.post.findFirst({
+ include: {
+ Acter: true,
+ Author: true,
+ },
+ where: {
+ id: postMention.postId,
+ },
+ })
+ return { post, postMention }
+ },
+ getFollowing: async ({ post }) => {
+ return await prisma.acter.findFirst({
+ include: {
+ ActerType: true,
+ Parent: {
+ include: {
+ ActerType: true,
+ },
+ },
+ },
+ where: {
+ id: post.Acter.id,
+ },
+ })
+ },
+ getFollowersWhere: ({ postMention }) => ({
+ Follower: {
+ id: postMention.acterId,
+ },
+ }),
+ getNotificationEmail: ({ data: { post }, notification }) =>
+ createPostMentionEmailNotification({
+ post,
+ notification,
+ }),
+ getNotificationEmailSubject: ({ notification }) =>
+ `New mention on post on ${notification.OnActer.name} via Acter`,
+ getPost: ({ post }) => post as Post,
+ type: NotificationType.NEW_MENTION,
+ getNotificationUrlPath: (postId, following) =>
+ following.ActerType.name === ActerTypes.ACTIVITY
+ ? `activities?activity=${slugify(following.name)}&post=${postId}`
+ : `forum/${postId}`,
+})
diff --git a/services/jobs/post-mention-notifications/index.ts b/services/jobs/post-mention-notifications/index.ts
new file mode 100644
index 000000000..35f64cbb1
--- /dev/null
+++ b/services/jobs/post-mention-notifications/index.ts
@@ -0,0 +1,2 @@
+export { createPostMentionNotifications } from './create-post-mention-notifications'
+export type { PostMentionJobVariables } from './types'
diff --git a/services/jobs/post-mention-notifications/template/index.tsx b/services/jobs/post-mention-notifications/template/index.tsx
new file mode 100644
index 000000000..dfb1e894c
--- /dev/null
+++ b/services/jobs/post-mention-notifications/template/index.tsx
@@ -0,0 +1,74 @@
+import { assert } from 'console'
+import {
+ render,
+ MjmlButton,
+ MjmlColumn,
+ MjmlSection,
+ MjmlText,
+} from 'mjml-react'
+
+import { CreateEmailReturn } from '@acter/lib/email'
+import { getNotificationUrl } from '@acter/lib/notification/get-notification-url'
+import { getArticle } from '@acter/lib/string/get-article'
+import { Acter, Notification, Post } from '@acter/schema'
+
+import { EmailLayout } from '../../templates/layout'
+import { PostMentionEmailBlock } from './post-mention-email-block'
+
+export type PostWithActerAndAuthor = Omit & {
+ Acter: ActerNameAndID
+ Author: ActerNameAndID
+}
+
+type ActerNameAndID = Pick
+
+type CreatePostEmailNotificationParams = {
+ notification: Notification
+ post: PostWithActerAndAuthor
+}
+
+export const createPostMentionEmailNotification = ({
+ notification,
+ post,
+}: CreatePostEmailNotificationParams): CreateEmailReturn => {
+ assert(!!post.Acter?.name, 'Post Acter name required')
+ assert(!!post.Author?.name, 'Post Author name required')
+ assert(!!post.createdAt, 'Post created at required')
+
+ const notificationUrl = getNotificationUrl(notification)
+ const postType = post.parentId ? 'comment' : 'post'
+ const { html } = render(
+
+
+
+
+ A new {postType} created on {post.Acter.name} mentioned you.
+
+
+
+
+
+
+
+ Go To Post
+
+
+
+
+ )
+ const { OnActer } = notification
+ const aAn = getArticle(OnActer.name)
+ const text = `A new ${postType} was created on ${aAn} ${OnActer.ActerType.name} you follow on Acter, ${OnActer.name}. To see it, visit: ${notificationUrl}`
+
+ return { html, text }
+}
diff --git a/services/jobs/post-mention-notifications/template/post-mention-email-block.tsx b/services/jobs/post-mention-notifications/template/post-mention-email-block.tsx
new file mode 100644
index 000000000..e20232b61
--- /dev/null
+++ b/services/jobs/post-mention-notifications/template/post-mention-email-block.tsx
@@ -0,0 +1,44 @@
+import React, { FC } from 'react'
+
+import Markdown from 'markdown-to-jsx'
+import { MjmlColumn, MjmlSection, MjmlText } from 'mjml-react'
+
+import { DATE_TIME_FORMAT_LONG } from '@acter/lib/constants'
+import { parseAndFormat } from '@acter/lib/datetime/parse-and-format'
+import { Post } from '@acter/schema'
+
+export interface PostMentionEmailBlockProps {
+ post: Post
+ notificationUrl: string
+}
+
+export const PostMentionEmailBlock: FC = ({
+ post,
+ notificationUrl,
+}) => {
+ const sentAt = parseAndFormat({
+ dateString: post.createdAt,
+ formatString: DATE_TIME_FORMAT_LONG,
+ })
+ return (
+ <>
+
+
+
+ On {sentAt} {post.Author.name}{' '}
+ mentioned you in the following post:
+
+
+
+
+
+
+
+ {post.content}
+
+
+
+
+ >
+ )
+}
diff --git a/services/jobs/post-mention-notifications/types.ts b/services/jobs/post-mention-notifications/types.ts
new file mode 100644
index 000000000..9613a595a
--- /dev/null
+++ b/services/jobs/post-mention-notifications/types.ts
@@ -0,0 +1,12 @@
+import { PostMention } from '@acter/schema'
+
+import { PostWithActerAndAuthor } from '../post-notifications/template'
+
+export interface PostMentionJobVariables {
+ id: string
+}
+
+export interface PostMentionJobData {
+ postMention: PostMention
+ post: PostWithActerAndAuthor
+}