diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index 89983b43da6..c21d51df108 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -107,12 +107,15 @@ export const authLib = betterAuth({ const locale = "locale" in user ? (user.locale as string) : await getLocale(); - await (await getEmailClient(locale)).sendTemplate("ResetPasswordEmail", { - to: user.email, - props: { - resetLink: url, + await (await getEmailClient({ locale })).sendTemplate( + "ResetPasswordEmail", + { + to: user.email, + props: { + resetLink: url, + }, }, - }); + ); }, onPasswordReset: async ({ user }) => { posthog()?.capture({ @@ -149,7 +152,7 @@ export const authLib = betterAuth({ overrideDefaultEmailVerification: true, async sendVerificationOTP({ email, otp, type }) { const locale = await getLocale(); // TODO: Get locale from email - const emailClient = await getEmailClient(locale); + const emailClient = await getEmailClient({ locale }); switch (type) { // We're not actually using the sign-in type anymore since we just we have `autoSignInAfterVerification` enabled. // This lets us keep things a bit simpler since we share the same verification flow for both login and registration. diff --git a/apps/web/src/trpc/routers/polls.ts b/apps/web/src/trpc/routers/polls.ts index 3e00b789ef3..53386016916 100644 --- a/apps/web/src/trpc/routers/polls.ts +++ b/apps/web/src/trpc/routers/polls.ts @@ -211,9 +211,9 @@ export const polls = router({ }); if (user) { - const emailClient = await getEmailClient( - ctx.user.locale ?? undefined, - ); + const emailClient = await getEmailClient({ + locale: ctx.user.locale ?? undefined, + }); after(() => emailClient.sendTemplate("NewPollEmail", { to: user.email, @@ -917,7 +917,9 @@ export const polls = router({ const hostEmail = poll.user.email; const hostName = poll.user.name; - const emailClient = await getEmailClient(poll.user.locale ?? undefined); + const emailClient = await getEmailClient({ + locale: poll.user.locale ?? undefined, + }); after(() => emailClient.sendTemplate("FinalizeHostEmail", { to: hostEmail, @@ -954,7 +956,9 @@ export const polls = router({ timeZone: scheduledEvent.timeZone, inviteeTimeZone: p.timeZone, }); - const emailClient = await getEmailClient(p.locale ?? undefined); + const emailClient = await getEmailClient({ + locale: p.locale ?? undefined, + }); after(() => emailClient.sendTemplate("FinalizeParticipantEmail", { to: p.email, diff --git a/apps/web/src/trpc/routers/polls/comments.ts b/apps/web/src/trpc/routers/polls/comments.ts index 2afb3f0e02e..a8471d7aa9f 100644 --- a/apps/web/src/trpc/routers/polls/comments.ts +++ b/apps/web/src/trpc/routers/polls/comments.ts @@ -137,9 +137,9 @@ export const comments = router({ }); if (recipient) { - const emailClient = await getEmailClient( - recipient.locale ?? undefined, - ); + const emailClient = await getEmailClient({ + locale: recipient.locale ?? undefined, + }); after(() => emailClient.sendTemplate("NewCommentEmail", { to: recipient.email, diff --git a/apps/web/src/trpc/routers/polls/participants.ts b/apps/web/src/trpc/routers/polls/participants.ts index ff9cfee786d..239adf1b1eb 100644 --- a/apps/web/src/trpc/routers/polls/participants.ts +++ b/apps/web/src/trpc/routers/polls/participants.ts @@ -81,7 +81,9 @@ async function sendNewResponseNotificationEmail({ return; } - const emailClient = await getEmailClient(recipient.locale ?? undefined); + const emailClient = await getEmailClient({ + locale: recipient.locale ?? undefined, + }); await emailClient.sendTemplate("NewParticipantEmail", { to: recipient.email, props: { @@ -286,6 +288,13 @@ export const participants = router({ select: { id: true, title: true, + space: { + select: { + showBranding: true, + primaryColor: true, + image: true, + }, + }, }, }, }, @@ -296,9 +305,18 @@ export const participants = router({ if (email) { const token = await createParticipantEditToken(ctx.user.id); - const emailClient = await getEmailClient( - ctx.user.locale ?? undefined, - ); + const space = participant.poll.space; + const emailClient = await getEmailClient({ + locale: ctx.user.locale ?? undefined, + ...(space?.showBranding + ? { + primaryColor: space.primaryColor ?? undefined, + logoUrl: space.image + ? absoluteUrl(`/api/storage/${space.image}`) + : undefined, + } + : {}), + }); after(() => emailClient.sendTemplate("NewParticipantConfirmationEmail", { diff --git a/apps/web/src/trpc/routers/spaces.ts b/apps/web/src/trpc/routers/spaces.ts index 4acf8133231..08f2789ca3f 100644 --- a/apps/web/src/trpc/routers/spaces.ts +++ b/apps/web/src/trpc/routers/spaces.ts @@ -447,9 +447,9 @@ export const spaces = router({ }, }); - const emailClient = await getEmailClient( - existingUser?.locale ?? ctx.user.locale, - ); + const emailClient = await getEmailClient({ + locale: existingUser?.locale ?? ctx.user.locale, + }); try { await emailClient.sendTemplate("SpaceInviteEmail", { diff --git a/apps/web/src/trpc/routers/user.ts b/apps/web/src/trpc/routers/user.ts index c9f1ab054ec..2923c26b3bc 100644 --- a/apps/web/src/trpc/routers/user.ts +++ b/apps/web/src/trpc/routers/user.ts @@ -115,7 +115,9 @@ export const user = router({ }, ); - const emailClient = await getEmailClient(currentUser.locale ?? undefined); + const emailClient = await getEmailClient({ + locale: currentUser.locale ?? undefined, + }); emailClient.sendTemplate("ChangeEmailRequest", { to: input.email, diff --git a/apps/web/src/utils/emails.ts b/apps/web/src/utils/emails.ts index 6eea12896ca..99e4e1d5f4b 100644 --- a/apps/web/src/utils/emails.ts +++ b/apps/web/src/utils/emails.ts @@ -9,7 +9,11 @@ import { getInstanceBrandingConfig } from "@/features/branding/queries"; const logger = createLogger("emails"); -export const getEmailClient = async (locale?: string) => { +export const getEmailClient = async (options?: { + locale?: string; + primaryColor?: string; + logoUrl?: string; +}) => { const brandingConfig = await getInstanceBrandingConfig(); return new EmailClient({ @@ -23,15 +27,15 @@ export const getEmailClient = async (locale?: string) => { }, }, config: { - logoUrl: brandingConfig.logoIcon, + logoUrl: options?.logoUrl ?? brandingConfig.logoIcon, baseUrl: absoluteUrl(), domain: absoluteUrl().replace(/(^\w+:|^)\/\//, ""), supportEmail: env.SUPPORT_EMAIL, - primaryColor: brandingConfig.primaryColor.light, + primaryColor: options?.primaryColor ?? brandingConfig.primaryColor.light, appName: brandingConfig.appName, hideAttribution: brandingConfig.hideAttribution, }, - locale, + locale: options?.locale, onError: (e) => { logger.error({ error: e }, "Email client error"); Sentry.captureException(e); diff --git a/packages/emails/src/components/email-layout.tsx b/packages/emails/src/components/email-layout.tsx index f60d2729d1d..f79261d25fa 100644 --- a/packages/emails/src/components/email-layout.tsx +++ b/packages/emails/src/components/email-layout.tsx @@ -46,6 +46,7 @@ export const EmailLayout = ({ height="42" style={{ marginBottom: 32, + borderRadius: 6, }} alt={ctx.appName} /> diff --git a/packages/emails/src/components/styled-components.tsx b/packages/emails/src/components/styled-components.tsx index a1e9d13408d..344c07976be 100644 --- a/packages/emails/src/components/styled-components.tsx +++ b/packages/emails/src/components/styled-components.tsx @@ -54,7 +54,7 @@ export const Button = ( className={props.className} style={{ backgroundColor: props.color ?? "#4F46E5", - borderRadius: "4px", + borderRadius: "6px", padding: "14px", fontFamily, boxSizing: "border-box", @@ -148,7 +148,7 @@ export const Card = (props: SectionProps) => {