"use client"; import { Button, Text, Input } from "@/components/retroui"; import { useState } from "react"; const INIT = "INIT"; const SUBMITTING = "SUBMITTING"; const ERROR = "ERROR"; const SUCCESS = "SUCCESS"; const formStates = [INIT, SUBMITTING, ERROR, SUCCESS] as const; const formStyles = { id: "cltl71xnw000ris85z92rosvh", name: "Default", formStyle: "inline", placeholderText: "you@example.com", formFont: "font-sans", formFontColor: "#000000", formFontSizePx: 14, buttonText: "Subscribe", buttonFont: "font-sans", buttonFontColor: "#ffffff", buttonColor: "#000000", buttonFontSizePx: 14, successMessage: "Thanks! We'll be in touch!", successFont: "Inter", successFontColor: "#000000", successFontSizePx: 14, userGroup: "", }; const domain = "app.loops.so"; export function JoinNewsletter() { const [email, setEmail] = useState(""); const [formState, setFormState] = useState<(typeof formStates)[number]>(INIT); const [errorMessage, setErrorMessage] = useState(""); const resetForm = () => { setEmail(""); setFormState(INIT); setErrorMessage(""); }; /** * Rate limit the number of submissions allowed * @returns {boolean} true if the form has been successfully submitted in the past minute */ const hasRecentSubmission = () => { const time = new Date(); const timestamp = time.valueOf(); const previousTimestamp = localStorage.getItem("loops-form-timestamp"); // Indicate if the last sign up was less than a minute ago if ( previousTimestamp && Number(previousTimestamp) + 60 * 1000 > timestamp ) { setFormState(ERROR); setErrorMessage("Too many signups, please try again in a little while"); return true; } localStorage.setItem("loops-form-timestamp", timestamp.toString()); return false; }; const handleSubmit = (event: React.FormEvent) => { // Prevent the default form submission event.preventDefault(); // boundary conditions for submission if (formState !== INIT) return; if (!isValidEmail(email)) { setFormState(ERROR); setErrorMessage("Please enter a valid email"); return; } if (hasRecentSubmission()) return; setFormState(SUBMITTING); // build body const formBody = `userGroup=${encodeURIComponent( formStyles.userGroup, )}&email=${encodeURIComponent(email)}&mailingLists=`; // API request to add user to newsletter fetch(`https://${domain}/api/newsletter-form/${formStyles.id}`, { method: "POST", body: formBody, headers: { "Content-Type": "application/x-www-form-urlencoded", }, }) .then((res: any) => [res.ok, res.json(), res]) .then(([ok, dataPromise, res]) => { if (ok) { resetForm(); setFormState(SUCCESS); } else { dataPromise.then((data: any) => { setFormState(ERROR); setErrorMessage(data.message || res.statusText); localStorage.setItem("loops-form-timestamp", ""); }); } }) .catch((error) => { setFormState(ERROR); // check for cloudflare error if (error.message === "Failed to fetch") { setErrorMessage( "Too many signups, please try again in a little while", ); } else if (error.message) { setErrorMessage(error.message); } localStorage.setItem("loops-form-timestamp", ""); }); }; switch (formState) { case SUCCESS: return (

{formStyles.successMessage}

); case ERROR: return ( <> ); default: return (
Join Our Newsletter

Get notified about latest updates and new launches.

setEmail(e.target.value)} />
); } function SignUpFormError() { return (

{errorMessage || "Oops! Something went wrong, please try again"}

); } function BackButton() { const [isHovered, setIsHovered] = useState(false); return ( ); } } function isValidEmail(email: any) { return /.+@.+/.test(email); }