From 2c32698c6ccbd5a346e57e823a94b38351602777 Mon Sep 17 00:00:00 2001 From: Matt Lewis Date: Wed, 20 Nov 2024 00:15:56 -0700 Subject: [PATCH] Resend RSVP message on dupe submission (#43) When a user tries to RSVP twice for the same event with the same email, show them this friendly message and resend their confirmation email: image --- NOTES.md | 5 +++++ api/src/services/responses/responses.ts | 15 ++++++++++++++- web/src/components/ResponseForm/ResponseForm.tsx | 13 +++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/NOTES.md b/NOTES.md index 45b9450..4df4562 100644 --- a/NOTES.md +++ b/NOTES.md @@ -24,7 +24,10 @@ - [ ] Why are hamburger items missing on Confirm RSVP page? - [ ] URL builder helpers - [ ] Form to request resend link(s) for email address + <<<<<<< HEAD +- [ ] # Add Sentry to frontend - [ ] Resend confirmation if an RSVP entersĀ an existing email + > > > > > > > origin/main - [ ] Diff dates in `notify` so that they are omitted when unchanged - [ ] Investigate why some emailed URLs use incorrect hosts for Netlify Deploy Preview - [ ] Stably sort RSVP list @@ -39,11 +42,13 @@ - [ ] Consensually gather user emails for mailing list - [ ] Add pretty error messages for 404s (e.g. clicked an expired/tidied link) - [ ] Redirect old slugs on slug change +- [ ] Add sticky bit to "sent you a confirmation email for your RSVP" - [ ] Site-wide announcement feature - [ ] Unify email templates (header, unsub footer, etc.) - [ ] Unify email and Discord notifications - [ ] "Serious mode" for LoadingBuddy for e.g. unsubscribe requests - [ ] Notification when target email is denylisted on record creation +- [x] Resend confirmation if an RSVP entersĀ an existing email - [x] Captcha - [x] Hold RSVP locally with cookie - [x] **Scheduler engine** diff --git a/api/src/services/responses/responses.ts b/api/src/services/responses/responses.ts index bf027a7..4338c67 100644 --- a/api/src/services/responses/responses.ts +++ b/api/src/services/responses/responses.ts @@ -94,10 +94,23 @@ export const createResponse: MutationResolvers['createResponse'] = async ({ const { captchaResponse, ...input } = _input const valid = await validateCaptcha(captchaResponse) - if (!valid) + if (!valid) { throw new RedwoodError( 'Could not validate reCAPTCHA. Please refresh the page and try again.' ) + } + + const existingResponse = await db.response.findFirst({ + where: { eventId, email: input.email }, + }) + if (existingResponse) { + await sendResponseConfirmation({ event, response: existingResponse }) + throw new RedwoodError( + `You have already RSVPed to this event. ` + + `We've resent your confirmation email to ${input.email}.`, + { forbidResubmitForEmail: input.email } + ) + } const reminders: { sendAt: Date }[] = [] if (input.remindPriorSec) { diff --git a/web/src/components/ResponseForm/ResponseForm.tsx b/web/src/components/ResponseForm/ResponseForm.tsx index 3243816..a043acc 100644 --- a/web/src/components/ResponseForm/ResponseForm.tsx +++ b/web/src/components/ResponseForm/ResponseForm.tsx @@ -62,7 +62,11 @@ const ResponseForm = (props: Props) => { onCaptchaResponse, onSubmit, } = props - const { formState } = formMethods + const { formState, getValues } = formMethods + + const { email } = getValues() + const forbidResubmit = + email === error?.cause?.extensions?.['forbidResubmitForEmail'] const [exampleName, setExampleName] = useState('') useEffect(() => { @@ -172,7 +176,12 @@ const ResponseForm = (props: Props) => { {mode === 'CREATE' ? loading