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

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions