From deef257d468dca3e8bf943340a1b097ea0560480 Mon Sep 17 00:00:00 2001 From: Michael H Date: Thu, 5 Dec 2024 00:00:21 +1100 Subject: [PATCH] Next.js 15 (#10) I trust... I hope this is a good decision... --- .github/workflows/build.yml | 3 + app/(auth)/api/invite/route.ts | 2 +- app/(auth)/login/action.ts | 6 +- app/(auth)/login/reset/form.client.tsx | 4 +- app/(auth)/login/reset/page.tsx | 11 +- app/(auth)/register/action.ts | 4 +- app/(auth)/register/page.tsx | 1 + .../[mailbox]/(email-list)/drafts/page.tsx | 15 ++- .../[mailbox]/(email-list)/email-list.tsx | 1 + .../mail/[mailbox]/(email-list)/page.tsx | 15 ++- .../mail/[mailbox]/(email-list)/sent/page.tsx | 15 ++- .../[mailbox]/(email-list)/starred/page.tsx | 15 ++- .../mail/[mailbox]/(email-list)/temp/page.tsx | 15 ++- .../[mailbox]/(email-list)/trash/page.tsx | 15 ++- .../[email]/attachment/[attachment]/route.ts | 9 +- .../mail/[mailbox]/[email]/email.eml/route.ts | 9 +- .../[mailbox]/[email]/email.html/route.ts | 9 +- .../mail/[mailbox]/[email]/email.txt/route.ts | 9 +- app/(email)/mail/[mailbox]/[email]/page.tsx | 21 ++-- .../mail/[mailbox]/[email]/parse-html.tsx | 6 + app/(email)/mail/[mailbox]/config/actions.ts | 2 +- app/(email)/mail/[mailbox]/config/page.tsx | 20 +-- .../[mailbox]/draft/[draft]/editor.client.tsx | 3 +- .../draft/[draft]/email.html/route.ts | 9 +- .../draft/[draft]/email.txt/route.ts | 9 +- .../mail/[mailbox]/draft/[draft]/page.tsx | 15 ++- app/(email)/mail/[mailbox]/draft/new/page.ts | 17 ++- app/(email)/mail/[mailbox]/layout.tsx | 16 +-- app/(email)/mail/[mailbox]/nav.search.tsx | 6 +- app/(email)/mail/route.ts | 10 +- .../emailme/[username]/action.ts | 2 +- .../emailme/[username]/components.client.tsx | 8 +- .../emailme/[username]/page.tsx | 6 +- app/(emailthing.me)/emailme/page.tsx | 2 + app/(home)/home/page.tsx | 2 +- app/(home)/stats/page.tsx | 3 +- app/(user)/actions.ts | 4 +- app/(user)/components.client.tsx | 13 +- app/actions.ts | 4 +- app/api/v0/tools.ts | 6 +- app/components/disable-reset.client.tsx | 13 ++ app/components/icons/mail-unread.tsx | 21 ++-- app/components/loadmore.client.tsx | 2 +- app/components/localtime.tsx | 4 +- app/components/ui/dropdown-menu.tsx | 4 + app/components/ui/progress.tsx | 25 ++++ app/utils/jwt.ts | 6 +- app/utils/passkeys.ts | 2 +- bun.lockb | Bin 297024 -> 308436 bytes next.config.js | 15 ++- package.json | 119 +++++++++--------- 51 files changed, 312 insertions(+), 241 deletions(-) create mode 100644 app/components/disable-reset.client.tsx create mode 100644 app/components/ui/progress.tsx diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb0ecd7..42f75ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,9 @@ jobs: bun run build 2>&1 | tee -a build.log env: STANDALONE: "1" + # TURBOPACK_BUILD: 1 + # TURBOPACK: 1 + # TURBO: 1 DATABASE_URL: "libsql://void.riskymh.dev" EMAIL_AUTH_TOKEN: "no" diff --git a/app/(auth)/api/invite/route.ts b/app/(auth)/api/invite/route.ts index 4354ad6..ecd7007 100644 --- a/app/(auth)/api/invite/route.ts +++ b/app/(auth)/api/invite/route.ts @@ -18,7 +18,7 @@ export async function GET() { if (!user) { // scamful way of getting bot :) - if (process.env.SECRET_SPECIAL_TOKEN_YAY !== headers().get("Authorization")) { + if (process.env.SECRET_SPECIAL_TOKEN_YAY !== (await headers()).get("Authorization")) { return new Response("Unauthorized", { status: 401 }); } } diff --git a/app/(auth)/login/action.ts b/app/(auth)/login/action.ts index 4f729ce..83bebaa 100644 --- a/app/(auth)/login/action.ts +++ b/app/(auth)/login/action.ts @@ -113,11 +113,11 @@ async function handleUserRedirection( columns: { mailboxId: true }, }); - const possibleMailbox = cookies().get("mailboxId")?.value; + const possibleMailbox = (await cookies()).get("mailboxId")?.value; const mailboxIdAllowed = mailboxes.some(({ mailboxId }) => mailboxId === possibleMailbox); if (!(possibleMailbox && mailboxIdAllowed)) { - cookies().set("mailboxId", mailboxes[0].mailboxId, { + (await cookies()).set("mailboxId", mailboxes[0].mailboxId, { path: "/", expires: new Date("2038-01-19 04:14:07"), }); @@ -134,7 +134,7 @@ async function handleUserRedirection( } // maybe ?from=/... - const referer = headers().get("referer"); + const referer = (await headers()).get("referer"); if (referer) { // biome-ignore lint: i lazy callback = new URL(referer).searchParams?.get("from"); diff --git a/app/(auth)/login/reset/form.client.tsx b/app/(auth)/login/reset/form.client.tsx index 09f0b06..2cf7316 100644 --- a/app/(auth)/login/reset/form.client.tsx +++ b/app/(auth)/login/reset/form.client.tsx @@ -1,5 +1,6 @@ "use client"; +import DisableFormReset from "@/components/disable-reset.client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -32,7 +33,7 @@ export function UserAuthForm({ className, username, token, ...props }: UserAuthF return ( <>
-
+
diff --git a/app/(auth)/login/reset/page.tsx b/app/(auth)/login/reset/page.tsx index f89cf47..1f3f3a5 100644 --- a/app/(auth)/login/reset/page.tsx +++ b/app/(auth)/login/reset/page.tsx @@ -10,15 +10,14 @@ import Link from "next/link"; import { notFound } from "next/navigation"; import { UserAuthForm } from "./form.client"; -export const revalidate = 0; +export const dynamic = "force-static"; -export default async function LoginPage({ - searchParams, -}: { - searchParams: { +export default async function LoginPage(props: { + searchParams: Promise<{ token: string; - }; + }>; }) { + const searchParams = await props.searchParams; if (!searchParams.token) return notFound(); const token = await db.query.ResetPasswordToken.findFirst({ diff --git a/app/(auth)/register/action.ts b/app/(auth)/register/action.ts index 78bcb7e..6250411 100644 --- a/app/(auth)/register/action.ts +++ b/app/(auth)/register/action.ts @@ -33,7 +33,7 @@ export default async function signUp( } // currently you require invite code to sign up - const referer = headers().get("referer"); + const referer = (await headers()).get("referer"); if (!referer) return noInvite; const inviteCode = new URL(referer).searchParams?.get("invite"); if (!inviteCode) return noInvite; @@ -111,7 +111,7 @@ export default async function signUp( // add user token to cookie await addUserTokenToCookie({ id: userId }); - cookies().set("mailboxId", mailboxId, { + (await cookies()).set("mailboxId", mailboxId, { path: "/", expires: new Date("2038-01-19 04:14:07"), }); diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index 48e1e74..c1699e4 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -9,6 +9,7 @@ import { UserAuthForm } from "./form.client"; // todo: maybe re-enable edge // export const runtime = "edge"; +export const dynamic = "force-static"; export default async function LoginPage() { return ( diff --git a/app/(email)/mail/[mailbox]/(email-list)/drafts/page.tsx b/app/(email)/mail/[mailbox]/(email-list)/drafts/page.tsx index 4246e85..49817d3 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/drafts/page.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/drafts/page.tsx @@ -6,18 +6,17 @@ export const metadata = { title: "Drafts", } as Metadata; -export default async function Mailbox({ - params, - searchParams, -}: { - params: { +export default async function Mailbox(props: { + params: Promise<{ mailbox: string; - }; - searchParams: { + }>; + searchParams: Promise<{ take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox); return ( diff --git a/app/(email)/mail/[mailbox]/(email-list)/email-list.tsx b/app/(email)/mail/[mailbox]/(email-list)/email-list.tsx index 1bd5b88..18d6500 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/email-list.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/email-list.tsx @@ -8,6 +8,7 @@ import { formatTimeAgo } from "@/utils/tools"; import { cn } from "@/utils/tw"; import { eq } from "drizzle-orm"; import Link from "next/link"; +import type { JSX } from "react"; import { RefreshButton } from "../components.client"; import { mailboxCategories, userMailboxAccess } from "../tools"; import { EmailItem } from "./email-item"; diff --git a/app/(email)/mail/[mailbox]/(email-list)/page.tsx b/app/(email)/mail/[mailbox]/(email-list)/page.tsx index ee82d51..0659588 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/page.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/page.tsx @@ -5,19 +5,18 @@ export const metadata = { title: "Inbox", } as Metadata; -export default async function Mailbox({ - params, - searchParams, -}: { - params: { +export default async function Mailbox(props: { + params: Promise<{ mailbox: string; - }; - searchParams?: { + }>; + searchParams?: Promise<{ category?: string; take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; return ( ; + searchParams?: Promise<{ category?: string; take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox); return ( diff --git a/app/(email)/mail/[mailbox]/(email-list)/starred/page.tsx b/app/(email)/mail/[mailbox]/(email-list)/starred/page.tsx index abe02e6..91cfec1 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/starred/page.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/starred/page.tsx @@ -6,19 +6,18 @@ export const metadata = { title: "Starred", } as Metadata; -export default async function Mailbox({ - params, - searchParams, -}: { - params: { +export default async function Mailbox(props: { + params: Promise<{ mailbox: string; - }; - searchParams?: { + }>; + searchParams?: Promise<{ category?: string; take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox); return ( diff --git a/app/(email)/mail/[mailbox]/(email-list)/temp/page.tsx b/app/(email)/mail/[mailbox]/(email-list)/temp/page.tsx index 549f5ab..2d86c8f 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/temp/page.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/temp/page.tsx @@ -6,19 +6,18 @@ export const metadata = { title: "Temporary Email", } as Metadata; -export default async function Mailbox({ - params, - searchParams, -}: { - params: { +export default async function Mailbox(props: { + params: Promise<{ mailbox: string; - }; - searchParams?: { + }>; + searchParams?: Promise<{ category?: string; take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox); return ( diff --git a/app/(email)/mail/[mailbox]/(email-list)/trash/page.tsx b/app/(email)/mail/[mailbox]/(email-list)/trash/page.tsx index 020e9e3..8c7370d 100644 --- a/app/(email)/mail/[mailbox]/(email-list)/trash/page.tsx +++ b/app/(email)/mail/[mailbox]/(email-list)/trash/page.tsx @@ -6,19 +6,18 @@ export const metadata = { title: "Trash", } as Metadata; -export default async function Mailbox({ - params, - searchParams, -}: { - params: { +export default async function Mailbox(props: { + params: Promise<{ mailbox: string; - }; - searchParams?: { + }>; + searchParams?: Promise<{ category?: string; take?: string; q?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox); return ( diff --git a/app/(email)/mail/[mailbox]/[email]/attachment/[attachment]/route.ts b/app/(email)/mail/[mailbox]/[email]/attachment/[attachment]/route.ts index cb10e58..4c9de18 100644 --- a/app/(email)/mail/[mailbox]/[email]/attachment/[attachment]/route.ts +++ b/app/(email)/mail/[mailbox]/[email]/attachment/[attachment]/route.ts @@ -9,16 +9,15 @@ import { userMailboxAccess } from "../../../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; email: string; attachment: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/[email]/email.eml/route.ts b/app/(email)/mail/[mailbox]/[email]/email.eml/route.ts index 6d3caab..188c9f3 100644 --- a/app/(email)/mail/[mailbox]/[email]/email.eml/route.ts +++ b/app/(email)/mail/[mailbox]/[email]/email.eml/route.ts @@ -9,15 +9,14 @@ import { userMailboxAccess } from "../../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; email: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/[email]/email.html/route.ts b/app/(email)/mail/[mailbox]/[email]/email.html/route.ts index 8381f00..f71ecca 100644 --- a/app/(email)/mail/[mailbox]/[email]/email.html/route.ts +++ b/app/(email)/mail/[mailbox]/[email]/email.html/route.ts @@ -8,15 +8,14 @@ import { userMailboxAccess } from "../../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; email: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/[email]/email.txt/route.ts b/app/(email)/mail/[mailbox]/[email]/email.txt/route.ts index bd86d6f..bddd04b 100644 --- a/app/(email)/mail/[mailbox]/[email]/email.txt/route.ts +++ b/app/(email)/mail/[mailbox]/[email]/email.txt/route.ts @@ -8,15 +8,14 @@ import { userMailboxAccess } from "../../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; email: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/[email]/page.tsx b/app/(email)/mail/[mailbox]/[email]/page.tsx index 986d20f..2e8d3d8 100644 --- a/app/(email)/mail/[mailbox]/[email]/page.tsx +++ b/app/(email)/mail/[mailbox]/[email]/page.tsx @@ -39,11 +39,11 @@ import { getEmail } from "./tools"; import TopButtons from "./top-buttons"; export async function generateMetadata(props: { - params: { mailbox: string; email: string }; + params: Promise<{ mailbox: string; email: string }>; }): Promise { - if (!(await pageMailboxAccess(props.params.mailbox, false))) return {}; + if (!(await pageMailboxAccess((await props.params).mailbox, false))) return {}; - const mail = await getEmail(props.params.mailbox, props.params.email); + const mail = await getEmail((await props.params).mailbox, (await props.params).email); if (!mail) return notFound(); return { @@ -51,18 +51,17 @@ export async function generateMetadata(props: { }; } -export default async function EmailPage({ - params, - searchParams, -}: { - params: { +export default async function EmailPage(props: { + params: Promise<{ mailbox: string; email: string; - }; - searchParams: { + }>; + searchParams: Promise<{ view?: "text" | "markdown" | "html"; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; await pageMailboxAccess(params.mailbox, false); const email = await getEmail(params.mailbox, params.email); if (!email) return notFound(); diff --git a/app/(email)/mail/[mailbox]/[email]/parse-html.tsx b/app/(email)/mail/[mailbox]/[email]/parse-html.tsx index 7eaa9b1..3b62207 100644 --- a/app/(email)/mail/[mailbox]/[email]/parse-html.tsx +++ b/app/(email)/mail/[mailbox]/[email]/parse-html.tsx @@ -4,7 +4,13 @@ import { JSDOM } from "jsdom"; const DOMPurify = createDOMPurify((globalThis as any).window || new JSDOM("").window); +function isElement(node: Node): node is Element { + return node.nodeType === node.ELEMENT_NODE; +} + DOMPurify.addHook("afterSanitizeAttributes", (node) => { + if (!isElement(node)) return; + // set all elements owning target to target=_blank if ("target" in node) { node.setAttribute("target", "_blank"); diff --git a/app/(email)/mail/[mailbox]/config/actions.ts b/app/(email)/mail/[mailbox]/config/actions.ts index 23ad94f..78f795a 100644 --- a/app/(email)/mail/[mailbox]/config/actions.ts +++ b/app/(email)/mail/[mailbox]/config/actions.ts @@ -608,6 +608,6 @@ export async function leaveMailbox(mailboxId: string) { revalidateTag(`user-mailbox-access-${mailboxId}-${currentUserId}`); revalidateTag(`user-mailboxes-${currentUserId}`); revalidatePath(`/mail/${mailboxId}/config`); - cookies().delete("mailboxId"); + (await cookies()).delete("mailboxId"); redirect("/mail"); } diff --git a/app/(email)/mail/[mailbox]/config/page.tsx b/app/(email)/mail/[mailbox]/config/page.tsx index 8d9715e..9cd9216 100644 --- a/app/(email)/mail/[mailbox]/config/page.tsx +++ b/app/(email)/mail/[mailbox]/config/page.tsx @@ -8,6 +8,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Progress } from "@/components/ui/progress"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { SmartDrawer, @@ -60,14 +61,13 @@ export const metadata: Metadata = { title: "Config", }; -export default async function EmailConfig({ - params, -}: { - params: { +export default async function EmailConfig(props: { + params: Promise<{ mailbox: string; email: string; - }; + }>; }) { + const params = await props.params; const userId = await pageMailboxAccess(params.mailbox); if (!userId) return notFound(); @@ -140,8 +140,12 @@ export default async function EmailConfig({

Mailbox Config

-
+

Storage

+

Used: {Math.ceil((mailbox.storageUsed / 1e6) * 10) / 10}MB / {storageLimit[mailbox.plan] / 1e6}MB

@@ -456,7 +460,7 @@ export default async function EmailConfig({

- Custom domains + Custom domains{" "} ({mailbox.customDomains.length}/3)

@@ -675,7 +679,7 @@ export default async function EmailConfig({

- If you would like to send emails via the API, see the documentation here: + If you would like to send emails via the API, see the documentation here:{" "} API Documentation diff --git a/app/(email)/mail/[mailbox]/draft/[draft]/editor.client.tsx b/app/(email)/mail/[mailbox]/draft/[draft]/editor.client.tsx index 510b174..3dfa67d 100644 --- a/app/(email)/mail/[mailbox]/draft/[draft]/editor.client.tsx +++ b/app/(email)/mail/[mailbox]/draft/[draft]/editor.client.tsx @@ -700,8 +700,8 @@ export function RecipientInput({ savedTo }: RecipientInputProps) { cc: type === "to" ? null : (type as any), }, ]); - update(); element.value = ""; + update(); } else if (toastOnError) { toast.error("Invalid email address"); } @@ -855,6 +855,7 @@ export function RecipientInput({ savedTo }: RecipientInputProps) { }} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); if (!e.currentTarget.value) return toast.warning("Please add an email first"); validate(e.currentTarget, type); } else if (e.key === "Backspace" && e.currentTarget.value === "") { diff --git a/app/(email)/mail/[mailbox]/draft/[draft]/email.html/route.ts b/app/(email)/mail/[mailbox]/draft/[draft]/email.html/route.ts index 241d66e..8e2285a 100644 --- a/app/(email)/mail/[mailbox]/draft/[draft]/email.html/route.ts +++ b/app/(email)/mail/[mailbox]/draft/[draft]/email.html/route.ts @@ -9,15 +9,14 @@ import { makeHtml } from "../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; draft: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/draft/[draft]/email.txt/route.ts b/app/(email)/mail/[mailbox]/draft/[draft]/email.txt/route.ts index 182825e..2d9315f 100644 --- a/app/(email)/mail/[mailbox]/draft/[draft]/email.txt/route.ts +++ b/app/(email)/mail/[mailbox]/draft/[draft]/email.txt/route.ts @@ -8,15 +8,14 @@ import { userMailboxAccess } from "../../../tools"; export async function GET( request: Request, - { - params, - }: { - params: { + props: { + params: Promise<{ mailbox: string; draft: string; - }; + }>; }, ) { + const params = await props.params; const userId = await getCurrentUser(); if (!(userId && (await userMailboxAccess(params.mailbox, userId)))) return notFound(); diff --git a/app/(email)/mail/[mailbox]/draft/[draft]/page.tsx b/app/(email)/mail/[mailbox]/draft/[draft]/page.tsx index f25cd13..ca68857 100644 --- a/app/(email)/mail/[mailbox]/draft/[draft]/page.tsx +++ b/app/(email)/mail/[mailbox]/draft/[draft]/page.tsx @@ -27,11 +27,11 @@ import { } from "./editor.client"; export async function generateMetadata(props: { - params: { mailbox: string; draft: string }; + params: Promise<{ mailbox: string; draft: string }>; }) { - if (!(await pageMailboxAccess(props.params.mailbox, false))) return {}; + if (!(await pageMailboxAccess((await props.params).mailbox, false))) return {}; - const mail = await fetchDraft(props.params.mailbox, props.params.draft); + const mail = await fetchDraft((await props.params).mailbox, (await props.params).draft); return { title: mail?.subject || "(Unnamed draft)", }; @@ -51,14 +51,13 @@ const fetchDraft = cache(async (mailboxId: string, draftId: string) => { }); }); -export default async function DraftPage({ - params, -}: { - params: { +export default async function DraftPage(props: { + params: Promise<{ mailbox: string; draft: string; - }; + }>; }) { + const params = await props.params; await pageMailboxAccess(params.mailbox); const { aliases, default: defaultAlias } = await mailboxAliases(params.mailbox); diff --git a/app/(email)/mail/[mailbox]/draft/new/page.ts b/app/(email)/mail/[mailbox]/draft/new/page.ts index 481c899..c9258e9 100644 --- a/app/(email)/mail/[mailbox]/draft/new/page.ts +++ b/app/(email)/mail/[mailbox]/draft/new/page.ts @@ -8,19 +8,18 @@ import { parseHTML } from "../../[email]/parse-html"; import { mailboxAliases, pageMailboxAccess } from "../../tools"; import type { Recipient } from "../[draft]/tools"; -export default async function Page({ - params, - searchParams, -}: { - params: { +export default async function Page(props: { + params: Promise<{ mailbox: string; - }; - searchParams: { + }>; + searchParams: Promise<{ reply?: string; replyAll?: string; forward?: string; - }; + }>; }) { + const searchParams = await props.searchParams; + const params = await props.params; // make new draft await pageMailboxAccess(params.mailbox); @@ -91,7 +90,7 @@ export default async function Page({ } } - const timeZone = headers().get("x-vercel-ip-timezone") || undefined; + const timeZone = (await headers()).get("x-vercel-ip-timezone") || undefined; const emailBody = await parseHTML( await marked.parse( `
\n
\nOn ${email.createdAt.toLocaleString([], { timeZone })}, ${email.from?.name ? `${email.from.name} <${email.from.address}>` : `${email.from.address}`} wrote:\n\n> ${email.body.split("\n").join("\n> ")}`, diff --git a/app/(email)/mail/[mailbox]/layout.tsx b/app/(email)/mail/[mailbox]/layout.tsx index ba8bb9a..2020dd9 100644 --- a/app/(email)/mail/[mailbox]/layout.tsx +++ b/app/(email)/mail/[mailbox]/layout.tsx @@ -3,7 +3,8 @@ import Header from "./header"; import Sidebar from "./sidebar"; import { mailboxAliases, pageMailboxAccess } from "./tools"; -export async function generateMetadata({ params }: { params: { mailbox: string } }): Promise { +export async function generateMetadata(props: { params: Promise<{ mailbox: string }> }): Promise { + const params = await props.params; await pageMailboxAccess(params.mailbox); const { default: defaultAlias } = await mailboxAliases(params.mailbox); @@ -17,15 +18,16 @@ export async function generateMetadata({ params }: { params: { mailbox: string } }; } -export default async function MailLayout({ - children, - params, -}: { +export default async function MailLayout(props: { children: React.ReactNode; - params: { + params: Promise<{ mailbox: string; - }; + }>; }) { + const params = await props.params; + + const { children } = props; + // const userId = await getCurrentUser() // if (!userId) return redirect("/login?from=/mail/" + params.mailbox) diff --git a/app/(email)/mail/[mailbox]/nav.search.tsx b/app/(email)/mail/[mailbox]/nav.search.tsx index 16b75e6..d3bfdbd 100644 --- a/app/(email)/mail/[mailbox]/nav.search.tsx +++ b/app/(email)/mail/[mailbox]/nav.search.tsx @@ -2,6 +2,7 @@ import { Button } from "@/components/ui/button"; import { cn } from "@/utils/tw"; import { ChevronDownIcon, Loader2, SearchIcon } from "lucide-react"; +import Form from "next/form"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useRef, useTransition } from "react"; @@ -41,8 +42,9 @@ export function Search({ className, mailboxId }: { className?: string; mailboxId }; return ( -
- + ); } diff --git a/app/(email)/mail/route.ts b/app/(email)/mail/route.ts index 79cbdc7..fb8c4fc 100644 --- a/app/(email)/mail/route.ts +++ b/app/(email)/mail/route.ts @@ -13,12 +13,12 @@ export async function GET() { const userId = await getCurrentUser(); if (!userId) { - removeToken(); + await removeToken(); return redirect("/login"); } // get the mailbox based on mailboxId cookie - const mailboxId = cookies().get("mailboxId"); + const mailboxId = (await cookies()).get("mailboxId"); if (mailboxId) { return redirect(`/mail/${mailboxId.value}`); @@ -28,13 +28,13 @@ export async function GET() { columns: { mailboxId: true }, }); if (firstMailbox) { - cookies().set("mailboxId", firstMailbox.mailboxId, { + (await cookies()).set("mailboxId", firstMailbox.mailboxId, { path: "/", expires: new Date("2038-01-19 04:14:07"), }); return redirect(`/mail/${firstMailbox.mailboxId}`); } - removeToken(); - cookies().delete("mailboxId"); + await removeToken(); + (await cookies()).delete("mailboxId"); return redirect("/login"); } diff --git a/app/(emailthing.me)/emailme/[username]/action.ts b/app/(emailthing.me)/emailme/[username]/action.ts index 5986df8..350b28a 100644 --- a/app/(emailthing.me)/emailme/[username]/action.ts +++ b/app/(emailthing.me)/emailme/[username]/action.ts @@ -34,7 +34,7 @@ export async function emailMeForm( // formData.append("secret", "1x0000000000000000000000000000000AA") formData.append("secret", env.TURNSTILE_SECRET_KEY); formData.append("response", data.get("cf-turnstile-response") as string); - formData.append("remoteip", headers().get("CF-Connecting-IP") as string); + formData.append("remoteip", (await headers()).get("CF-Connecting-IP") as string); const turnstile = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", { method: "POST", diff --git a/app/(emailthing.me)/emailme/[username]/components.client.tsx b/app/(emailthing.me)/emailme/[username]/components.client.tsx index 3ce6f6e..4f2fa13 100644 --- a/app/(emailthing.me)/emailme/[username]/components.client.tsx +++ b/app/(emailthing.me)/emailme/[username]/components.client.tsx @@ -1,15 +1,17 @@ "use client"; import { ClientInput, ClientTextarea } from "@/(user)/components.client"; +import DisableFormReset from "@/components/disable-reset.client"; import { Button } from "@/components/ui/button"; import { CheckIcon, Loader2 } from "lucide-react"; import { useEffect } from "react"; -import { useFormState, useFormStatus } from "react-dom"; +import { useActionState } from "react"; +import { useFormStatus } from "react-dom"; import { toast } from "sonner"; import { emailMeForm } from "./action"; export function Form({ publicEmail, username }: { className?: string; publicEmail?: string; username?: string }) { - const [state, formAction] = useFormState(emailMeForm, {}); + const [state, formAction] = useActionState(emailMeForm, {}); useEffect(() => { if (state?.error) { @@ -23,6 +25,7 @@ export function Form({ publicEmail, username }: { className?: string; publicEmai action={formAction} className="flex w-full max-w-[35rem] flex-col gap-2 self-center" suppressHydrationWarning + id="emailme-form" >
@@ -102,6 +105,7 @@ export function Form({ publicEmail, username }: { className?: string; publicEmai
+