From 738f6c693309a2109ad18f3706b83d209e8ca1b7 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sun, 13 Oct 2024 04:19:21 +0000 Subject: [PATCH] Introduce asset registration system --- cli/index.ts | 22 ++------- emails/_common/watcloud-email.tsx | 33 +++++++++++++ emails/onboarding.tsx | 77 +++++++++++++------------------ utils/watcloud-uri.ts | 38 ++++++++++++++- 4 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 emails/_common/watcloud-email.tsx diff --git a/cli/index.ts b/cli/index.ts index 4872148..f399253 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -3,7 +3,7 @@ import { render } from '@react-email/components'; import { Command } from 'commander'; import fs from 'fs'; -import { resolveAll } from '../utils/watcloud-uri'; +import { waitForAssets } from '../utils/watcloud-uri'; const program = new Command(); @@ -19,14 +19,8 @@ program .option('-p, --pretty', 'Pretty print the output') .option('-t, --text', 'Generate plain text output') .action(async (template_name, options) => { - const mod = require(`../emails/${template_name}`); - const template = mod.default; - - const images = mod.images; - if (images) { - // Preload all images - await resolveAll(Object.values(images)); - } + const template = require(`../emails/${template_name}`).default; + await waitForAssets(); let data = {}; if (options.data) { @@ -51,14 +45,8 @@ program .option('-d, --data ', 'Data to pass to the template. Should be a JSON array of objects') .option('-o, --output ', 'Output file. A JSON array of HTML and text emails will be written to this file') .action(async (template_name, options) => { - const mod = require(`../emails/${template_name}`); - const template = mod.default; - - const images = mod.images; - if (images) { - // Preload all images - await resolveAll(Object.values(images)); - } + const template = require(`../emails/${template_name}`).default; + await waitForAssets(); let data = []; if (options.data) { diff --git a/emails/_common/watcloud-email.tsx b/emails/_common/watcloud-email.tsx new file mode 100644 index 0000000..11238be --- /dev/null +++ b/emails/_common/watcloud-email.tsx @@ -0,0 +1,33 @@ +import { + Body, + Container, + Head, + Html, + Img, + Preview +} from "@react-email/components"; +import { getAsset, registerAsset, WATcloudURI } from "../../utils/watcloud-uri"; + +registerAsset('watcloud-logo', new WATcloudURI("watcloud://v1/sha256:393767e36d5387815c15d11c506c3c820de5db41723ffc062751673621dedb15?name=1024x512%20black%401x.png")) + +// Wrapper for WATcloud-themed emails +export function WATcloudEmail({ + previewText, + children, +}: { + previewText: string, + children: React.ReactNode +}) { + return ( + + + {previewText} + + + WATcloud Logo + {children} + + + + ) +} diff --git a/emails/onboarding.tsx b/emails/onboarding.tsx index b79c12d..2c0237d 100644 --- a/emails/onboarding.tsx +++ b/emails/onboarding.tsx @@ -11,11 +11,7 @@ import { } from "@react-email/components"; import dedent from "dedent-js"; import { z } from "zod"; -import { WATcloudURI } from "../utils/watcloud-uri"; - -export const images = { - 'watcloud-logo': new WATcloudURI("watcloud://v1/sha256:393767e36d5387815c15d11c506c3c820de5db41723ffc062751673621dedb15?name=1024x512%20black%401x.png") -} +import { WATcloudEmail } from "./_common/watcloud-email"; const WATcloudOnboardingEmailProps = z.object({ name: z.string(), @@ -37,51 +33,44 @@ export const WATcloudOnboardingEmail = (props: WATcloudOnboardingEmailProps) => ) return ( - - - {previewText} - - - WATcloud Logo - - Hi {name}, - - - Welcome to WATcloud, WATonomous's compute cluster and infrastructure. We are excited to have you on board! - - - This email confirms that your access to WATcloud has been successfully updated. - -
- - Here's a list of services that you have access to: - - {accessInstructions} - - Access instructions for each service can be found in the Services documentation. - -
- - If you have any questions, please reach out to your WATcloud contact or the WATcloud team at infra-outreach@watonomous.ca. - - - Vroom vroom, - -
-                        {dedent(String.raw`
+        
+            
+                Hi {name},
+            
+            
+                Welcome to WATcloud, WATonomous's compute cluster and infrastructure. We are excited to have you on board!
+            
+            
+                This email confirms that your access to WATcloud has been successfully updated.
+            
+            
+ + Here's a list of services that you have access to: + + {accessInstructions} + + Access instructions for each service can be found in the Services documentation. + +
+ + If you have any questions, please reach out to your WATcloud contact or the WATcloud team at infra-outreach@watonomous.ca. + + + Vroom vroom, + +
+                {dedent(String.raw`
                                 _∩_
                               __|_|_
                              /|__|__\____
                             |            |
                             ${"`"}.(o)-----(o).'
                         `)}
-                    
- - WATcloud Onboarding Bot 🤖 - - - - +
+ + WATcloud Onboarding Bot 🤖 + + ); }; diff --git a/utils/watcloud-uri.ts b/utils/watcloud-uri.ts index e585c69..78a8c2c 100644 --- a/utils/watcloud-uri.ts +++ b/utils/watcloud-uri.ts @@ -92,9 +92,45 @@ export class WATcloudURI extends URL { return ret } + + equals(other: WATcloudURI) { + return this.toString() === other.toString(); + } } // Resolve all assets. This can also be used to prewarm the cache. export function resolveAll(assets: WATcloudURI[]) { return Promise.all(assets.map((asset) => asset.resolveToURL())); -} \ No newline at end of file +} + +// MARK: Asset registration +// This section contains code for the asset registration system. +// This system allows for assets to be resolved synchronously. +// Usage: +// 1. Register assets using `registerAsset(name, uri)`. +// 2. Wait for all assets to resolve using `await waitForAssets()`. +// 3. Get an asset using `getAsset(name)`. + +const assets = new Map(); +const assetResolverPromises = new Map>(); + +// 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()}.`); + } + assets.set(name, uri); + assetResolverPromises.set(name, uri.resolveToURL()); +} + +export function getAsset(name: string) { + if (!assets.has(name)) { + throw new Error(`Asset ${name} is not registered.`); + } + return assets.get(name)!; +} + +export function waitForAssets() { + return Promise.all(assetResolverPromises.values()); +}