2022-09-08 00:38:37 +00:00
|
|
|
import { signIn } from "next-auth/react";
|
2022-08-06 03:09:52 +00:00
|
|
|
import Head from "next/head";
|
2023-08-02 09:35:48 +00:00
|
|
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
2022-09-08 00:38:37 +00:00
|
|
|
import z from "zod";
|
2022-08-06 03:09:52 +00:00
|
|
|
|
2023-01-04 22:14:46 +00:00
|
|
|
import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants";
|
2023-08-02 09:35:48 +00:00
|
|
|
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
2022-09-08 00:38:37 +00:00
|
|
|
import { trpc } from "@calcom/trpc/react";
|
2022-11-23 02:55:25 +00:00
|
|
|
import { Button, showToast } from "@calcom/ui";
|
2023-08-02 09:35:48 +00:00
|
|
|
import { AlertTriangle, Check, MailOpen } from "@calcom/ui/components/icon";
|
2022-09-08 00:38:37 +00:00
|
|
|
|
|
|
|
import Loader from "@components/Loader";
|
2023-04-18 18:45:32 +00:00
|
|
|
import PageWrapper from "@components/PageWrapper";
|
2022-08-06 03:09:52 +00:00
|
|
|
|
|
|
|
async function sendVerificationLogin(email: string, username: string) {
|
|
|
|
await signIn("email", {
|
|
|
|
email: email.toLowerCase(),
|
|
|
|
username: username.toLowerCase(),
|
|
|
|
redirect: false,
|
|
|
|
callbackUrl: WEBAPP_URL || "https://app.cal.com",
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
showToast("Verification email sent", "success");
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
showToast(err, "error");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-08 00:38:37 +00:00
|
|
|
function useSendFirstVerificationLogin({
|
|
|
|
email,
|
|
|
|
username,
|
|
|
|
}: {
|
|
|
|
email: string | undefined;
|
|
|
|
username: string | undefined;
|
|
|
|
}) {
|
2022-08-06 03:09:52 +00:00
|
|
|
const sent = useRef(false);
|
|
|
|
useEffect(() => {
|
2022-09-08 00:38:37 +00:00
|
|
|
if (!email || !username || sent.current) {
|
|
|
|
return;
|
2022-08-06 03:09:52 +00:00
|
|
|
}
|
2022-09-08 00:38:37 +00:00
|
|
|
(async () => {
|
|
|
|
await sendVerificationLogin(email, username);
|
|
|
|
sent.current = true;
|
|
|
|
})();
|
|
|
|
}, [email, username]);
|
2022-08-06 03:09:52 +00:00
|
|
|
}
|
|
|
|
|
2022-09-08 00:38:37 +00:00
|
|
|
const querySchema = z.object({
|
|
|
|
stripeCustomerId: z.string().optional(),
|
|
|
|
sessionId: z.string().optional(),
|
|
|
|
t: z.string().optional(),
|
|
|
|
});
|
|
|
|
|
2022-08-06 03:09:52 +00:00
|
|
|
export default function Verify() {
|
2023-08-02 09:35:48 +00:00
|
|
|
const searchParams = useSearchParams();
|
|
|
|
const pathname = usePathname();
|
2022-08-06 03:09:52 +00:00
|
|
|
const router = useRouter();
|
2023-08-02 09:35:48 +00:00
|
|
|
const routerQuery = useRouterQuery();
|
|
|
|
const { t, sessionId, stripeCustomerId } = querySchema.parse(routerQuery);
|
2022-08-06 03:09:52 +00:00
|
|
|
const [secondsLeft, setSecondsLeft] = useState(30);
|
2023-09-25 17:57:02 +00:00
|
|
|
const { data } = trpc.viewer.public.stripeCheckoutSession.useQuery(
|
|
|
|
{
|
|
|
|
stripeCustomerId,
|
|
|
|
checkoutSessionId: sessionId,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
enabled: !!stripeCustomerId || !!sessionId,
|
|
|
|
staleTime: Infinity,
|
|
|
|
}
|
|
|
|
);
|
2022-09-08 00:38:37 +00:00
|
|
|
useSendFirstVerificationLogin({ email: data?.customer?.email, username: data?.customer?.username });
|
2022-08-06 03:09:52 +00:00
|
|
|
// @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]);
|
|
|
|
|
2023-08-02 09:35:48 +00:00
|
|
|
if (!data) {
|
2022-09-08 00:38:37 +00:00
|
|
|
// Loading state
|
|
|
|
return <Loader />;
|
|
|
|
}
|
|
|
|
const { valid, hasPaymentFailed, customer } = data;
|
|
|
|
if (!valid) {
|
|
|
|
throw new Error("Invalid session or customer id");
|
|
|
|
}
|
2022-08-06 03:09:52 +00:00
|
|
|
|
2022-09-08 00:38:37 +00:00
|
|
|
if (!stripeCustomerId && !sessionId) {
|
|
|
|
return <div>Invalid Link</div>;
|
|
|
|
}
|
2022-08-06 03:09:52 +00:00
|
|
|
|
|
|
|
return (
|
2023-04-05 18:14:46 +00:00
|
|
|
<div className="text-inverted bg-black bg-opacity-90 backdrop-blur-md backdrop-grayscale backdrop-filter">
|
2022-08-06 03:09:52 +00:00
|
|
|
<Head>
|
|
|
|
<title>
|
|
|
|
{/* @note: Ternary can look ugly ant his might be extracted later but I think at 3 it's not yet worth
|
2023-08-02 09:35:48 +00:00
|
|
|
it or too hard to read. */}
|
2022-09-08 00:38:37 +00:00
|
|
|
{hasPaymentFailed
|
2022-08-06 03:09:52 +00:00
|
|
|
? "Your payment failed"
|
2022-09-08 00:38:37 +00:00
|
|
|
: sessionId
|
2022-08-06 03:09:52 +00:00
|
|
|
? "Payment successful!"
|
2023-10-03 18:52:19 +00:00
|
|
|
: `Verify your email | ${APP_NAME}`}
|
2022-08-06 03:09:52 +00:00
|
|
|
</title>
|
|
|
|
</Head>
|
|
|
|
<div className="flex min-h-screen flex-col items-center justify-center px-6">
|
|
|
|
<div className="m-10 flex max-w-2xl flex-col items-start border border-white p-12 text-left">
|
|
|
|
<div className="rounded-full border border-white p-3">
|
2022-09-08 00:38:37 +00:00
|
|
|
{hasPaymentFailed ? (
|
2023-05-17 12:47:44 +00:00
|
|
|
<AlertTriangle className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight" />
|
2022-09-08 00:38:37 +00:00
|
|
|
) : sessionId ? (
|
2023-09-25 17:57:02 +00:00
|
|
|
<Check className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight dark:text-white" />
|
2022-08-06 03:09:52 +00:00
|
|
|
) : (
|
2023-05-17 12:47:44 +00:00
|
|
|
<MailOpen className="text-inverted h-12 w-12 flex-shrink-0 p-0.5 font-extralight" />
|
2022-08-06 03:09:52 +00:00
|
|
|
)}
|
|
|
|
</div>
|
2023-09-25 17:57:02 +00:00
|
|
|
<h3 className="font-cal text-inverted my-6 text-3xl font-normal dark:text-white">
|
2022-09-08 00:38:37 +00:00
|
|
|
{hasPaymentFailed
|
|
|
|
? "Your payment failed"
|
|
|
|
: sessionId
|
|
|
|
? "Payment successful!"
|
|
|
|
: "Check your Inbox"}
|
2022-08-06 03:09:52 +00:00
|
|
|
</h3>
|
2022-09-08 00:38:37 +00:00
|
|
|
{hasPaymentFailed && (
|
2022-08-06 03:09:52 +00:00
|
|
|
<p className="my-6">Your account has been created, but your premium has not been reserved.</p>
|
|
|
|
)}
|
2023-09-25 17:57:02 +00:00
|
|
|
<p className="text-inverted dark:text-white">
|
2022-09-08 00:38:37 +00:00
|
|
|
We have sent an email to <b>{customer?.email} </b>with a link to activate your account.{" "}
|
|
|
|
{hasPaymentFailed &&
|
2022-08-06 03:09:52 +00:00
|
|
|
"Once you activate your account you will be able to try purchase your premium username again or select a different one."}
|
|
|
|
</p>
|
2023-04-05 18:14:46 +00:00
|
|
|
<p className="text-muted mt-6">
|
2022-08-06 03:09:52 +00:00
|
|
|
Don't see an email? Click the button below to send another email.
|
|
|
|
</p>
|
|
|
|
|
2022-09-08 00:38:37 +00:00
|
|
|
<div className="mt-6 flex space-x-5 text-center">
|
2022-08-06 03:09:52 +00:00
|
|
|
<Button
|
|
|
|
color="secondary"
|
|
|
|
disabled={secondsLeft > 0}
|
|
|
|
onClick={async (e) => {
|
2022-09-08 00:38:37 +00:00
|
|
|
if (!customer) {
|
|
|
|
return;
|
|
|
|
}
|
2022-08-06 03:09:52 +00:00
|
|
|
e.preventDefault();
|
|
|
|
setSecondsLeft(30);
|
|
|
|
// Update query params with t:timestamp, shallow: true doesn't re-render the page
|
2023-09-25 17:57:02 +00:00
|
|
|
const _searchParams = new URLSearchParams(searchParams.toString());
|
2023-08-02 09:35:48 +00:00
|
|
|
_searchParams.set("t", `${Date.now()}`);
|
|
|
|
router.replace(`${pathname}?${_searchParams.toString()}`);
|
2022-09-08 00:38:37 +00:00
|
|
|
return await sendVerificationLogin(customer.email, customer.username);
|
2022-08-06 03:09:52 +00:00
|
|
|
}}>
|
|
|
|
{secondsLeft > 0 ? `Resend in ${secondsLeft} seconds` : "Send another mail"}
|
|
|
|
</Button>
|
|
|
|
<Button color="primary" href={`${WEBAPP_URL || "https://app.cal.com"}/auth/login`}>
|
|
|
|
Login using another method
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2023-04-18 18:45:32 +00:00
|
|
|
|
|
|
|
Verify.PageWrapper = PageWrapper;
|