feat(email): convert digest.html to shared partials + brand, loops intact

This commit is contained in:
2026-06-26 23:59:33 +02:00
parent 09d92828c2
commit b9f85d1ac9
2 changed files with 185 additions and 5 deletions
@@ -584,11 +584,11 @@ The largest template — keeps `{{{ each notifications }}}`, `publicRooms`, `top
**Files:** **Files:**
- Modify: `email/digest.html` - Modify: `email/digest.html`
- [ ] **Step 1: Replace the head/header (lines ~1160) with IMPORT header + preheader** - [x] **Step 1: Replace the head/header (lines ~1160) with IMPORT header + preheader**
Prepend `<!-- IMPORT emails/partials/header.html -->` + the two-line preheader block using `[[email:digest.preheader]]`, deleting everything through `<!-- Email Header : END -->`. Prepend `<!-- IMPORT emails/partials/header.html -->` + the two-line preheader block using `[[email:digest.preheader]]`, deleting everything through `<!-- Email Header : END -->`.
- [ ] **Step 2: Apply the palette/font find/replace across the body and every loop row** - [x] **Step 2: Apply the palette/font find/replace across the body and every loop row**
Same find/replace as Task 6, applied to all loop iterations. Specifically: Same find/replace as Task 6, applied to all loop iterations. Specifically:
- card `bgcolor="#ffffff"``#fbf7ef` - card `bgcolor="#ffffff"``#fbf7ef`
@@ -601,11 +601,11 @@ Same find/replace as Task 6, applied to all loop iterations. Specifically:
Leave every `{{{ each }}}`, `{{{ if }}}`, `{var}`, `[[email:digest.*]]`, and `{renderDigestAvatar(@value)}` untouched. Leave every `{{{ each }}}`, `{{{ if }}}`, `{var}`, `[[email:digest.*]]`, and `{renderDigestAvatar(@value)}` untouched.
- [ ] **Step 3: Replace the original footer with IMPORT footer** - [x] **Step 3: Replace the original footer with IMPORT footer**
Delete the digest's own footer table + trailing `</div></center></body></html>`/MSO close; replace with `<!-- IMPORT emails/partials/footer.html -->`. Delete the digest's own footer table + trailing `</div></center></body></html>`/MSO close; replace with `<!-- IMPORT emails/partials/footer.html -->`.
- [ ] **Step 4: Guard passes for all templates** - [x] **Step 4: Guard passes for all templates**
Run: `node scripts/check-emails.js` Run: `node scripts/check-emails.js`
Expected: PASS (`Email template check passed (10 templates).`). Expected: PASS (`Email template check passed (10 templates).`).
@@ -614,7 +614,9 @@ Expected: PASS (`Email template check passed (10 templates).`).
Run: `./nodebb build`. Trigger a digest (ACP → manually run digest, or test account). Confirm loops render, headings are serif/gold-muted, separators visible, button-free. Run: `./nodebb build`. Trigger a digest (ACP → manually run digest, or test account). Confirm loops render, headings are serif/gold-muted, separators visible, button-free.
- [ ] **Step 6: Commit** Task 9 note: `node scripts/check-emails.js` passed with `Email template check passed (10 templates).` Lightweight marker checks confirmed the digest loops and `{renderDigestAvatar(@value)}` remain present. `./nodebb build` and digest test-send were not run because no executable `./nodebb` / NodeBB root is available in this standalone theme repo.
- [x] **Step 6: Commit**
```bash ```bash
git add email/digest.html git add email/digest.html
+178
View File
@@ -0,0 +1,178 @@
<!-- IMPORT emails/partials/header.html -->
<!-- preheader -->
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">[[email:digest.preheader]]</div>
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;&nbsp;&zwnj;</div>
<!-- Email Body : BEGIN -->
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" width="100%" style="max-width: 600px;">
<!-- 1 Column Text + Button : BEGIN -->
<tr>
<td bgcolor="#fbf7ef">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 40px 40px 6px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 24px; line-height: 27px; color: #1a1418; font-weight: normal;">[[email:greeting-with-name, {displayname}]]</h1>
</td>
</tr>
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 0 0 10px 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 18px; line-height: 21px; color: #9a9086; font-weight: normal;">[[email:digest.title.{interval}]]</h1>
</td>
</tr>
{{{ if notifications.length }}}
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<ul style="margin: 0; padding: 0;">
{{{ each notifications }}}
<li style="text-decoration: none; list-style-type: none; padding-bottom: 0.5em;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; width: 32px;">
{{{ if notifications.image }}}
<img style="vertical-align: middle; width: 32px; height: 32px; border-radius: 50%" src="{notifications.image}" alt="" />
{{{ else }}}
<div style="vertical-align: middle; width: 32px; height: 32px; line-height: 32px; font-size: 16px; background-color: {notifications.user.icon:bgColor}; color: white; text-align: center; display: inline-block; border-radius: 50%">{notifications.user.icon:text}</div>
{{{ end }}}
</td>
<td style="padding: 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #a8893f;" href="{notifications.notification_url}">{notifications.bodyShort}</a></p>
</td>
</tr>
</table>
</li>
{{{ end }}}
</ul>
</td>
</tr>
{{{ end }}}
{{{ if publicRooms.length }}}
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 16px 0 24px 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 18px; line-height: 21px; color: #9a9086; font-weight: normal;">[[email:digest.unread-rooms]]</h1>
<ul style="margin: 0; padding: 0;">
{{{ each publicRooms }}}
<li style="text-decoration: none; list-style-type: none; padding-bottom: 0.5em;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #a8893f;" href="{url}/chats/{./roomId}"># [[email:digest.room-name-unreadcount, {./roomName}, {./unreadCountText}]]</a></p>
</td>
</tr>
</table>
</li>
{{{ end }}}
</ul>
</td>
</tr>
{{{ end }}}
{{{ if topTopics.length }}}
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 16px 0 24px 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 18px; line-height: 21px; color: #9a9086; font-weight: normal;">[[email:digest.top-topics, {site_title}]]</h1>
<ul style="margin: 0; padding: 0;">
{{{ each topTopics }}}
<li style="text-decoration: none; list-style-type: none; padding-bottom: 0.5em;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; width: 32px; vertical-align: middle;">{renderDigestAvatar(@value)}</td>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #a8893f;" href="{url}/topic/{topTopics.slug}"><strong>{topTopics.title}</strong></a></p>
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #9a9086; line-height: 16px;" href="{url}/uid/{topTopics.teaser.user.uid}"><strong>{topTopics.teaser.user.displayname}</strong></a></p>
</td>
</tr>
<tr>
<td colspan="2" style="padding: 8px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 13px; line-height: 26px; color: #3a3340;">{topTopics.teaser.content}</p>
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px;">
<a style="text-decoration:none !important; text-decoration:none; text-transform: capitalize; color: #a8893f; line-height: 16px;" href="{url}/topic/{topTopics.slug}">
<small><strong><span style="color: #9a9086;">&rsaquo;</span> [[global:read-more]]</strong></small>
</a>
</p>
</td>
</tr>
</table>
</li>
{{{ if !@last }}}
<li style="text-decoration: none; list-style-type: none; margin: 0px 64px 16px 64px; border-bottom: 1px solid #e2d8c4"></li>
{{{ end }}}
{{{ end }}}
</ul>
</td>
</tr>
{{{ end }}}
{{{ if popularTopics.length }}}
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 16px 0 24px 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 18px; line-height: 21px; color: #9a9086; font-weight: normal;">[[email:digest.popular-topics, {site_title}]]</h1>
<ul style="margin: 0; padding: 0;">
{{{ each popularTopics }}}
<li style="text-decoration: none; list-style-type: none; padding-bottom: 0.5em;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; width: 32px; vertical-align: middle;">{renderDigestAvatar(@value)}</td>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #a8893f;" href="{url}/topic/{popularTopics.slug}"><strong>{popularTopics.title}</strong></a></p>
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #9a9086; line-height: 16px;" href="{url}/uid/{popularTopics.teaser.user.uid}"><strong>{popularTopics.teaser.user.displayname}</strong></a></p>
</td>
</tr>
<tr>
<td colspan="2" style="padding: 8px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 13px; line-height: 26px; color: #3a3340;">{popularTopics.teaser.content}</p>
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px;">
<a style="text-decoration:none !important; text-decoration:none; text-transform: capitalize; color: #a8893f; line-height: 16px;" href="{url}/topic/{popularTopics.slug}">
<small><strong><span style="color: #9a9086;">&rsaquo;</span> [[global:read-more]]</strong></small>
</a>
</p>
</td>
</tr>
</table>
</li>
{{{ if !@last }}}
<li style="text-decoration: none; list-style-type: none; margin: 0px 64px 16px 64px; border-bottom: 1px solid #e2d8c4"></li>
{{{ end }}}
{{{ end }}}
</ul>
</td>
</tr>
{{{ end }}}
{{{ if recent.length }}}
<tr>
<td style="padding: 0px 40px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; line-height: 20px; color: #3a3340;">
<h1 style="margin: 16px 0 24px 0; font-family: Cinzel, Georgia, 'Times New Roman', serif; font-size: 18px; line-height: 21px; color: #9a9086; font-weight: normal;">[[email:digest.latest-topics, {site_title}]]</h1>
<ul style="margin: 0; padding: 0;">
{{{ each recent }}}
<li style="text-decoration: none; list-style-type: none; padding-bottom: 0.5em;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; width: 32px; vertical-align: middle;">{renderDigestAvatar(@value)}</td>
<td style="padding: 6px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0;"><a style="text-decoration:none !important; text-decoration:none; color: #a8893f;" href="{url}/topic/{recent.slug}"><strong>{recent.title}</strong></a></p>
<p style="margin: 0; font-size: 12px;"><a style="text-decoration:none !important; text-decoration:none; color: #9a9086; line-height: 16px;" href="{url}/uid/{recent.teaser.user.uid}"><strong>{recent.teaser.user.displayname}</strong></a></p>
</td>
</tr>
<tr>
<td colspan="2" style="padding: 8px 16px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px; color: #3a3340;">
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 13px; line-height: 26px; color: #3a3340;">{recent.teaser.content}</p>
<p style="margin: 0; padding: 6px 0px; font-family: Jost, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; line-height: 16px;">
<a style="text-decoration:none !important; text-decoration:none; text-transform: capitalize; color: #a8893f; line-height: 16px;" href="{url}/topic/{recent.slug}">
<small><strong><span style="color: #9a9086;">&rsaquo;</span> [[global:read-more]]</strong></small>
</a>
</p>
</td>
</tr>
</table>
</li>
{{{ if !@last }}}
<li style="text-decoration: none; list-style-type: none; margin: 0px 64px 16px 64px; border-bottom: 1px solid #e2d8c4"></li>
{{{ end }}}
{{{ end }}}
</ul>
</td>
</tr>
{{{ end }}}
</table>
</td>
</tr>
<!-- 1 Column Text : END -->
</table>
<!-- Email Body : END -->
<!-- IMPORT emails/partials/footer.html -->