You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today's notification emails for Community Discussions and Product Discussions are one-line summaries (e.g. "X commented on your post 'Y' in Z") with a "View notification" button — no comment content, no thread context. This issue covers making those emails rich enough to understand the conversation without opening CourseLit.
Applies to the five conversation activity types:
COMMUNITY_POST_CREATED (new post — include post body excerpt)
COMMUNITY_COMMENT_CREATED
COMMUNITY_REPLY_CREATED
COURSE_DISCUSSION_COMMENT_CREATED (eventType comment_created and reply_created)
All other notification emails remain unchanged, and the in-app (APP) channel is untouched.
Approach
Enrich content queue-side at send time using the existing notification entity resolver — no changes to recordActivity callers, no fatter job metadata in Redis, no stale content if a comment is edited before dispatch.
Extend NotificationEntityResolver (packages/common-logic/src/utils/notification-entity-resolver.ts) with optional getDiscussionComment(commentId, domainId) and getDiscussionReply(replyId, domainId) using the same lazy-model pattern with the product-discussion schemas from @courselit/orm-models. (Community comment content is already reachable via the existing getComment(); add content to the getPost() projection — post content is TextEditorContent | string, handle both.)
New getNotificationEmailContent() in packages/common-logic/src/utils/get-notification-email-content.ts:
Internally calls getNotificationMessageAndHref() for message/href (single source of truth).
Populates the extra fields only for the five conversation activity types; product-discussion content converted with extractTextFromTextEditorContent from @courselit/utils (community content is a plain string).
Caps: comment body ~1000 chars, parent excerpt ~200 chars.
Optional resolveUserName(userId) callback so the queue can resolve parent-author names without common-logic knowing user DB shape.
Extend the email template (apps/queue/src/notifications/services/channels/notification-email-template.ts):
Parent context block (small gray, In reply to {author}: "{excerpt}") when applicable.
Comment body block (15px, line breaks preserved — verify markdown hard-break behavior in renderEmailToHtml).
CTA text "View discussion" for conversation types (default stays "View notification").
All content passed through the existing encodePlainTextForMarkdown.
Wire EmailChannel (apps/queue/src/notifications/services/channels/email.ts): swap getNotificationMessageAndHref → getNotificationEmailContent, pass the new fields into the template, use content.subject as the mail subject.
Subject stays the one-line summary (actor + action + truncated title) for inbox scanability.
Acceptance criteria
New community post notification emails include the post title and a body excerpt.
Community comment/reply notification emails include the comment/reply body, post title, and parent-comment excerpt (for replies).
Product discussion comment/reply notification emails include the content, course title, and parent context.
Emails clearly identify the author and contain a CTA linking to the conversation.
All non-conversation notification emails are byte-for-byte unchanged.
In-app notifications are unchanged.
Unit tests: getNotificationEmailContent (per conversation type + one non-conversation type asserting unchanged output), template block presence/absence.
Out of scope
Reply-by-email (tokens, Reply-To, inbound webhooks) — tracked in #811.
Part 1 of #811 — independently shippable.
Summary
Today's notification emails for Community Discussions and Product Discussions are one-line summaries (e.g. "X commented on your post 'Y' in Z") with a "View notification" button — no comment content, no thread context. This issue covers making those emails rich enough to understand the conversation without opening CourseLit.
Applies to the five conversation activity types:
COMMUNITY_POST_CREATED(new post — include post body excerpt)COMMUNITY_COMMENT_CREATEDCOMMUNITY_REPLY_CREATEDCOURSE_DISCUSSION_COMMENT_CREATED(eventTypecomment_createdandreply_created)All other notification emails remain unchanged, and the in-app (APP) channel is untouched.
Approach
Enrich content queue-side at send time using the existing notification entity resolver — no changes to
recordActivitycallers, no fatter job metadata in Redis, no stale content if a comment is edited before dispatch.Extend
NotificationEntityResolver(packages/common-logic/src/utils/notification-entity-resolver.ts) with optionalgetDiscussionComment(commentId, domainId)andgetDiscussionReply(replyId, domainId)using the same lazy-model pattern with the product-discussion schemas from@courselit/orm-models. (Community comment content is already reachable via the existinggetComment(); addcontentto thegetPost()projection — post content isTextEditorContent | string, handle both.)New
getNotificationEmailContent()inpackages/common-logic/src/utils/get-notification-email-content.ts:{ subject, message, href, commentText?, parentText?, parentAuthorName?, threadTitle?, replyContext? }.getNotificationMessageAndHref()formessage/href(single source of truth).extractTextFromTextEditorContentfrom@courselit/utils(community content is a plain string).resolveUserName(userId)callback so the queue can resolve parent-author names without common-logic knowing user DB shape.replyContextcarries thread coordinates — unused in this phase, consumed later by reply-by-email (Enhanced conversation email notifications and reply-by-email support #811).Extend the email template (
apps/queue/src/notifications/services/channels/notification-email-template.ts):renderEmailToHtml).encodePlainTextForMarkdown.Wire
EmailChannel(apps/queue/src/notifications/services/channels/email.ts): swapgetNotificationMessageAndHref→getNotificationEmailContent, pass the new fields into the template, usecontent.subjectas the mail subject.Acceptance criteria
getNotificationEmailContent(per conversation type + one non-conversation type asserting unchanged output), template block presence/absence.Out of scope
Reply-by-email (tokens, Reply-To, inbound webhooks) — tracked in #811.