️ Improve email rendering speed (#654)

This commit is contained in:
Luke Vella 2023-04-06 15:40:19 +01:00 committed by GitHub
parent c6e61c3a72
commit 42794fedd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 319 additions and 329 deletions

View file

@ -87,6 +87,7 @@ export const sendEmail = async <T extends TemplateName>(
}
const Template = templates[templateName] as TemplateComponent<T>;
const html = render(<Template {...(options.props as any)} />);
try {
await sendRawEmail({
@ -97,7 +98,7 @@ export const sendEmail = async <T extends TemplateName>(
to: options.to,
subject: options.subject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
html: render(<Template {...(options.props as any)} />),
html,
});
return;
} catch (e) {

View file

@ -1,19 +1,15 @@
import tailwindConfig from "@rallly/tailwind-config";
import { absoluteUrl } from "@rallly/utils";
import {
Body,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Section,
} from "@react-email/components";
import { Tailwind } from "@react-email/tailwind";
import { Text } from "./styled-components";
import { fontFamily, Section, Text } from "./styled-components";
interface EmailLayoutProps {
preview: string;
@ -21,6 +17,22 @@ interface EmailLayoutProps {
footNote?: React.ReactNode;
}
const containerStyles = {
maxWidth: "600px",
margin: "0 auto",
fontFamily,
};
const sectionStyles = {
marginTop: "16px",
marginBottom: "16px",
};
const linkStyles = {
color: "#64748B",
marginRight: "8px",
};
export const EmailLayout = ({
preview,
recipientName = "Guest",
@ -32,152 +44,45 @@ export const EmailLayout = ({
<Html>
<Head />
<Preview>{preview}</Preview>
<Tailwind
config={{
theme: {
extend: {
...tailwindConfig.theme.extend,
spacing: {
screen: "100vw",
full: "100%",
px: "1px",
0: "0",
0.5: "2px",
1: "4px",
1.5: "6px",
2: "8px",
2.5: "10px",
3: "12px",
3.5: "14px",
4: "16px",
4.5: "18px",
5: "20px",
5.5: "22px",
6: "24px",
6.5: "26px",
7: "28px",
7.5: "30px",
8: "32px",
8.5: "34px",
9: "36px",
9.5: "38px",
10: "40px",
11: "44px",
12: "48px",
14: "56px",
16: "64px",
20: "80px",
24: "96px",
28: "112px",
32: "128px",
36: "144px",
40: "160px",
44: "176px",
48: "192px",
52: "208px",
56: "224px",
60: "240px",
64: "256px",
72: "288px",
80: "320px",
96: "384px",
97.5: "390px",
120: "480px",
150: "600px",
160: "640px",
175: "700px",
"1/2": "50%",
"1/3": "33.333333%",
"2/3": "66.666667%",
"1/4": "25%",
"2/4": "50%",
"3/4": "75%",
"1/5": "20%",
"2/5": "40%",
"3/5": "60%",
"4/5": "80%",
"1/6": "16.666667%",
"2/6": "33.333333%",
"3/6": "50%",
"4/6": "66.666667%",
"5/6": "83.333333%",
"1/12": "8.333333%",
"2/12": "16.666667%",
"3/12": "25%",
"4/12": "33.333333%",
"5/12": "41.666667%",
"6/12": "50%",
"7/12": "58.333333%",
"8/12": "66.666667%",
"9/12": "75%",
"10/12": "83.333333%",
"11/12": "91.666667%",
},
borderRadius: {
none: "0px",
sm: "2px",
DEFAULT: "4px",
md: "6px",
lg: "8px",
xl: "12px",
"2xl": "16px",
"3xl": "24px",
},
},
},
}}
>
<Body className="bg-white p-3">
<Container className="max-w-xl">
<Section className="my-4">
<Img src={absoluteUrl("/logo.png")} alt="Rallly" width={128} />
</Section>
<Section>
<Text>Hi {firstName},</Text>
{children}
{footNote ? (
<>
<Hr />
<Text light={true}>{footNote}</Text>
</>
) : null}
</Section>
<Section className="mt-4 text-sm text-slate-500">
<Link className="font-sans text-slate-500" href={absoluteUrl()}>
Home
</Link>
&nbsp;&bull;&nbsp;
<Link
className="font-sans text-slate-500"
href="https://twitter.com/ralllyco"
>
Twitter
</Link>
&nbsp;&bull;&nbsp;
<Link
className="font-sans text-slate-500"
href="https://github.com/lukevella/rallly"
>
Github
</Link>
&nbsp;&bull;&nbsp;
<Link
className="font-sans text-slate-500"
href="https://www.paypal.com/donate/?hosted_button_id=7QXP2CUBLY88E"
>
Donate
</Link>
&nbsp;&bull;&nbsp;
<Link
className="font-sans text-slate-500"
href={`mailto:${process.env.SUPPORT_EMAIL}`}
>
Contact
</Link>
</Section>
</Container>
</Body>
</Tailwind>
<Body style={{ backgroundColor: "white", padding: "16px" }}>
<Container style={containerStyles}>
<Img src={absoluteUrl("/logo.png")} alt="Rallly" width={128} />
<Section style={sectionStyles}>
<Text>Hi {firstName},</Text>
{children}
{footNote ? (
<Text style={{ color: "#64748B", fontFamily }}>{footNote}</Text>
) : null}
</Section>
<Section style={{ ...sectionStyles, fontSize: 14 }}>
<Link style={linkStyles} href={absoluteUrl()}>
Home
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link style={linkStyles} href="https://twitter.com/ralllyco">
Twitter
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link style={linkStyles} href="https://github.com/lukevella/rallly">
Github
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link
style={linkStyles}
href="https://www.paypal.com/donate/?hosted_button_id=7QXP2CUBLY88E"
>
Donate
</Link>
<span>&nbsp;&bull;&nbsp;</span>
<Link
style={linkStyles}
href={`mailto:${process.env.SUPPORT_EMAIL}`}
>
Contact
</Link>
</Section>
</Container>
</Body>
</Html>
);
};

View file

@ -1,43 +0,0 @@
import { absoluteUrl } from "@rallly/utils";
import { Hr } from "@react-email/components";
import { Container } from "@react-email/container";
import { Link, SmallText, Text } from "./styled-components";
import { removeProtocalFromUrl } from "./utils";
export interface NewPollBaseEmailProps {
title: string;
name: string;
adminLink: string;
}
export const NewPollBaseEmail = ({
name,
title,
adminLink,
children,
}: React.PropsWithChildren<NewPollBaseEmailProps>) => {
return (
<Container>
<Text>Hi {name},</Text>
<Text>
Your poll <strong>&quot;{title}&quot;</strong> has been created.
</Text>
<Text>
To manage your poll use the <em>admin link</em> below.
</Text>
<Text>
<Link href={adminLink}>
<span className="font-mono">{adminLink}</span> &rarr;
</Link>
</Text>
{children}
<Hr />
<SmallText>
You are receiving this email because a new poll was created with this
email address on{" "}
<Link href={absoluteUrl()}>{removeProtocalFromUrl(absoluteUrl())}</Link>
</SmallText>
</Container>
);
};

View file

@ -10,23 +10,24 @@ import {
Text as UnstyledText,
TextProps,
} from "@react-email/components";
import clsx from "clsx";
import { getDomain } from "./utils";
export const Text = (
props: TextProps & { light?: boolean; small?: boolean },
) => {
const { light, small, className, ...forwardProps } = props;
const { light, small, ...forwardProps } = props;
return (
<UnstyledText
{...forwardProps}
className={clsx(
"my-4 font-sans ",
{ "text-base": !small, "text-sm": small },
{ "text-slate-800": !light, "text-slate-500": light },
className,
)}
style={{
margin: "16px 0",
fontFamily,
fontSize: small ? "14px" : "16px",
color: light ? "#64748B" : "#374151",
lineHeight: "1.5",
...props.style,
}}
/>
);
};
@ -39,10 +40,15 @@ export const Button = (props: ButtonProps) => {
return (
<UnstyledButton
{...props}
className={clsx(
"bg-primary-600 rounded px-3 py-2 font-sans text-white",
props.className,
)}
className={props.className}
style={{
backgroundColor: "#4F46E5",
borderRadius: "4px",
padding: "8px 12px",
fontFamily,
fontSize: "16px",
color: "white",
}}
/>
);
};
@ -51,7 +57,7 @@ export const Link = (props: LinkProps) => {
return (
<UnstyledLink
{...props}
className={clsx("text-primary-600", props.className)}
style={{ color: "#4F46E5", fontFamily, ...props.style }}
/>
);
};
@ -60,35 +66,53 @@ export const Heading = (
props: React.ComponentProps<typeof UnstyledHeading>,
) => {
const { as = "h3" } = props;
const fontSize = {
h1: "32px",
h2: "24px",
h3: "20px",
h4: "16px",
h5: "14px",
h6: "12px",
};
return (
<UnstyledHeading
{...props}
as={as}
className={clsx(
"mt-4 mb-2 font-sans font-semibold text-slate-800",
props.className,
)}
style={{
marginTop: "16px",
marginBottom: "8px",
fontFamily: "sans-serif",
fontWeight: "bold",
fontSize: fontSize[as],
color: "#1E293B",
...props.style,
}}
/>
);
};
export const SubHeadingText = (props: TextProps) => {
const { className, ...forwardProps } = props;
return (
<UnstyledText
{...forwardProps}
className={clsx(
"mb-4 mt-2 font-sans text-base text-slate-800",
className,
)}
{...props}
style={{
marginBottom: "16px",
marginTop: "8px",
fontSize: 16,
color: "#374151",
fontFamily,
...props.style,
}}
/>
);
};
export const Section = (props: SectionProps) => {
const { className, ...forwardProps } = props;
return (
<UnstyledSection {...forwardProps} className={clsx("my-4", className)} />
<UnstyledSection
{...props}
style={{ marginTop: "16px", marginBottom: "16px", ...props.style }}
/>
);
};
@ -96,7 +120,11 @@ export const SmallText = (props: TextProps) => {
return (
<UnstyledText
{...props}
className={clsx("font-sans text-sm text-slate-500", props.className)}
style={{
fontSize: "14px",
color: "#6B7280",
...props.style,
}}
/>
);
};
@ -105,7 +133,20 @@ export const Card = (props: SectionProps) => {
return (
<Section
{...props}
className={clsx("rounded bg-gray-50 px-4", props.className)}
style={{
borderRadius: "4px",
backgroundColor: "#F1F5F9",
paddingRight: "16px",
paddingLeft: "16px",
border: "1px solid #E2E8F0",
}}
/>
);
};
export const trackingWide = {
letterSpacing: 2,
};
export const fontFamily =
"'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;";

View file

@ -5,6 +5,7 @@ import {
Domain,
Heading,
Text,
trackingWide,
} from "./components/styled-components";
import { getDomain } from "./components/utils";
@ -45,7 +46,7 @@ export const LoginEmail = ({
<Card>
<Heading>Option 2: Verification Code</Heading>
<Text>Enter this one-time 6-digit verification code.</Text>
<Heading as="h1" className="tracking-widest" id="code">
<Heading as="h1" style={trackingWide} id="code">
{code}
</Heading>
<Text light={true}>This code will expire in 15 minutes.</Text>

View file

@ -41,6 +41,23 @@ const ShareLink = ({
);
};
const LinkContainer = (props: { href: string }) => {
return (
<Text
style={{
borderRadius: "4px",
backgroundColor: "white",
padding: "12px",
border: "1px solid #E2E8F0",
}}
>
<Link href={props.href} style={{ letterSpacing: 1 }}>
{props.href}
</Link>
</Text>
);
};
export const NewPollEmail = ({
title = "Untitled Poll",
name = "John",
@ -60,18 +77,15 @@ export const NewPollEmail = ({
preview="Share your participant link to start collecting responses."
>
<Text>
Your poll is live! Here are two links you will need to manage your poll.
Your poll for <strong>{title}</strong> is live! Here are two links you
will need to manage your poll.
</Text>
<Card>
<Heading>Admin link</Heading>
<SubHeadingText>
Use this link to view results and make changes to your poll.
</SubHeadingText>
<Text className="rounded bg-white px-4 py-3">
<Link href={adminLink} className="font-mono">
{adminLink}
</Link>
</Text>
<LinkContainer href={adminLink} />
<Text>
<Button href={adminLink}>Go to admin page</Button>
</Text>
@ -82,11 +96,7 @@ export const NewPollEmail = ({
Copy this link and share it with your participants to start collecting
responses.
</SubHeadingText>
<Text className="rounded bg-white px-4 py-3">
<Link href={participantLink} className="font-mono">
{participantLink}
</Link>
</Text>
<LinkContainer href={participantLink} />
<Text>
<ShareLink
title={title}

View file

@ -1,5 +1,10 @@
import { EmailLayout } from "./components/email-layout";
import { Domain, Heading, Text } from "./components/styled-components";
import {
Domain,
Heading,
Text,
trackingWide,
} from "./components/styled-components";
interface RegisterEmailProps {
name: string;
@ -25,7 +30,7 @@ export const RegisterEmail = ({
<Text>
Please use the following 6-digit verification code to verify your email:
</Text>
<Heading as="h1" className="font-sans tracking-widest" id="code">
<Heading as="h1" style={{ ...trackingWide }} id="code">
{code}
</Heading>
<Text>This code is valid for 15 minutes</Text>