"use client"; import { motion } from "framer-motion"; import { signIn } from "next-auth/react"; import { usePathname, useRouter } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import z from "zod"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; import { trpc } from "@calcom/trpc/react"; import classNames from "@calcom/ui/classNames"; import { Button } from "@calcom/ui/components/button"; import { Icon } from "@calcom/ui/components/icon"; import { showToast } from "@calcom/ui/components/toast"; import Loader from "@components/Loader"; async function sendVerificationLogin(email: string, username: string, t: (key: string) => string) { await signIn("email", { email: email.toLowerCase(), username: username.toLowerCase(), redirect: false, callbackUrl: WEBAPP_URL || "https://app.cal.com", }) .then(() => { showToast(t("verification_email_sent"), "success"); }) .catch((err) => { showToast(err, "error"); }); } function useSendFirstVerificationLogin({ email, username, }: { email: string | undefined; username: string | undefined; }) { const sent = useRef(false); const { t } = useLocale(); useEffect(() => { if (!email || !username || sent.current) { return; } (async () => { await sendVerificationLogin(email, username, t); sent.current = true; })(); }, [email, username, t]); } const querySchema = z.object({ stripeCustomerId: z.string().optional(), sessionId: z.string().optional(), t: z.string().optional(), }); const PaymentFailedIcon = () => (
); const PaymentSuccess = () => (
); const MailOpenIcon = () => (
); export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) { const searchParams = useCompatSearchParams(); const pathname = usePathname(); const router = useRouter(); const routerQuery = useRouterQuery(); const { t: tParam, sessionId, stripeCustomerId } = querySchema.parse(routerQuery); const { t } = useLocale(); const [secondsLeft, setSecondsLeft] = useState(30); const { data } = trpc.viewer.public.stripeCheckoutSession.useQuery( { stripeCustomerId, checkoutSessionId: sessionId, }, { enabled: !!stripeCustomerId || !!sessionId, staleTime: Infinity, } ); useSendFirstVerificationLogin({ email: data?.customer?.email, username: data?.customer?.username }); // @note: check for t=timestamp and apply disabled state and secondsLeft accordingly // to avoid refresh to skip waiting 30 seconds to re-send email useEffect(() => { const lastSent = new Date(parseInt(`${t}`)); // @note: This double round() looks ugly but it's the only way I came up to get the time difference in seconds const difference = Math.round(Math.round(new Date().getTime() - lastSent.getTime()) / 1000); if (difference < 30) { // If less than 30 seconds, set the seconds left to 30 - difference setSecondsLeft(30 - difference); } else { // else set the seconds left to 0 and disabled false setSecondsLeft(0); } }, [t]); // @note: here we make sure each second is decremented if disabled up to 0. useEffect(() => { if (secondsLeft > 0) { const interval = setInterval(() => { if (secondsLeft > 0) { setSecondsLeft(secondsLeft - 1); } }, 1000); return () => clearInterval(interval); } }, [secondsLeft]); if (!data) { // Loading state return ; } const { valid, hasPaymentFailed, customer } = data; if (!valid) { throw new Error("Invalid session or customer id"); } if (!stripeCustomerId && !sessionId) { return
{t("invalid_link")}
; } return (
{hasPaymentFailed ? : sessionId ? : }

{hasPaymentFailed ? t("your_payment_failed") : sessionId ? t("payment_successful") : t("check_your_inbox")}

{hasPaymentFailed &&

{t("account_created_premium_not_reserved")}

}

{t("email_sent_with_activation_link", { email: customer?.email })}{" "} {hasPaymentFailed && t("activate_account_to_purchase_username")}

{t("dont_see_email")}

); }