({ EMAIL_FROM }: { EMAIL_FROM?: string })
| 112 | ); |
| 113 | |
| 114 | export default function Verify({ EMAIL_FROM }: { EMAIL_FROM?: string }) { |
| 115 | const searchParams = useCompatSearchParams(); |
| 116 | const pathname = usePathname(); |
| 117 | const router = useRouter(); |
| 118 | const routerQuery = useRouterQuery(); |
| 119 | const { email, username, paymentStatus } = querySchema.parse(routerQuery); |
| 120 | const { t } = useLocale(); |
| 121 | const [secondsLeft, setSecondsLeft] = useState(30); |
| 122 | |
| 123 | // Derive payment failed status from payment status |
| 124 | const hasPaymentFailed = paymentStatus !== undefined && paymentStatus !== "paid"; |
| 125 | const isPremiumUsername = !!paymentStatus; // If paymentStatus exists, it's from premium username flow |
| 126 | |
| 127 | // Only send verification login if we DON'T have the email yet (for resend button) |
| 128 | // The email is already sent server-side in paymentCallback |
| 129 | useSendFirstVerificationLogin({ |
| 130 | email: !isPremiumUsername ? email : undefined, |
| 131 | username: !isPremiumUsername ? username : undefined, |
| 132 | }); |
| 133 | // @note: check for t=timestamp and apply disabled state and secondsLeft accordingly |
| 134 | // to avoid refresh to skip waiting 30 seconds to re-send email |
| 135 | useEffect(() => { |
| 136 | const lastSent = new Date(parseInt(`${t}`)); |
| 137 | // @note: This double round() looks ugly but it's the only way I came up to get the time difference in seconds |
| 138 | const difference = Math.round(Math.round(new Date().getTime() - lastSent.getTime()) / 1000); |
| 139 | if (difference < 30) { |
| 140 | // If less than 30 seconds, set the seconds left to 30 - difference |
| 141 | setSecondsLeft(30 - difference); |
| 142 | } else { |
| 143 | // else set the seconds left to 0 and disabled false |
| 144 | setSecondsLeft(0); |
| 145 | } |
| 146 | }, [t]); |
| 147 | // @note: here we make sure each second is decremented if disabled up to 0. |
| 148 | useEffect(() => { |
| 149 | if (secondsLeft > 0) { |
| 150 | const interval = setInterval(() => { |
| 151 | if (secondsLeft > 0) { |
| 152 | setSecondsLeft(secondsLeft - 1); |
| 153 | } |
| 154 | }, 1000); |
| 155 | return () => clearInterval(interval); |
| 156 | } |
| 157 | }, [secondsLeft]); |
| 158 | |
| 159 | if (!email) { |
| 160 | return <div>{t("invalid_link")}</div>; |
| 161 | } |
| 162 | |
| 163 | return ( |
| 164 | <div className="text-default bg-cal-muted/90 backdrop-blur-md backdrop-grayscale backdrop-filter"> |
| 165 | <div className="flex min-h-screen flex-col items-center justify-center px-6"> |
| 166 | <div className="border-subtle bg-default m-10 flex max-w-2xl flex-col items-center rounded-xl border px-8 py-14 text-left"> |
| 167 | {hasPaymentFailed ? ( |
| 168 | <PaymentFailedIcon /> |
| 169 | ) : isPremiumUsername ? ( |
| 170 | <PaymentSuccess /> |
| 171 | ) : ( |
nothing calls this directly
no test coverage detected