Repository: getsentry/sentry

Issue:
User avatars don't show in emails
At least for comment notifications, the avatar of the user who commented is just a blue box with a question mark regardless of whether they have a custom avatar or the default gravatar. We should check if this is happening for other notifications or if it's just the comment workflow email.


Discussion:

Resolving patch:
diff --git a/src/sentry/notifications/notifications/activity/note.py b/src/sentry/notifications/notifications/activity/note.py
--- a/src/sentry/notifications/notifications/activity/note.py
+++ b/src/sentry/notifications/notifications/activity/note.py
@@ -2,6 +2,10 @@
 
 from typing import Any, Mapping, Optional
 
+from django.utils.html import format_html
+from django.utils.safestring import SafeString
+
+from sentry.notifications.utils.avatar import avatar_as_html
 from sentry.services.hybrid_cloud.actor import RpcActor
 from sentry.types.integrations import ExternalProviders
 
@@ -33,3 +37,15 @@ def get_notification_title(
 
     def get_message_description(self, recipient: RpcActor, provider: ExternalProviders) -> Any:
         return self.get_context()["text_description"]
+
+    def description_as_html(self, description: str, params: Mapping[str, Any]) -> SafeString:
+        """Note emails are formatted differently from almost all other activity emails.
+        Rather than passing the `description` as a string to be formatted into HTML with
+        `author` and `an_issue` (see base definition and resolved.py's `get_description`
+        as an example) we are simply passed the comment as a string that needs no formatting,
+        and want the avatar on it's own rather than bundled with the author's display name
+        because the display name is already shown in the notification title."""
+        fmt = '<span class="avatar-container">{}</span>'
+        if self.user:
+            return format_html(fmt, avatar_as_html(self.user, 48))
+        return format_html(description)
diff --git a/src/sentry/notifications/utils/avatar.py b/src/sentry/notifications/utils/avatar.py
--- a/src/sentry/notifications/utils/avatar.py
+++ b/src/sentry/notifications/utils/avatar.py
@@ -38,15 +38,18 @@ def get_sentry_avatar_url() -> str:
     return str(absolute_uri(get_asset_url("sentry", url)))
 
 
-def avatar_as_html(user: User | RpcUser) -> SafeString:
+def avatar_as_html(user: User | RpcUser, size: int = 20) -> SafeString:
     if not user:
         return format_html(
-            '<img class="avatar" src="{}" width="20px" height="20px" />', get_sentry_avatar_url()
+            '<img class="avatar" src="{}" width="{}px" height="{}px" />',
+            get_sentry_avatar_url(),
+            size,
+            size,
         )
     avatar_type = user.get_avatar_type()
     if avatar_type == "upload":
         return format_html('<img class="avatar" src="{}" />', get_user_avatar_url(user))
     elif avatar_type == "letter_avatar":
-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, False)
+        return get_email_avatar(user.get_display_name(), user.get_label(), size, False)
     else:
-        return get_email_avatar(user.get_display_name(), user.get_label(), 20, True)
+        return get_email_avatar(user.get_display_name(), user.get_label(), size, True)


Test patch: