diff options
| author | enkei64 <imenkei64@gmail.com> | 2025-07-16 19:07:33 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-16 19:07:33 +1000 |
| commit | 0cb26ef088d7b813bef4fd702ac26b813f1a9db1 (patch) | |
| tree | da9042be04762fda283df64a4684e1cf3644b301 /apps/web/src/components/landing | |
| parent | 7c6b2e4f499a9878b641bac5692f5a2ee884d125 (diff) | |
| parent | 27731a270dec7f854876157fac790d998b3b6a8c (diff) | |
| download | opencut-0cb26ef088d7b813bef4fd702ac26b813f1a9db1.tar.gz | |
Merge branch 'OpenCut-app:main' into feat/display-all-contributors
Diffstat (limited to 'apps/web/src/components/landing')
| -rw-r--r-- | apps/web/src/components/landing/hero.tsx | 82 |
1 files changed, 49 insertions, 33 deletions
diff --git a/apps/web/src/components/landing/hero.tsx b/apps/web/src/components/landing/hero.tsx index 462e3ba7..b1910dc5 100644 --- a/apps/web/src/components/landing/hero.tsx +++ b/apps/web/src/components/landing/hero.tsx @@ -4,7 +4,7 @@ import { motion } from "motion/react"; import { Button } from "../ui/button"; import { Input } from "../ui/input"; import { ArrowRight } from "lucide-react"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { toast } from "sonner"; import Image from "next/image"; @@ -13,6 +13,28 @@ import { Handlebars } from "./handlebars"; export function Hero() { const [email, setEmail] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); + const [csrfToken, setCsrfToken] = useState<string | null>(null); + + useEffect(() => { + let isMounted = true; + fetch("/api/waitlist/token", { + credentials: "include", + }) + .then((res) => res.json()) + .then((data) => { + if (isMounted && data.token) { + setCsrfToken(data.token); + } + }) + .catch((err) => { + console.error("Failed to fetch CSRF token:", err); + if (isMounted) { + toast.error("Security initialization failed", { + description: "Please refresh the page to continue.", + }); + } + }); + }, []); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -24,6 +46,13 @@ export function Hero() { return; } + if (!csrfToken) { + toast.error("Security error", { + description: "Please refresh the page and try again.", + }); + return; + } + setIsSubmitting(true); try { @@ -31,7 +60,9 @@ export function Hero() { method: "POST", headers: { "Content-Type": "application/json", + "X-CSRF-Token": csrfToken, }, + credentials: "include", body: JSON.stringify({ email: email.trim() }), }); @@ -42,11 +73,18 @@ export function Hero() { description: "You'll be notified when we launch.", }); setEmail(""); + + fetch("/api/waitlist/token", { credentials: "include" }) + .then((res) => res.json()) + .then((data) => { + if (data.token) setCsrfToken(data.token); + }) + .catch((err) => { + console.error("Failed to refresh CSRF token:", err); + }); } else { toast.error("Oops!", { - description: - (data as { error: string }).error || - "Something went wrong. Please try again.", + description: (data as { error: string }).error || "Something went wrong. Please try again.", }); } } catch (error) { @@ -60,13 +98,7 @@ export function Hero() { return ( <div className="min-h-[calc(100vh-4.5rem)] supports-[height:100dvh]:min-h-[calc(100dvh-4.5rem)] flex flex-col justify-between items-center text-center px-4"> - <Image - className="absolute top-0 left-0 -z-50 size-full object-cover" - src="/landing-page-bg.png" - height={1903.5} - width={1269} - alt="landing-page.bg" - /> + <Image className="absolute top-0 left-0 -z-50 size-full object-cover" src="/landing-page-bg.png" height={1903.5} width={1269} alt="landing-page.bg" /> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} @@ -89,20 +121,11 @@ export function Hero() { animate={{ opacity: 1 }} transition={{ delay: 0.4, duration: 0.8 }} > - A simple but powerful video editor that gets the job done. Works on - any platform. + A simple but powerful video editor that gets the job done. Works on any platform. </motion.p> - <motion.div - className="mt-12 flex gap-8 justify-center" - initial={{ opacity: 0 }} - animate={{ opacity: 1 }} - transition={{ delay: 0.6, duration: 0.8 }} - > - <form - onSubmit={handleSubmit} - className="flex gap-3 w-full max-w-lg flex-col sm:flex-row" - > + <motion.div className="mt-12 flex gap-8 justify-center" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 0.6, duration: 0.8 }}> + <form onSubmit={handleSubmit} className="flex gap-3 w-full max-w-lg flex-col sm:flex-row"> <div className="relative w-full"> <Input type="email" @@ -110,19 +133,12 @@ export function Hero() { className="h-11 text-base flex-1" value={email} onChange={(e) => setEmail(e.target.value)} - disabled={isSubmitting} + disabled={isSubmitting || !csrfToken} required /> </div> - <Button - type="submit" - size="lg" - className="px-6 h-11 text-base !bg-foreground" - disabled={isSubmitting} - > - <span className="relative z-10"> - {isSubmitting ? "Joining..." : "Join waitlist"} - </span> + <Button type="submit" size="lg" className="px-6 h-11 text-base !bg-foreground" disabled={isSubmitting || !csrfToken}> + <span className="relative z-10">{isSubmitting ? "Joining..." : "Join waitlist"}</span> <ArrowRight className="relative z-10 ml-0.5 h-4 w-4 inline-block" /> </Button> </form> |
