Skip intro-author avatar embed when no intro is being sent

Two-level gate: resolve and embed the tree-contact portrait only
when (a) at least one locale on the tree has a non-empty intro on
file, and (b) the specific recipient is still pending delivery
for the current intro version. Recipients who have already seen
this intro, or whose locale has no intro, no longer carry the
extra image bytes.
This commit is contained in:
2026-05-15 16:08:11 +02:00
parent 7402843d07
commit 2f174bb229
+25 -16
View File
@@ -152,9 +152,20 @@ final class NewsletterDispatchService
// The intro paragraph is attributed to the tree contact user // The intro paragraph is attributed to the tree contact user
// (the same person we use as Reply-To). If they're linked to an // (the same person we use as Reply-To). If they're linked to an
// Individual record we fold their avatar into the embed set so // Individual record we fold their avatar into the embed set so
// the editorial block can render with their face on the left, // the editorial block can render with their face on the left.
// matching the styling of the event cards below. // We only bother when at least one locale actually has an intro
$intro_author = $this->resolveIntroAuthor($tree, $reply_to); // to send — otherwise loading/encoding the portrait is wasted
// work and the bytes would be attached to every recipient's
// email without ever being referenced.
$has_any_intro = false;
foreach (array_keys(Configuration::supportedSubjectLocales()) as $code) {
if (trim(Configuration::introForLocale($module, $tree, $code)) !== '') {
$has_any_intro = true;
break;
}
}
$intro_author = $has_any_intro ? $this->resolveIntroAuthor($tree, $reply_to) : null;
if ($intro_author instanceof Individual && !isset($avatars[$this->avatarCidName($intro_author->xref())])) { if ($intro_author instanceof Individual && !isset($avatars[$this->avatarCidName($intro_author->xref())])) {
$author_avatar = $this->resolveAvatar($intro_author); $author_avatar = $this->resolveAvatar($intro_author);
@@ -213,19 +224,6 @@ final class NewsletterDispatchService
$this->avatarKeysForXrefs(array_keys($detailed_set)), $this->avatarKeysForXrefs(array_keys($detailed_set)),
); );
// The intro author sits outside the detailed set —
// make sure their avatar bytes still ride along so
// the editorial portrait renders inline instead of
// showing a broken image (which we'd otherwise hide
// behind a tree-page login link).
if ($intro_author instanceof Individual) {
$author_cid = $this->avatarCidName($intro_author->xref());
if (isset($avatars[$author_cid])) {
$recipient_avatars[$author_cid] = $avatars[$author_cid];
}
}
// Decide whether to attach the intro for *this* // Decide whether to attach the intro for *this*
// recipient: only if it's non-empty AND this // recipient: only if it's non-empty AND this
// recipient hasn't yet received the current // recipient hasn't yet received the current
@@ -237,6 +235,17 @@ final class NewsletterDispatchService
: $external_seen; : $external_seen;
$show_intro = $intro !== '' && $intro_version > $recipient_seen; $show_intro = $intro !== '' && $intro_version > $recipient_seen;
// Only attach the editorial portrait to recipients
// who actually see the intro — otherwise the bytes
// would ride along uselessly and bloat the message.
if ($show_intro && $intro_author instanceof Individual) {
$author_cid = $this->avatarCidName($intro_author->xref());
if (isset($avatars[$author_cid])) {
$recipient_avatars[$author_cid] = $avatars[$author_cid];
}
}
$personalised_intro = $show_intro $personalised_intro = $show_intro
? $this->renderIntroTemplate($intro, $recipient) ? $this->renderIntroTemplate($intro, $recipient)
: ''; : '';