import { createSignal, createEffect, onCleanup, Show } from "solid-js"; import CountdownCircleTimer from "~/components/CountdownCircleTimer"; import { Spinner } from "~/components/Spinner"; import { getClientCookie } from "~/lib/cookies.client"; /** * Product discriminator forwarded to the generalized * `misc.sendDeletionRequestEmail` mutation so the email copy + cooldown * cookie are product-appropriate. */ export type DeletionProduct = "lineage" | "nessa"; export interface DeletionFormProps { /** * Product whose account is being deleted. Determines the email branding * AND the cooldown cookie name on the server. Defaults to `"lineage"` * (the original / legacy flow) for backward compatibility. */ product?: DeletionProduct; /** * Cooldown cookie name read on mount + written by the server response * (the mutation sets its own cookie; this is only for the client-side * countdown). Defaults to the legacy `deletionRequestSent` name so an * in-flight Lineage cooldown survives the legacy redirect. */ cookieName?: string; } export default function DeletionForm(props: DeletionFormProps = {}) { const product = () => props.product ?? "lineage"; const cookieName = () => props.cookieName ?? "deletionRequestSent"; const [countDown, setCountDown] = createSignal(0); const [emailSent, setEmailSent] = createSignal(false); const [error, setError] = createSignal(""); const [loading, setLoading] = createSignal(false); let emailRef: HTMLInputElement | undefined; let timerInterval: number | undefined; const calcRemainder = (timer: string) => { const expires = new Date(timer); const remaining = expires.getTime() - Date.now(); const remainingInSeconds = remaining / 1000; if (remainingInSeconds <= 0) { setCountDown(0); if (timerInterval) { clearInterval(timerInterval); } } else { setCountDown(remainingInSeconds); } }; createEffect(() => { const timer = getClientCookie(cookieName()); if (timer) { timerInterval = setInterval( () => calcRemainder(timer), 1000 ) as unknown as number; onCleanup(() => { if (timerInterval) { clearInterval(timerInterval); } }); } }); const sendEmailTrigger = async (e: Event) => { e.preventDefault(); setLoading(true); setError(""); setEmailSent(false); if (!emailRef) { setError("Please enter your email"); setLoading(false); return; } const email = emailRef.value; try { const response = await fetch("/api/trpc/misc.sendDeletionRequestEmail", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, product: product() }) }); const result = await response.json(); if (response.ok && result.result?.data?.message === "request sent") { setEmailSent(true); const timer = getClientCookie(cookieName()); if (timer) { if (timerInterval) { clearInterval(timerInterval); } timerInterval = setInterval( () => calcRemainder(timer), 1000 ) as unknown as number; } } else { const errorMsg = result.error?.message || "Failed to send deletion request"; setError(errorMsg); } } catch (err: any) { console.error("Deletion request error:", err); setError(err.message || "An error occurred"); } finally { setLoading(false); } }; const renderTime = ({ remainingTime }: { remainingTime: number }) => { return (
{remainingTime.toFixed(0)}
); }; return (
Deletion Form
0} fallback={ } > {renderTime}
Request Sent!
); }