From d2c57c5ef083e182d6dada434f6de50a29e95561 Mon Sep 17 00:00:00 2001 From: Dan Head Date: Sun, 26 Feb 2017 14:51:22 +0000 Subject: [PATCH] Send email on RSVP --- package.json | 1 + src/core/emailer.js | 62 ++++++++++++++++++++++++++++++++++++++++ src/routes/rsvp/index.js | 30 ++----------------- src/server.js | 2 ++ yarn.lock | 4 +++ 5 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/core/emailer.js diff --git a/package.json b/package.json index 7186625..7277d5f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "isomorphic-style-loader": "^1.1.0", "jsonwebtoken": "^7.2.1", "node-fetch": "^1.6.3", + "nodemailer": "^3.1.3", "normalize.css": "^5.0.0", "passport": "^0.3.2", "passport-facebook": "^2.1.1", diff --git a/src/core/emailer.js b/src/core/emailer.js new file mode 100644 index 0000000..059e970 --- /dev/null +++ b/src/core/emailer.js @@ -0,0 +1,62 @@ +import Nodemailer from 'nodemailer'; +import { email, starters, mains, getRsvpEnd } from '../config'; +import { handleError } from '../core/rollbar'; + +const transporter = Nodemailer.createTransport({ + service: 'gmail', + auth: { + user: email.senderAddress, + pass: email.password, + }, +}); + +// Debounce sending the email to avoid spamming +const emailTimers = {}; + +function getEmailText(person) { + return `Dear ${person.firstname}.\n\n +Thanks for your RSVP, ${person.attending ? 'we\'re delighted to hear you will be attending' : 'we\'re sorry to hear you will not be attending'}.\n +${person.attending ? `Your meal choices are ${starters[person.starter]} and ${mains[person.main]}.\n` : ''} +${person.attending && person.dietary.length > 0 ? `Your dietary requirements are: ${person.dietary}.\n` : ''} +You can modify your RSVP up until ${getRsvpEnd()} here: https://danielandhana.com/${person.password}.\n +${person.attending ? 'See you at our wedding,' : 'See you soon,'}\n +Daniel and Hana +`; +} + +function getEmailHTML(person) { + return `

Dear ${person.firstname}.

+

Thanks for your RSVP, ${person.attending ? 'we\'re delighted to hear you will be attending' : 'we\'re sorry to hear you will not be attending'}.

+${person.attending ? `

Your meal choices are ${starters[person.starter]} and ${mains[person.main]}.

` : ''} +${person.attending && person.dietary.length > 0 ? `

Your dietary requirements are: ${person.dietary}.

` : ''} +

You can modify your RSVP up until ${getRsvpEnd()} here.

+

${person.attending ? 'See you at our wedding,' : 'See you soon,'}

+

Daniel and Hana

+`; +} + +export const sendRSVPEmail = function sendRSVPEmail(person) { + const mailOptions = { + from: `"${email.senderName}" <${email.senderAddress}>`, + to: person.email, + subject: 'Thank you for your RSVP', + text: getEmailText(person), + html: getEmailHTML(person), + }; + // send mail with defined transport object + transporter.sendMail(mailOptions, (error) => { + if (error) { + handleError(error); + } + }); +}; + +export const sendRSVPEmailWithDebounce = function sendRSVPEmailWithDebounce(person) { + if (person.key) { + // Stop previous email + clearTimeout(emailTimers[person.key]); + emailTimers[person.key] = setTimeout(() => { + sendRSVPEmail(person); + }, 10000); + } +}; diff --git a/src/routes/rsvp/index.js b/src/routes/rsvp/index.js index 2db50c2..ea6baf8 100644 --- a/src/routes/rsvp/index.js +++ b/src/routes/rsvp/index.js @@ -3,40 +3,14 @@ import Layout from '../../components/Layout'; import fetchQL from '../../core/fetchQL'; import Rsvp from './Rsvp'; import NotFound from '../notFound/NotFound'; -import { rsvpEndDate } from '../../config'; +import { rsvpEndDate, getRsvpEnd } from '../../config'; let title = 'RSVP'; -const months = [ - 'January', - 'February', - 'March', - 'April', - 'May', - 'June', - 'July', - 'August', - 'September', - 'October', - 'November', - 'December', -]; - export default { path: '/rsvp/:password', - getRsvpEnd() { - const date = rsvpEndDate.getDate(); - let dateSuffix = 'th'; - if (date === 1 || date === 21 || date === 31) { dateSuffix = 'st'; } - if (date === 2 || date === 22) { dateSuffix = 'nd'; } - if (date === 3 || date === 23) { dateSuffix = 'rd'; } - const month = months[rsvpEndDate.getMonth()]; - const year = rsvpEndDate.getFullYear(); - return `${date}${dateSuffix} ${month} ${year}`; - }, - isEditable() { return new Date() < rsvpEndDate; }, @@ -68,7 +42,7 @@ export default { diff --git a/src/server.js b/src/server.js index c15a099..196c5b1 100644 --- a/src/server.js +++ b/src/server.js @@ -26,6 +26,7 @@ import Person from './data/models/Person'; import Rollbar from './core/rollbar'; import { sendSlackMsg, sendSlackMsgWithDebounce } from './core/slack'; import sequelize from './data/sequelize'; +import { sendRSVPEmailWithDebounce } from './core/emailer'; const app = express(); @@ -170,6 +171,7 @@ app.post('/rsvp/save', (req, res) => { const dietary = (data.attending && data.dietary) ? `\nDietary requirements: ${data.dietary}` : ''; sendSlackMsgWithDebounce(`${data.firstname} ${data.lastname} has saved their RSVP:${attending}${starter}${main}${dietary}`, '#wedding-rsvps', req.body.key); + sendRSVPEmailWithDebounce(data); } res.json({ success: true, diff --git a/yarn.lock b/yarn.lock index 73e87c5..fa1438e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4423,6 +4423,10 @@ node-uuid@1.4.x, node-uuid@~1.4.4, node-uuid@~1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" +nodemailer@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-3.1.3.tgz#331b3265522f52b128bfc2dcaba9fe317b82f018" + "nopt@2 || 3", nopt@3.0.x, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"