Skip to content

Commit

Permalink
Add email template for blog update (#10)
Browse files Browse the repository at this point in the history
This PR creates an email template for blog updates.

<img width="613" alt="image"
src="https://github.com/user-attachments/assets/3ac49628-2e58-4af9-a738-e8a4bc337f7d">
  • Loading branch information
ben-z authored Oct 13, 2024
1 parent 839b6ac commit 3ffabb1
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 13 deletions.
17 changes: 13 additions & 4 deletions cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import { waitForAssets } from '../utils/watcloud-uri';

const program = new Command();

// Returns the email template with the given name and performs any necessary initialization.
async function getTemplate(template_name: string) {
const mod = require(`../emails/${template_name}`);
if (mod.init) {
await mod.init();
}
await waitForAssets();

return mod.default;
}

program
.description('A CLI tool for working with WATcloud emails')

Expand All @@ -19,8 +30,7 @@ program
.option('-p, --pretty', 'Pretty print the output')
.option('-t, --text', 'Generate plain text output')
.action(async (template_name, options) => {
const template = require(`../emails/${template_name}`).default;
await waitForAssets();
const template = await getTemplate(template_name);

let data = {};
if (options.data) {
Expand All @@ -45,8 +55,7 @@ program
.option('-d, --data <data>', 'Data to pass to the template. Should be a JSON array of objects')
.option('-o, --output <output>', 'Output file. A JSON array of HTML and text emails will be written to this file')
.action(async (template_name, options) => {
const template = require(`../emails/${template_name}`).default;
await waitForAssets();
const template = await getTemplate(template_name);

let data = [];
if (options.data) {
Expand Down
2 changes: 1 addition & 1 deletion emails/_common/watcloud-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function WATcloudEmail({
<Html>
<Head />
<Preview>{previewText}</Preview>
<Body style={{ backgroundColor: "#ffffff", margin: "auto", fontFamily: "sans-serif" }}>
<Body style={{ backgroundColor: "#ffffff", color: "#333", margin: "auto", fontFamily: "sans-serif" }}>
<Container style={{ border: "1px solid #eaeaea", borderRadius: "5px", margin: "40px auto", padding: "20px", maxWidth: "600px" }}>
<Img src={getAsset('watcloud-logo').resolveFromCache()} alt="WATcloud Logo" style={{ display: "block", margin: "0 auto" }} height="100" />
{children}
Expand Down
77 changes: 77 additions & 0 deletions emails/blog-update.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {
Body,
Container,
Head,
Heading,
Hr,
Html,
Img,
Link,
Preview,
Section,
Text
} from "@react-email/components";
import dedent from "dedent-js";
import { z } from "zod";
import { WATcloudEmail } from "./_common/watcloud-email";
import { getAsset, registerAsset, WATcloudURI } from "../utils/watcloud-uri";

const WATcloudBlogUpdateEmailProps = z.object({
path: z.string(),
title: z.string(),
author: z.string(),
date: z.string(),
abstract: z.string(),
image: z.string(),
});

type WATcloudBlogUpdateEmailProps = z.infer<typeof WATcloudBlogUpdateEmailProps>;

export function init({ image }: WATcloudBlogUpdateEmailProps) {
registerAsset(image, new WATcloudURI(image));
}

export function WATcloudBlogUpdateEmail(props: WATcloudBlogUpdateEmailProps) {
const { path, title, author, date, abstract, image } = WATcloudBlogUpdateEmailProps.parse(props);

if (process.env.NODE_ENV === "development") {
init(props);
}

const previewText = `New WATcloud Blog Post: ${title}`;

const link = `https://cloud.watonomous.ca/blog/${path}`;

const imageSrc = getAsset(image).resolveFromCache();

return (
<WATcloudEmail previewText={previewText}>
<Text>Hello! WATcloud has published a new blog post.</Text>
<Hr style={{ marginTop: "20px", marginBottom: "20px" }} />
<Img src={imageSrc} alt={title} height="200" />
<Link href={link}>
<Heading as="h2" style={{ marginBottom: 0 }}>{title}</Heading>
</Link>
<Text style={{ color: "#333", marginTop: 0 }}>
By {author} on {date}
</Text>
<Text style={{ color: "#333" }}>
{abstract}
</Text>
<Link href={link}>Read more →</Link>
<Hr style={{ marginTop: "20px", marginBottom: "20px" }} />
<Text style={{ color: "#666", fontSize: "12px" }}>You are receiving this email because you are subscribed to the WATcloud blog.</Text>
</WATcloudEmail>
);
};

WATcloudBlogUpdateEmail.PreviewProps = {
path: "what-is-watcloud",
title: "Under the Hood: What is WATcloud?",
author: "Ben Zhang",
date: "Sunday, September 22, 2024",
abstract: "Curious about how WATcloud makes computing resources easily and fairly accessible to everyone? Join us as we kick off a blog series exploring our four-year journey of refining our compute cluster's hardware and software—from servers and networking to user provisioning and observability systems.",
image: "watcloud://v1/sha256:5cc9868176110e693921b642ef69b43ea1d6728c822d824bb405e1fc1631b345?name=under-the-hood-wide.png",
} as WATcloudBlogUpdateEmailProps;

export default WATcloudBlogUpdateEmail;
12 changes: 6 additions & 6 deletions emails/onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ export const WATcloudOnboardingEmail = (props: WATcloudOnboardingEmailProps) =>
</Text>
<pre style={{ fontFamily: "Courier New, monospace" }}>
{dedent(String.raw`
_∩_
__|_|_
/|__|__\____
| |
${"`"}.(o)-----(o).'
`)}
_∩_
__|_|_
/|__|__\____
| |
${"`"}.(o)-----(o).'
`)}
</pre>
<Text style={{ color: "#000", fontSize: "14px", lineHeight: "24px", marginTop: "10px" }}>
WATcloud Onboarding Bot 🤖
Expand Down
10 changes: 8 additions & 2 deletions utils/watcloud-uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,14 @@ const assetResolverPromises = new Map<string, Promise<string>>();
// Register an asset with a name and a URI. This will also start resolving the asset.
// To wait for all assets to resolve, `await waitForAssets()` after all assets have been registered.
export function registerAsset(name: string, uri: WATcloudURI) {
if (assets.has(name) && !assets.get(name)!.equals(uri)) {
throw new Error(`Asset ${name} is already registered with a different URI (${assets.get(name)!.toString()}). Cannot add asset with URI ${uri.toString()}.`);
if (assets.has(name)) {
const asset = assets.get(name)!;
if (asset.equals(uri)) {
// already registered
return;
} else {
throw new Error(`Asset ${name} is already registered with a different URI (${!asset.toString()}). Cannot add asset with URI ${uri.toString()}.`);
}
}
assets.set(name, uri);
assetResolverPromises.set(name, uri.resolveToURL());
Expand Down

0 comments on commit 3ffabb1

Please sign in to comment.