From dcf700072d1e456a32a47ae7b5f070e1f86f2418 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Mon, 26 Sep 2022 15:43:38 +0200 Subject: [PATCH] Extract email styles into head (#4172) * Extract email styles into head * tsc --- server/emails/templates/BaseEmail.tsx | 9 ++++++ .../templates/DocumentNotificationEmail.tsx | 31 ++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/server/emails/templates/BaseEmail.tsx b/server/emails/templates/BaseEmail.tsx index d26519271..6862b2d15 100644 --- a/server/emails/templates/BaseEmail.tsx +++ b/server/emails/templates/BaseEmail.tsx @@ -81,6 +81,7 @@ export default abstract class BaseEmail { previewText: this.preview(data), component: this.render(data), text: this.renderAsText(data), + headCSS: this.headCSS?.(data), }); Metrics.increment("email.sent", { templateName, @@ -145,6 +146,14 @@ export default abstract class BaseEmail { */ protected abstract render(props: S & T): JSX.Element; + /** + * Allows injecting additional CSS into the head of the email. + * + * @param props Props in email constructor + * @returns A string of CSS + */ + protected headCSS?(props: T): string | undefined; + /** * beforeSend hook allows async loading additional data that was not passed * through the serialized worker props. If false is returned then the email diff --git a/server/emails/templates/DocumentNotificationEmail.tsx b/server/emails/templates/DocumentNotificationEmail.tsx index d99a1693b..577a73f5e 100644 --- a/server/emails/templates/DocumentNotificationEmail.tsx +++ b/server/emails/templates/DocumentNotificationEmail.tsx @@ -1,3 +1,4 @@ +import { JSDOM } from "jsdom"; import * as React from "react"; import { Document } from "@server/models"; import BaseEmail from "./BaseEmail"; @@ -23,6 +24,8 @@ type InputProps = { type BeforeSend = { document: Document; + css: string | undefined; + body: string | undefined; }; type Props = InputProps & BeforeSend; @@ -35,13 +38,27 @@ export default class DocumentNotificationEmail extends BaseEmail< InputProps, BeforeSend > { - protected async beforeSend({ documentId }: InputProps) { + protected async beforeSend({ documentId, content }: InputProps) { const document = await Document.unscoped().findByPk(documentId); if (!document) { return false; } - return { document }; + // extract the css styles so they can be injected into the head of email + // for best compatability + let css, body; + if (content) { + const dom = new JSDOM(content); + const styles = dom.window.document.querySelectorAll("style"); + css = Array.from(styles) + .map((style) => style.innerHTML) + .join(" "); + + styles.forEach((style) => style.remove()); + body = dom.window.document.body.innerHTML; + } + + return { document, body, css }; } protected subject({ document, eventName }: Props) { @@ -68,6 +85,10 @@ Open Document: ${teamUrl}${document.url} `; } + protected headCSS(props: Props) { + return props.css; + } + protected render({ document, actorName, @@ -75,7 +96,7 @@ Open Document: ${teamUrl}${document.url} eventName = "published", teamUrl, unsubscribeUrl, - content, + body, }: Props) { const link = `${teamUrl}${document.url}?ref=notification-email`; @@ -92,11 +113,11 @@ Open Document: ${teamUrl}${document.url} {document.title}, in the {collectionName}{" "} collection.

- {content && ( + {body && ( <> -
+