From 7bcd794c32eaa3a19cd6755569ddd605a5b573d8 Mon Sep 17 00:00:00 2001 From: Skyler Cohen Date: Thu, 29 Jan 2026 03:05:23 -0500 Subject: [PATCH 1/4] fix(newsletter sign up form): Prevent duplicate submissions in the frontend once a submission is completed --- static/js/NewsletterSignUpForm.jsx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index a067b97a9d..32fd188cf8 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -15,12 +15,17 @@ export function NewsletterSignUpForm({ const [educatorCheck, setEducatorCheck] = useState(false); const [subscribeMessage, setSubscribeMessage] = useState(null); const [showNameInputs, setShowNameInputs] = useState(false); + const [isSubscribed, setIsSubscribed] = useState(false); function handleSubscribe() { + if (isSubscribed) { + return; + } if (showNameInputs === true) { // submit if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); subscribe(firstName, lastName, email, educatorCheck, additionalNewsletterMailingLists).then(res => { + setIsSubscribed(true); setSubscribeMessage("Subscribed! Welcome to our list."); Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); }).catch(error => { @@ -47,6 +52,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Email address")} type="email" value={email} + disabled={isSubscribed} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -57,10 +63,11 @@ export function NewsletterSignUpForm({ aria-label="כתובת אימייל" type="email" value={email} + disabled={isSubscribed} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> - {!showNameInputs ? {Sefaria._("Submit")} : null} + {!showNameInputs && !isSubscribed ? {Sefaria._("Submit")} : null} {showNameInputs ? <> setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -80,6 +88,7 @@ export function NewsletterSignUpForm({ aria-label="שם פרטי" type="text" value={firstName} + disabled={isSubscribed} onChange={e => setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -90,6 +99,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Last Name")} type="text" value={lastName} + disabled={isSubscribed} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -100,12 +110,13 @@ export function NewsletterSignUpForm({ aria-label="שם משפחה" type="text" value={lastName} + disabled={isSubscribed} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> {includeEducatorOption ? - : null} - {Sefaria._("Submit")} + : null} + {!isSubscribed && {Sefaria._("Submit")}} : null} {subscribeMessage ? @@ -116,7 +127,7 @@ export function NewsletterSignUpForm({ } -const EducatorCheckbox = ({educatorCheck, setEducatorCheck}) => { +const EducatorCheckbox = ({educatorCheck, setEducatorCheck, disabled}) => { return (
@@ -125,6 +136,7 @@ const EducatorCheckbox = ({educatorCheck, setEducatorCheck}) => { className="educatorNewsletterInput" id="educator-check-en" checked={educatorCheck} + disabled={disabled} onChange={e => setEducatorCheck(!!e.target.checked)}/> @@ -134,6 +146,7 @@ const EducatorCheckbox = ({educatorCheck, setEducatorCheck}) => { className="educatorNewsletterInput" id="educator-check-he" checked={educatorCheck} + disabled={disabled} onChange={e => setEducatorCheck(!!e.target.checked)}/> From 7cfbf33036ffd6f19d972196d31458e8817460c7 Mon Sep 17 00:00:00 2001 From: Skyler Cohen Date: Sun, 1 Feb 2026 20:30:30 -0500 Subject: [PATCH 2/4] fix(newsletter sign up form): Prevent duplicate submissions in the frontend during the submission process while the API call is being made --- static/js/NewsletterSignUpForm.jsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index 32fd188cf8..cb6348fc5f 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -16,19 +16,23 @@ export function NewsletterSignUpForm({ const [subscribeMessage, setSubscribeMessage] = useState(null); const [showNameInputs, setShowNameInputs] = useState(false); const [isSubscribed, setIsSubscribed] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); function handleSubscribe() { - if (isSubscribed) { + if (isSubscribed || isSubmitting) { return; } if (showNameInputs === true) { // submit if (firstName.length > 0 && lastName.length > 0) { setSubscribeMessage("Subscribing..."); + setIsSubmitting(true); subscribe(firstName, lastName, email, educatorCheck, additionalNewsletterMailingLists).then(res => { setIsSubscribed(true); + setIsSubmitting(false); setSubscribeMessage("Subscribed! Welcome to our list."); Sefaria.track.event("Newsletter", "Subscribe from " + contextName, ""); }).catch(error => { + setIsSubmitting(false); setSubscribeMessage(error?.message || "Sorry, there was an error."); setShowNameInputs(false); }); @@ -52,7 +56,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Email address")} type="email" value={email} - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -63,7 +67,7 @@ export function NewsletterSignUpForm({ aria-label="כתובת אימייל" type="email" value={email} - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -77,7 +81,7 @@ export function NewsletterSignUpForm({ type="text" value={firstName} autoFocus - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -88,7 +92,7 @@ export function NewsletterSignUpForm({ aria-label="שם פרטי" type="text" value={firstName} - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -99,7 +103,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Last Name")} type="text" value={lastName} - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -110,12 +114,12 @@ export function NewsletterSignUpForm({ aria-label="שם משפחה" type="text" value={lastName} - disabled={isSubscribed} + disabled={isSubscribed || isSubmitting} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> {includeEducatorOption ? - : null} + : null} {!isSubscribed && {Sefaria._("Submit")}} : null} From eafcb3adb41c9e5ab81f244eeae575653424c71c Mon Sep 17 00:00:00 2001 From: Skyler Cohen Date: Sun, 1 Feb 2026 23:19:30 -0500 Subject: [PATCH 3/4] chore(newsletter sign up form): Refactor to use derived state from the state variables --- static/js/NewsletterSignUpForm.jsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index cb6348fc5f..da7f4f1940 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -18,8 +18,10 @@ export function NewsletterSignUpForm({ const [isSubscribed, setIsSubscribed] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); + const isFormDisabled = isSubscribed || isSubmitting; + function handleSubscribe() { - if (isSubscribed || isSubmitting) { + if (isFormDisabled) { return; } if (showNameInputs === true) { // submit @@ -56,7 +58,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Email address")} type="email" value={email} - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -67,7 +69,7 @@ export function NewsletterSignUpForm({ aria-label="כתובת אימייל" type="email" value={email} - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -81,7 +83,7 @@ export function NewsletterSignUpForm({ type="text" value={firstName} autoFocus - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -92,7 +94,7 @@ export function NewsletterSignUpForm({ aria-label="שם פרטי" type="text" value={firstName} - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setFirstName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -103,7 +105,7 @@ export function NewsletterSignUpForm({ aria-label={Sefaria._("Last Name")} type="text" value={lastName} - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> @@ -114,12 +116,12 @@ export function NewsletterSignUpForm({ aria-label="שם משפחה" type="text" value={lastName} - disabled={isSubscribed || isSubmitting} + disabled={isFormDisabled} onChange={e => setLastName(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> {includeEducatorOption ? - : null} + : null} {!isSubscribed && {Sefaria._("Submit")}} : null} From 2297662732868cddacc1c9f88fe9b59d52142736 Mon Sep 17 00:00:00 2001 From: Skyler Cohen Date: Mon, 2 Feb 2026 02:43:13 -0500 Subject: [PATCH 4/4] chore(newsletter sign up form): Refactor to conditionally render the submit button when the form is not submitting or already subscribed --- static/js/NewsletterSignUpForm.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/js/NewsletterSignUpForm.jsx b/static/js/NewsletterSignUpForm.jsx index da7f4f1940..322f83b8c4 100644 --- a/static/js/NewsletterSignUpForm.jsx +++ b/static/js/NewsletterSignUpForm.jsx @@ -73,7 +73,7 @@ export function NewsletterSignUpForm({ onChange={e => setEmail(e.target.value)} onKeyUp={(e) => Util.handleEnterKey(e, handleSubscribe)}/> - {!showNameInputs && !isSubscribed ? {Sefaria._("Submit")} : null} + {!showNameInputs && !isFormDisabled ? {Sefaria._("Submit")} : null} {showNameInputs ? <> {includeEducatorOption ? : null} - {!isSubscribed && {Sefaria._("Submit")}} + {!isFormDisabled && {Sefaria._("Submit")}} : null} {subscribeMessage ?