diff --git a/src/app/(inbox)/calendar/page.tsx b/src/app/(inbox)/calendar/page.tsx index e0835f6..8cbcefe 100644 --- a/src/app/(inbox)/calendar/page.tsx +++ b/src/app/(inbox)/calendar/page.tsx @@ -67,36 +67,43 @@ export default async function CalendarPage({ searchParams }: Props) {

-
+
{monthKey !== currentMonthKey && ( Today )} - Prev + + Previous -
+

{monthTitle(monthKey)} -

+ - Next + Next +
-
+
{DAY_LABELS.map((label) => (
- {day.date.getDate()} - +
{day.events.map((event) => ( diff --git a/src/app/(inbox)/settings/SignatureForm.tsx b/src/app/(inbox)/settings/SignatureForm.tsx index e96cbfe..470b2e8 100644 --- a/src/app/(inbox)/settings/SignatureForm.tsx +++ b/src/app/(inbox)/settings/SignatureForm.tsx @@ -1,8 +1,12 @@ "use client"; -import { useState, useTransition } from "react"; +import { useEffect, useState, useTransition } from "react"; import { saveSignatureAction } from "./actions"; -import { stripSignatureSeparator } from "@/lib/compose"; +import { + formatSignatureForSave, + stripSignatureSeparator, +} from "@/lib/compose"; +import { useToast } from "@/components/ToastProvider"; interface Props { identityLabel?: string; @@ -16,53 +20,144 @@ function stripSepPrefix(sig: string): string { } export default function SignatureForm({ identityLabel, initialSignature }: Props) { - const [value, setValue] = useState(() => stripSepPrefix(initialSignature)); - const [saved, setSaved] = useState(false); + const initialValue = stripSepPrefix(initialSignature); + const [value, setValue] = useState(initialValue); + const [savedValue, setSavedValue] = useState(initialValue); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); + const showToast = useToast(); + const dirty = value !== savedValue; + + useEffect(() => { + if (!dirty) return; + function warnBeforeUnload(event: BeforeUnloadEvent) { + event.preventDefault(); + } + window.addEventListener("beforeunload", warnBeforeUnload); + return () => window.removeEventListener("beforeunload", warnBeforeUnload); + }, [dirty]); function handleSave() { + if (!dirty || isPending) return; setError(null); - setSaved(false); startTransition(async () => { try { // Re-add the `-- \n` prefix so Fastmail stores it in standard format, // which other mail clients (Fastmail web, mobile apps) also expect. - const toSave = value.trim() ? `-- \n${value.trim()}` : ""; + const normalized = value.trim(); + const toSave = formatSignatureForSave(value); await saveSignatureAction(toSave); - setSaved(true); + setValue(normalized); + setSavedValue(normalized); + showToast({ message: "Signature saved" }); } catch (e) { - setError(e instanceof Error ? e.message : "Failed to save"); + const message = e instanceof Error ? e.message : "Failed to save"; + setError(message); + showToast({ message: "Couldn’t save the signature.", tone: "error" }); } }); } return ( -
+
{identityLabel && ( -

{identityLabel}

+
+

+ Sending as +

+

+ {identityLabel} +

+
)} -