"use client"; 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 classNames from "@calcom/ui/classNames"; import { Button } from "@calcom/ui/components/button"; import { showToast } from "@calcom/ui/components/toast"; import { MailOpenIcon, TriangleAlertIcon } from "@coss/ui/icons"; import { motion } from "framer-motion"; import { usePathname, useRouter } from "next/navigation"; import { signIn } from "next-auth/react"; import { useEffect, useRef, useState } from "react"; import z from "zod"; 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({ email: z.string().optional(), username: z.string().optional(), paymentStatus: z.string().optional(), t: z.string().optional(), }); const PaymentFailedIcon = () => (
); const PaymentSuccess = () => (
); const MailOpenIconWrapper = () => (
); export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) { const searchParams = useCompatSearchParams(); const pathname = usePathname(); const router = useRouter(); const routerQuery = useRouterQuery(); const { email, username, paymentStatus } = querySchema.parse(routerQuery); const { t } = useLocale(); const [secondsLeft, setSecondsLeft] = useState(30); // Derive payment failed status from payment status const hasPaymentFailed = paymentStatus !== undefined && paymentStatus !== "paid"; const isPremiumUsername = !!paymentStatus; // If paymentStatus exists, it's from premium username flow // Only send verification login if we DON'T have the email yet (for resend button) // The email is already sent server-side in paymentCallback useSendFirstVerificationLogin({ email: !isPremiumUsername ? email : undefined, username: !isPremiumUsername ? username : undefined, }); // @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 (!email) { return
{t("invalid_link")}
; } return (
{hasPaymentFailed ? ( ) : isPremiumUsername ? ( ) : ( )}

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

{hasPaymentFailed &&

{t("account_created_premium_not_reserved")}

}

{t("email_sent_with_activation_link", { email })}{" "} {hasPaymentFailed && t("activate_account_to_purchase_username")}

{t("dont_see_email")}

); }