mirror of
https://github.com/lukevella/rallly.git
synced 2025-04-29 10:16:32 +02:00
♻️ Refactor code for generating absolute url (#904)
This commit is contained in:
parent
eaa8f5813d
commit
703d551aac
25 changed files with 108 additions and 61 deletions
|
@ -2,7 +2,6 @@ import "tailwindcss/tailwind.css";
|
||||||
import "../style.css";
|
import "../style.css";
|
||||||
|
|
||||||
import { trpc, UserSession } from "@rallly/backend/next/trpc/client";
|
import { trpc, UserSession } from "@rallly/backend/next/trpc/client";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { inject } from "@vercel/analytics";
|
import { inject } from "@vercel/analytics";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||||
|
@ -16,6 +15,8 @@ import { appWithTranslation } from "next-i18next";
|
||||||
import { DefaultSeo, SoftwareAppJsonLd } from "next-seo";
|
import { DefaultSeo, SoftwareAppJsonLd } from "next-seo";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
|
|
||||||
import * as nextI18nNextConfig from "../../next-i18next.config.js";
|
import * as nextI18nNextConfig from "../../next-i18next.config.js";
|
||||||
import { NextPageWithLayout } from "../types";
|
import { NextPageWithLayout } from "../types";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { ArrowLeftIcon } from "@rallly/icons";
|
import { ArrowLeftIcon } from "@rallly/icons";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { GetStaticPropsContext } from "next";
|
import { GetStaticPropsContext } from "next";
|
||||||
import ErrorPage from "next/error";
|
import ErrorPage from "next/error";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
@ -14,6 +13,7 @@ import { getBlogLayout } from "@/components/layouts/blog-layout";
|
||||||
import { getAllPosts, getPostBySlug } from "@/lib/api";
|
import { getAllPosts, getPostBySlug } from "@/lib/api";
|
||||||
import markdownToHtml from "@/lib/markdownToHtml";
|
import markdownToHtml from "@/lib/markdownToHtml";
|
||||||
import { NextPageWithLayout, Post } from "@/types";
|
import { NextPageWithLayout, Post } from "@/types";
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
import { getStaticTranslations } from "@/utils/page-translations";
|
import { getStaticTranslations } from "@/utils/page-translations";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|
|
@ -28,8 +28,3 @@ export function absoluteUrl(subpath = "", query?: Record<string, string>) {
|
||||||
|
|
||||||
return joinPath(baseUrl, subpath) + queryString;
|
return joinPath(baseUrl, subpath) + queryString;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shortUrl(subpath = "") {
|
|
||||||
const baseUrl = process.env.NEXT_PUBLIC_SHORT_BASE_URL ?? absoluteUrl();
|
|
||||||
return joinPath(baseUrl, subpath);
|
|
||||||
}
|
|
8
apps/web/declarations/environment.d.ts
vendored
8
apps/web/declarations/environment.d.ts
vendored
|
@ -9,10 +9,6 @@ declare global {
|
||||||
* "development" or "production"
|
* "development" or "production"
|
||||||
*/
|
*/
|
||||||
NODE_ENV: "development" | "production";
|
NODE_ENV: "development" | "production";
|
||||||
/**
|
|
||||||
* Set to "true" to take users straight to app instead of landing page
|
|
||||||
*/
|
|
||||||
DISABLE_LANDING_PAGE?: string;
|
|
||||||
/**
|
/**
|
||||||
* Must be 32 characters long
|
* Must be 32 characters long
|
||||||
*/
|
*/
|
||||||
|
@ -84,10 +80,6 @@ declare global {
|
||||||
* The app version just for reference
|
* The app version just for reference
|
||||||
*/
|
*/
|
||||||
NEXT_PUBLIC_APP_VERSION?: string;
|
NEXT_PUBLIC_APP_VERSION?: string;
|
||||||
/**
|
|
||||||
* "true" to enable finalization of polls
|
|
||||||
*/
|
|
||||||
NEXT_PUBLIC_ENABLE_FINALIZATION?: string;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@rallly/ui/dialog";
|
} from "@rallly/ui/dialog";
|
||||||
import { shortUrl } from "@rallly/utils";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useCopyToClipboard } from "react-use";
|
import { useCopyToClipboard } from "react-use";
|
||||||
|
@ -16,6 +15,7 @@ import { useCopyToClipboard } from "react-use";
|
||||||
import { useParticipants } from "@/components/participants-provider";
|
import { useParticipants } from "@/components/participants-provider";
|
||||||
import { Trans } from "@/components/trans";
|
import { Trans } from "@/components/trans";
|
||||||
import { usePoll } from "@/contexts/poll";
|
import { usePoll } from "@/contexts/poll";
|
||||||
|
import { shortUrl } from "@/utils/absolute-url";
|
||||||
import { isSelfHosted } from "@/utils/constants";
|
import { isSelfHosted } from "@/utils/constants";
|
||||||
|
|
||||||
export const InviteDialog = () => {
|
export const InviteDialog = () => {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { getSession } from "@rallly/backend/next/session";
|
import { getSession } from "@rallly/backend/next/session";
|
||||||
import { stripe } from "@rallly/backend/stripe";
|
import { stripe } from "@rallly/backend/stripe";
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
edge: true,
|
edge: true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { getSession } from "@rallly/backend/next/session";
|
import { getSession } from "@rallly/backend/next/session";
|
||||||
import { stripe } from "@rallly/backend/stripe";
|
import { stripe } from "@rallly/backend/stripe";
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
|
|
||||||
const inputSchema = z.object({
|
const inputSchema = z.object({
|
||||||
session_id: z.string().optional(),
|
session_id: z.string().optional(),
|
||||||
return_path: z.string().optional(),
|
return_path: z.string().optional(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { trpcNextApiHandler } from "@rallly/backend/next/trpc/server";
|
import { trpcNextApiHandler } from "@rallly/backend/next/trpc/server";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
import { absoluteUrl, shortUrl } from "@/utils/absolute-url";
|
||||||
import { getServerSession, isEmailBlocked } from "@/utils/auth";
|
import { getServerSession, isEmailBlocked } from "@/utils/auth";
|
||||||
import { isSelfHosted } from "@/utils/constants";
|
import { isSelfHosted } from "@/utils/constants";
|
||||||
import { emailClient } from "@/utils/emails";
|
import { emailClient } from "@/utils/emails";
|
||||||
|
@ -31,5 +32,7 @@ export default async function handler(
|
||||||
emailClient,
|
emailClient,
|
||||||
isSelfHosted,
|
isSelfHosted,
|
||||||
isEmailBlocked,
|
isEmailBlocked,
|
||||||
|
absoluteUrl,
|
||||||
|
shortUrl,
|
||||||
})(req, res);
|
})(req, res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ import { trpc } from "@rallly/backend";
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { ArrowUpLeftIcon } from "@rallly/icons";
|
import { ArrowUpLeftIcon } from "@rallly/icons";
|
||||||
import { Button } from "@rallly/ui/button";
|
import { Button } from "@rallly/ui/button";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { GetStaticProps } from "next";
|
import { GetStaticProps } from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
@ -19,6 +18,7 @@ import { UserProvider, useUser } from "@/components/user-provider";
|
||||||
import { VisibilityProvider } from "@/components/visibility";
|
import { VisibilityProvider } from "@/components/visibility";
|
||||||
import { PermissionsContext } from "@/contexts/permissions";
|
import { PermissionsContext } from "@/contexts/permissions";
|
||||||
import { usePoll } from "@/contexts/poll";
|
import { usePoll } from "@/contexts/poll";
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
import { ConnectedDayjsProvider } from "@/utils/dayjs";
|
import { ConnectedDayjsProvider } from "@/utils/dayjs";
|
||||||
import { getStaticTranslations } from "@/utils/with-page-translations";
|
import { getStaticTranslations } from "@/utils/with-page-translations";
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,30 @@ const getVercelUrl = () => {
|
||||||
: null;
|
: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function absoluteUrl(path = "") {
|
function joinPath(baseUrl: string, subpath = "") {
|
||||||
|
if (subpath) {
|
||||||
|
const url = new URL(subpath, baseUrl);
|
||||||
|
return url.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function absoluteUrl(subpath = "", query?: Record<string, string>) {
|
||||||
|
const queryString = query
|
||||||
|
? `?${Object.entries(query)
|
||||||
|
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
||||||
|
.join("&")}`
|
||||||
|
: "";
|
||||||
const baseUrl =
|
const baseUrl =
|
||||||
process.env.NEXT_PUBLIC_BASE_URL ??
|
process.env.NEXT_PUBLIC_BASE_URL ??
|
||||||
getVercelUrl() ??
|
getVercelUrl() ??
|
||||||
`http://localhost:${port}`;
|
`http://localhost:${port}`;
|
||||||
return `${baseUrl}${path}`;
|
|
||||||
|
return joinPath(baseUrl, subpath) + queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shortUrl(subpath = "") {
|
||||||
|
const baseUrl = process.env.NEXT_PUBLIC_SHORT_BASE_URL ?? absoluteUrl();
|
||||||
|
return joinPath(baseUrl, subpath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { EmailClient, SupportedEmailProviders } from "@rallly/emails";
|
import { EmailClient, SupportedEmailProviders } from "@rallly/emails";
|
||||||
|
|
||||||
|
import { absoluteUrl } from "@/utils/absolute-url";
|
||||||
|
|
||||||
const env = process.env["NODE" + "_ENV"];
|
const env = process.env["NODE" + "_ENV"];
|
||||||
|
|
||||||
export const emailClient = new EmailClient({
|
export const emailClient = new EmailClient({
|
||||||
|
@ -16,4 +18,8 @@ export const emailClient = new EmailClient({
|
||||||
(process.env.SUPPORT_EMAIL as string),
|
(process.env.SUPPORT_EMAIL as string),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
context: {
|
||||||
|
logoUrl: absoluteUrl("/logo.png"),
|
||||||
|
baseUrl: absoluteUrl(""),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,13 @@ export interface TRPCContext {
|
||||||
emailClient: EmailClient;
|
emailClient: EmailClient;
|
||||||
isSelfHosted: boolean;
|
isSelfHosted: boolean;
|
||||||
isEmailBlocked?: (email: string) => boolean;
|
isEmailBlocked?: (email: string) => boolean;
|
||||||
|
/**
|
||||||
|
* Takes a relative path and returns an absolute URL to the app
|
||||||
|
* @param path
|
||||||
|
* @returns absolute URL
|
||||||
|
*/
|
||||||
|
absoluteUrl: (path?: string) => string;
|
||||||
|
shortUrl: (path?: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const trpcNextApiHandler = (context: TRPCContext) => {
|
export const trpcNextApiHandler = (context: TRPCContext) => {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl, shortUrl } from "@rallly/utils";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import timezone from "dayjs/plugin/timezone";
|
import timezone from "dayjs/plugin/timezone";
|
||||||
|
@ -121,9 +120,9 @@ export const polls = router({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const pollLink = absoluteUrl(`/poll/${pollId}`);
|
const pollLink = ctx.absoluteUrl(`/poll/${pollId}`);
|
||||||
|
|
||||||
const participantLink = shortUrl(`/invite/${pollId}`);
|
const participantLink = ctx.shortUrl(`/invite/${pollId}`);
|
||||||
|
|
||||||
if (ctx.user.isGuest === false) {
|
if (ctx.user.isGuest === false) {
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
|
@ -658,7 +657,7 @@ export const polls = router({
|
||||||
to: poll.user.email,
|
to: poll.user.email,
|
||||||
props: {
|
props: {
|
||||||
name: poll.user.name,
|
name: poll.user.name,
|
||||||
pollUrl: absoluteUrl(`/poll/${poll.id}`),
|
pollUrl: ctx.absoluteUrl(`/poll/${poll.id}`),
|
||||||
location: poll.location,
|
location: poll.location,
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
attendees: poll.participants
|
attendees: poll.participants
|
||||||
|
@ -682,7 +681,7 @@ export const polls = router({
|
||||||
to: p.email,
|
to: p.email,
|
||||||
props: {
|
props: {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
pollUrl: absoluteUrl(`/poll/${poll.id}`),
|
pollUrl: ctx.absoluteUrl(`/poll/${poll.id}`),
|
||||||
location: poll.location,
|
location: poll.location,
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
hostName: poll.user?.name ?? "",
|
hostName: poll.user?.name ?? "",
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createToken } from "../../../session";
|
import { createToken } from "../../../session";
|
||||||
|
@ -87,8 +86,8 @@ export const comments = router({
|
||||||
props: {
|
props: {
|
||||||
name: watcher.user.name,
|
name: watcher.user.name,
|
||||||
authorName,
|
authorName,
|
||||||
pollUrl: absoluteUrl(`/poll/${poll.id}`),
|
pollUrl: ctx.absoluteUrl(`/poll/${poll.id}`),
|
||||||
disableNotificationsUrl: absoluteUrl(
|
disableNotificationsUrl: ctx.absoluteUrl(
|
||||||
`/auth/disable-notifications?token=${token}`,
|
`/auth/disable-notifications?token=${token}`,
|
||||||
),
|
),
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { prisma } from "@rallly/database";
|
import { prisma } from "@rallly/database";
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
@ -112,7 +111,7 @@ export const participants = router({
|
||||||
props: {
|
props: {
|
||||||
name,
|
name,
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
editSubmissionUrl: absoluteUrl(
|
editSubmissionUrl: ctx.absoluteUrl(
|
||||||
`/invite/${poll.id}?token=${token}`,
|
`/invite/${poll.id}?token=${token}`,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -149,8 +148,8 @@ export const participants = router({
|
||||||
props: {
|
props: {
|
||||||
name: watcher.user.name,
|
name: watcher.user.name,
|
||||||
participantName: participant.name,
|
participantName: participant.name,
|
||||||
pollUrl: absoluteUrl(`/poll/${poll.id}`),
|
pollUrl: ctx.absoluteUrl(`/poll/${poll.id}`),
|
||||||
disableNotificationsUrl: absoluteUrl(
|
disableNotificationsUrl: ctx.absoluteUrl(
|
||||||
`/auth/disable-notifications?token=${token}`,
|
`/auth/disable-notifications?token=${token}`,
|
||||||
),
|
),
|
||||||
title: poll.title,
|
title: poll.title,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import previewEmail from "preview-email";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import * as templates from "./templates";
|
import * as templates from "./templates";
|
||||||
|
import { EmailContext } from "./templates/components/email-context";
|
||||||
|
|
||||||
type Templates = typeof templates;
|
type Templates = typeof templates;
|
||||||
|
|
||||||
|
@ -15,7 +16,6 @@ type TemplateName = keyof typeof templates;
|
||||||
type TemplateProps<T extends TemplateName> = React.ComponentProps<
|
type TemplateProps<T extends TemplateName> = React.ComponentProps<
|
||||||
TemplateComponent<T>
|
TemplateComponent<T>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type TemplateComponent<T extends TemplateName> = Templates[T];
|
type TemplateComponent<T extends TemplateName> = Templates[T];
|
||||||
|
|
||||||
type SendEmailOptions<T extends TemplateName> = {
|
type SendEmailOptions<T extends TemplateName> = {
|
||||||
|
@ -59,6 +59,10 @@ type EmailClientConfig = {
|
||||||
address: string;
|
address: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* Context to pass to each email
|
||||||
|
*/
|
||||||
|
context: EmailContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class EmailClient {
|
export class EmailClient {
|
||||||
|
@ -79,8 +83,14 @@ export class EmailClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Template = templates[templateName] as TemplateComponent<T>;
|
const Template = templates[templateName] as TemplateComponent<T>;
|
||||||
|
const html = render(
|
||||||
|
<EmailContext.Provider value={this.config.context}>
|
||||||
|
<Template
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const html = render(<Template {...(options.props as any)} />);
|
{...(options.props as any)}
|
||||||
|
/>
|
||||||
|
</EmailContext.Provider>,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.sendEmail({
|
await this.sendEmail({
|
||||||
|
|
19
packages/emails/src/templates/components/email-context.tsx
Normal file
19
packages/emails/src/templates/components/email-context.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export type EmailContext = {
|
||||||
|
logoUrl: string;
|
||||||
|
baseUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmailContext = React.createContext<EmailContext>({
|
||||||
|
logoUrl: "",
|
||||||
|
baseUrl: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useEmailContext = () => {
|
||||||
|
const context = React.useContext(EmailContext);
|
||||||
|
return {
|
||||||
|
...context,
|
||||||
|
domain: context.baseUrl.replace(/(^\w+:|^)\/\//, ""),
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Container,
|
Container,
|
||||||
|
@ -9,6 +8,7 @@ import {
|
||||||
Preview,
|
Preview,
|
||||||
} from "@react-email/components";
|
} from "@react-email/components";
|
||||||
|
|
||||||
|
import { useEmailContext } from "./email-context";
|
||||||
import { fontFamily, Section, Text } from "./styled-components";
|
import { fontFamily, Section, Text } from "./styled-components";
|
||||||
|
|
||||||
export interface EmailLayoutProps {
|
export interface EmailLayoutProps {
|
||||||
|
@ -43,13 +43,14 @@ export const EmailLayout = ({
|
||||||
children,
|
children,
|
||||||
footNote,
|
footNote,
|
||||||
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
}: React.PropsWithChildren<EmailLayoutProps>) => {
|
||||||
|
const { logoUrl, baseUrl } = useEmailContext();
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
<Head />
|
<Head />
|
||||||
<Preview>{preview}</Preview>
|
<Preview>{preview}</Preview>
|
||||||
<Body style={{ backgroundColor: "#F3F4F6", padding: "16px" }}>
|
<Body style={{ backgroundColor: "#F3F4F6", padding: "16px" }}>
|
||||||
<Container style={containerStyles}>
|
<Container style={containerStyles}>
|
||||||
<Img src={absoluteUrl("/logo.png")} alt="Rallly" width={128} />
|
<Img src={logoUrl} alt="Rallly" width={128} />
|
||||||
<Section style={sectionStyles}>
|
<Section style={sectionStyles}>
|
||||||
<Text>Hi {recipientName},</Text>
|
<Text>Hi {recipientName},</Text>
|
||||||
{children}
|
{children}
|
||||||
|
@ -68,7 +69,7 @@ export const EmailLayout = ({
|
||||||
) : null}
|
) : null}
|
||||||
</Section>
|
</Section>
|
||||||
<Section style={{ ...sectionStyles, fontSize: 14, marginBottom: 0 }}>
|
<Section style={{ ...sectionStyles, fontSize: 14, marginBottom: 0 }}>
|
||||||
<Link style={linkStyles} href={absoluteUrl()}>
|
<Link style={linkStyles} href={baseUrl}>
|
||||||
Home
|
Home
|
||||||
</Link>
|
</Link>
|
||||||
<span> • </span>
|
<span> • </span>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { useEmailContext } from "./email-context";
|
||||||
import { EmailLayout } from "./email-layout";
|
import { EmailLayout } from "./email-layout";
|
||||||
import { Button, Link, Text } from "./styled-components";
|
import { Button, Link, Text } from "./styled-components";
|
||||||
import { getDomain } from "./utils";
|
|
||||||
|
|
||||||
export interface NotificationBaseProps {
|
export interface NotificationBaseProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -20,6 +20,7 @@ export const NotificationEmail = ({
|
||||||
preview,
|
preview,
|
||||||
children,
|
children,
|
||||||
}: React.PropsWithChildren<NotificationEmailProps>) => {
|
}: React.PropsWithChildren<NotificationEmailProps>) => {
|
||||||
|
const { domain } = useEmailContext();
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
recipientName={name}
|
recipientName={name}
|
||||||
|
@ -36,7 +37,7 @@ export const NotificationEmail = ({
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<Text>
|
<Text>
|
||||||
<Button href={pollUrl}>View on {getDomain()}</Button>
|
<Button href={pollUrl}>View on {domain}</Button>
|
||||||
</Text>
|
</Text>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
import {
|
import {
|
||||||
Button as UnstyledButton,
|
Button as UnstyledButton,
|
||||||
ButtonProps,
|
ButtonProps,
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
TextProps,
|
TextProps,
|
||||||
} from "@react-email/components";
|
} from "@react-email/components";
|
||||||
|
|
||||||
import { getDomain } from "./utils";
|
import { useEmailContext } from "./email-context";
|
||||||
|
|
||||||
export const borderColor = "#E2E8F0";
|
export const borderColor = "#E2E8F0";
|
||||||
export const Text = (
|
export const Text = (
|
||||||
|
@ -34,7 +33,8 @@ export const Text = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Domain = () => {
|
export const Domain = () => {
|
||||||
return <Link href={absoluteUrl()}>{getDomain()}</Link>;
|
const { baseUrl, domain } = useEmailContext();
|
||||||
|
return <Link href={baseUrl}>{domain}</Link>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Button = (props: ButtonProps) => {
|
export const Button = (props: ButtonProps) => {
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
|
||||||
|
|
||||||
export const removeProtocalFromUrl = (url: string) => {
|
|
||||||
return url.replace(/(^\w+:|^)\/\//, "");
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDomain = () => removeProtocalFromUrl(absoluteUrl());
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useEmailContext } from "./components/email-context";
|
||||||
import { EmailLayout } from "./components/email-layout";
|
import { EmailLayout } from "./components/email-layout";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
@ -7,7 +8,6 @@ import {
|
||||||
Text,
|
Text,
|
||||||
trackingWide,
|
trackingWide,
|
||||||
} from "./components/styled-components";
|
} from "./components/styled-components";
|
||||||
import { getDomain } from "./components/utils";
|
|
||||||
|
|
||||||
interface LoginEmailProps {
|
interface LoginEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -20,6 +20,7 @@ export const LoginEmail = ({
|
||||||
code = "123456",
|
code = "123456",
|
||||||
magicLink = "https://rallly.co",
|
magicLink = "https://rallly.co",
|
||||||
}: LoginEmailProps) => {
|
}: LoginEmailProps) => {
|
||||||
|
const { domain } = useEmailContext();
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
footNote={
|
footNote={
|
||||||
|
@ -39,7 +40,7 @@ export const LoginEmail = ({
|
||||||
<Heading>Option 1: Magic Link</Heading>
|
<Heading>Option 1: Magic Link</Heading>
|
||||||
<Text>Click this magic link to log in on this device.</Text>
|
<Text>Click this magic link to log in on this device.</Text>
|
||||||
<Button href={magicLink} id="magicLink">
|
<Button href={magicLink} id="magicLink">
|
||||||
Log in to {getDomain()}
|
Log in to {domain}
|
||||||
</Button>
|
</Button>
|
||||||
<Text light={true}>This link will expire in 15 minutes.</Text>
|
<Text light={true}>This link will expire in 15 minutes.</Text>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { useEmailContext } from "./components/email-context";
|
||||||
import { EmailLayout } from "./components/email-layout";
|
import { EmailLayout } from "./components/email-layout";
|
||||||
import { Button, Domain, Section, Text } from "./components/styled-components";
|
import { Button, Domain, Section, Text } from "./components/styled-components";
|
||||||
import { getDomain } from "./components/utils";
|
|
||||||
|
|
||||||
interface NewParticipantConfirmationEmailProps {
|
interface NewParticipantConfirmationEmailProps {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -12,6 +12,7 @@ export const NewParticipantConfirmationEmail = ({
|
||||||
name = "John",
|
name = "John",
|
||||||
editSubmissionUrl = "https://rallly.co",
|
editSubmissionUrl = "https://rallly.co",
|
||||||
}: NewParticipantConfirmationEmailProps) => {
|
}: NewParticipantConfirmationEmailProps) => {
|
||||||
|
const { domain } = useEmailContext();
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
footNote={
|
footNote={
|
||||||
|
@ -32,7 +33,7 @@ export const NewParticipantConfirmationEmail = ({
|
||||||
</Text>
|
</Text>
|
||||||
<Section>
|
<Section>
|
||||||
<Button id="editSubmissionUrl" href={editSubmissionUrl}>
|
<Button id="editSubmissionUrl" href={editSubmissionUrl}>
|
||||||
Review response on {getDomain()}
|
Review response on {domain}
|
||||||
</Button>
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
</EmailLayout>
|
</EmailLayout>
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { absoluteUrl } from "@rallly/utils";
|
import { useEmailContext } from "./components/email-context";
|
||||||
|
|
||||||
import { EmailLayout } from "./components/email-layout";
|
import { EmailLayout } from "./components/email-layout";
|
||||||
import { Button, Card, Link, Text } from "./components/styled-components";
|
import { Button, Card, Link, Text } from "./components/styled-components";
|
||||||
import { getDomain } from "./components/utils";
|
|
||||||
|
|
||||||
export interface NewPollEmailProps {
|
export interface NewPollEmailProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -40,13 +38,14 @@ export const NewPollEmail = ({
|
||||||
adminLink = "https://rallly.co/admin/abcdefg123",
|
adminLink = "https://rallly.co/admin/abcdefg123",
|
||||||
participantLink = "https://rallly.co/invite/wxyz9876",
|
participantLink = "https://rallly.co/invite/wxyz9876",
|
||||||
}: NewPollEmailProps) => {
|
}: NewPollEmailProps) => {
|
||||||
|
const { baseUrl, domain } = useEmailContext();
|
||||||
return (
|
return (
|
||||||
<EmailLayout
|
<EmailLayout
|
||||||
footNote={
|
footNote={
|
||||||
<>
|
<>
|
||||||
You are receiving this email because a new poll was created with this
|
You are receiving this email because a new poll was created with this
|
||||||
email address on <Link href={absoluteUrl()}>{getDomain()}</Link>. If
|
email address on <Link href={baseUrl}>{domain}</Link>. If this
|
||||||
this wasn't you, please ignore this email.
|
wasn't you, please ignore this email.
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
recipientName={name}
|
recipientName={name}
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
export * from "./src/absolute-url";
|
|
||||||
export * from "./src/prevent-widows";
|
export * from "./src/prevent-widows";
|
||||||
|
|
Loading…
Add table
Reference in a new issue