Skip to content

Richer conversation notification emails (include content and thread context) #825

Description

@rajat1saxena

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_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.

  1. 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.)

  2. New getNotificationEmailContent() in packages/common-logic/src/utils/get-notification-email-content.ts:

    • Returns { subject, message, href, commentText?, parentText?, parentAuthorName?, threadTitle?, replyContext? }.
    • 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.
    • replyContext carries thread coordinates — unused in this phase, consumed later by reply-by-email (Enhanced conversation email notifications and reply-by-email support #811).
  3. 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.
  4. Wire EmailChannel (apps/queue/src/notifications/services/channels/email.ts): swap getNotificationMessageAndHrefgetNotificationEmailContent, 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions