From 5b7b3ca33c69a7f76d5e17e335b3a118ddf6fcf4 Mon Sep 17 00:00:00 2001 From: Bouncey Date: Thu, 6 Sep 2018 14:59:52 +0100 Subject: [PATCH] feat(unsubscribe): Add unsubscribed page and update api --- .prettierrc | 2 +- api-server/server/boot/randomAPIs.js | 197 ++++++++---------- client/gatsby-config.js | 4 +- .../client-only-routes/ShowUnsubscribed.js | 41 ++++ client/src/pages/unsubscribed.js | 19 ++ client/static/_redirects | 7 +- 6 files changed, 158 insertions(+), 112 deletions(-) create mode 100644 client/src/client-only-routes/ShowUnsubscribed.js create mode 100644 client/src/pages/unsubscribed.js diff --git a/.prettierrc b/.prettierrc index 36301bc5cf..c3e03287be 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "semi": false, + "semi": true, "singleQuote": true, "trailingComma": "es5" } diff --git a/api-server/server/boot/randomAPIs.js b/api-server/server/boot/randomAPIs.js index bf0dd5068e..0dfaae46a5 100644 --- a/api-server/server/boot/randomAPIs.js +++ b/api-server/server/boot/randomAPIs.js @@ -1,5 +1,7 @@ import request from 'request'; +import { homeLocation } from '../../../config/env'; + import constantStrings from '../utils/constantStrings.json'; import testimonials from '../resources/testimonials.json'; @@ -8,13 +10,14 @@ const githubSecret = process.env.GITHUB_SECRET; module.exports = function(app) { const router = app.loopback.Router(); + const api = app.loopback.Router(); const User = app.models.User; router.get('/api/github', githubCalls); router.get('/chat', chat); router.get('/twitch', twitch); - router.get('/u/:email', unsubscribe); - router.get('/unsubscribe/:email', unsubscribe); + router.get('/u/:email', unsubscribeDepricated); + router.get('/unsubscribe/:email', unsubscribeDepricated); router.get('/ue/:unsubscribeId', unsubscribeById); router.get( '/the-fastest-web-page-on-the-internet', @@ -22,7 +25,7 @@ module.exports = function(app) { ); router.get('/unsubscribed/:unsubscribeId', unsubscribedWithId); router.get('/unsubscribed', unsubscribed); - router.get('/resubscribe/:unsubscribeId', resubscribe); + api.get('/resubscribe/:unsubscribeId', resubscribe); router.get('/nonprofits', nonprofits); router.get('/nonprofits-form', nonprofitsForm); router.get('/pmi-acp-agile-project-managers', agileProjectManagers); @@ -32,21 +35,24 @@ module.exports = function(app) { router.get('/all-stories', showAllTestimonials); router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork); router.get( - '/software-resources-for-nonprofits', - softwareResourcesForNonprofits + '/software-resources-for-nonprofits', + softwareResourcesForNonprofits ); router.get('/academic-honesty', academicHonesty); app.use(router); + app.use('/internal', api); + function chat(req, res) { res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp'); } function howNonprofitProjectsWork(req, res) { - res.redirect(301, - 'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a'); - + res.redirect( + 301, + 'https://medium.freecodecamp.com/open-source-for-good-1a0ea9f32d5a' + ); } function softwareResourcesForNonprofits(req, res) { @@ -56,9 +62,9 @@ module.exports = function(app) { } function academicHonesty(req, res) { - res.render('resources/academic-honesty', { - title: 'Academic Honesty policy' - }); + res.render('resources/academic-honesty', { + title: 'Academic Honesty policy' + }); } function theFastestWebPageOnTheInternet(req, res) { @@ -69,7 +75,8 @@ module.exports = function(app) { function showTestimonials(req, res) { res.render('resources/stories', { - title: 'Testimonials from Happy freeCodeCamp Students ' + + title: + 'Testimonials from Happy freeCodeCamp Students ' + 'who got Software Engineer Jobs', stories: testimonials.slice(0, 72), moreStories: true @@ -78,7 +85,8 @@ module.exports = function(app) { function showAllTestimonials(req, res) { res.render('resources/stories', { - title: 'Testimonials from Happy freeCodeCamp Students ' + + title: + 'Testimonials from Happy freeCodeCamp Students ' + 'who got Software Engineer Jobs', stories: testimonials, moreStories: false @@ -119,88 +127,51 @@ module.exports = function(app) { res.redirect('https://twitch.tv/freecodecamp'); } - function unsubscribe(req, res, next) { - req.checkParams( - 'email', - `"${req.params.email}" isn't a valid email address.` - ).isEmail(); - const errors = req.validationErrors(true); - if (errors) { - req.flash('error', { msg: errors.email.msg }); - return res.redirect('/'); - } - return User.find({ - where: { - email: req.params.email - } - }, (err, users) => { - if (err) { return next(err); } - if (!users.length) { - req.flash('info', { - msg: 'Email address not found. Please update your Email ' + - 'preferences from your settings.' - }); - return res.redirect('/'); - } - - const updates = users.map(user => { - return new Promise((resolve, reject) => - user.updateAttributes({ - sendQuincyEmail: false - }, (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }) - ); - }); - return Promise.all(updates) - .then(() => { - req.flash('info', { - msg: 'We\'ve successfully updated your Email preferences.' - }); - return res.redirect('/unsubscribed'); - }) - .catch(next); - }); + function unsubscribeDepricated(req, res) { + req.flash( + 'info', + 'We are no longer able to process this unsubscription request. ' + + 'Please go to your settings to update your email preferences' + ); + res.redirectWithFlash(homeLocation); } function unsubscribeById(req, res, next) { const { unsubscribeId } = req.params; if (!unsubscribeId) { - req.flash('info', { - msg: 'We could not find an account to unsubscribe' - }); - return res.redirect('/'); + req.flash('info', 'We could not find an account to unsubscribe'); + return res.redirectWithFlash(homeLocation); } return User.find({ where: { unsubscribeId } }, (err, users) => { if (err || !users.length) { - req.flash('info', { - msg: 'We could not find an account to unsubscribe' - }); - return res.redirect('/'); + req.flash('info', 'We could not find an account to unsubscribe'); + return res.redirectWithFlash(homeLocation); } const updates = users.map(user => { return new Promise((resolve, reject) => - user.updateAttributes({ - sendQuincyEmail: false - }, (err) => { - if (err) { - reject(err); - } else { - resolve(); + user.updateAttributes( + { + sendQuincyEmail: false + }, + err => { + if (err) { + reject(err); + } else { + resolve(); + } } - }) + ) ); }); return Promise.all(updates) .then(() => { - req.flash('success', { - msg: 'We\'ve successfully updated your email preferences.' - }); - return res.redirect(`/unsubscribed/${unsubscribeId}`); + req.flash( + 'success', + "We've successfully updated your email preferences." + ); + return res.redirectWithFlash( + `${homeLocation}/unsubscribed/${unsubscribeId}` + ); }) .catch(next); }); @@ -222,40 +193,45 @@ module.exports = function(app) { function resubscribe(req, res, next) { const { unsubscribeId } = req.params; - return User.find({ where: { unsubscribeId } }, - (err, users) => { - if (err || !users.length) { - req.flash('info', { - msg: 'We could not find an account to unsubscribe' - }); - return res.redirect('/'); - - } - const [ user ] = users; - return new Promise((resolve, reject) => - user.updateAttributes({ + if (!unsubscribeId) { + req.flash( + 'info', + 'We we unable to process this request, please check and try againÍ' + ); + res.redirect(homeLocation); + } + return User.find({ where: { unsubscribeId } }, (err, users) => { + if (err || !users.length) { + req.flash('info', 'We could not find an account to resubscribe'); + return res.redirectWithFlash(homeLocation); + } + const [user] = users; + return new Promise((resolve, reject) => + user.updateAttributes( + { sendQuincyEmail: true - }, (err) => { + }, + err => { if (err) { reject(err); } else { resolve(); } - }) + } ) + ) .then(() => { - req.flash('success', { - msg: - 'We\'ve successfully updated your email preferences. Thank you ' + - 'for resubscribing.' - }); - return res.redirect('/'); + req.flash( + 'success', + "We've successfully updated your email preferences. Thank you " + + 'for resubscribing.' + ); + return res.redirectWithFlash(homeLocation); }) .catch(next); }); } - function githubCalls(req, res, next) { var githubHeaders = { headers: { @@ -273,10 +249,12 @@ module.exports = function(app) { ].join(''), githubHeaders, function(err, status1, pulls) { - if (err) { return next(err); } - pulls = pulls ? - Object.keys(JSON.parse(pulls)).length : - 'Can\'t connect to github'; + if (err) { + return next(err); + } + pulls = pulls + ? Object.keys(JSON.parse(pulls)).length + : "Can't connect to github"; return request( [ @@ -288,10 +266,13 @@ module.exports = function(app) { ].join(''), githubHeaders, function(err, status2, issues) { - if (err) { return next(err); } - issues = ((pulls === parseInt(pulls, 10)) && issues) ? - Object.keys(JSON.parse(issues)).length - pulls : - "Can't connect to GitHub"; + if (err) { + return next(err); + } + issues = + pulls === parseInt(pulls, 10) && issues + ? Object.keys(JSON.parse(issues)).length - pulls + : "Can't connect to GitHub"; return res.send({ issues: issues, pulls: pulls diff --git a/client/gatsby-config.js b/client/gatsby-config.js index f3c0d26ce2..78e5379808 100644 --- a/client/gatsby-config.js +++ b/client/gatsby-config.js @@ -11,7 +11,7 @@ module.exports = { 'gatsby-plugin-react-helmet', { resolve: 'gatsby-plugin-create-client-paths', - options: { prefixes: ['/certification/*'] } + options: { prefixes: ['/certification/*', '/unsubscribed/*'] } }, { resolve: 'gatsby-plugin-manifest', @@ -27,4 +27,4 @@ module.exports = { }, 'gatsby-plugin-sitemap' ] -}; +} diff --git a/client/src/client-only-routes/ShowUnsubscribed.js b/client/src/client-only-routes/ShowUnsubscribed.js new file mode 100644 index 0000000000..b0c6ae0647 --- /dev/null +++ b/client/src/client-only-routes/ShowUnsubscribed.js @@ -0,0 +1,41 @@ +import React from 'react' +import { Grid, Panel, Button } from '@freecodecamp/react-bootstrap' + +import { apiLocation } from '../../config/env.json' +import Layout from '../components/Layout' +import FullWidthRow from '../components/helpers/FullWidthRow' +import { Spacer } from '../components/helpers' + +function ShowUnsubscribed({ unsubscribeId }) { + return ( + + + + + + + +

You have successfully been unsubscribed

+

Whatever you go on to, keep coding!

+
+
+ {unsubscribeId ? ( + + + + ) : null} +
+
+ ) +} + +ShowUnsubscribed.displayName = 'ShowUnsubscribed' + +export default ShowUnsubscribed diff --git a/client/src/pages/unsubscribed.js b/client/src/pages/unsubscribed.js new file mode 100644 index 0000000000..a366ce4a54 --- /dev/null +++ b/client/src/pages/unsubscribed.js @@ -0,0 +1,19 @@ +import React from 'react' +import { Router } from '@reach/router' + +import RedirectHome from '../components/RedirectHome' +import ShowUnsubscribed from '../client-only-routes/ShowUnsubscribed' + +function Unsubscribed() { + return ( + + + + + + ) +} + +Unsubscribed.displayName = 'Unsubscribed' + +export default Unsubscribed diff --git a/client/static/_redirects b/client/static/_redirects index 1168df4046..a0650ec966 100644 --- a/client/static/_redirects +++ b/client/static/_redirects @@ -10,4 +10,9 @@ /:username/front-end-certification /certification/:username/legacy-front-end 301 /:username/data-visualization-certification /certification/:username/legacy-data-visualization 301 /:username/back-end-certification /certification/:username/legacy-back-end 301 -/:username/full-stack-certification /certification/:username/full-stack 301 \ No newline at end of file +/:username/full-stack-certification /certification/:username/full-stack 301 + +# unsunscribe redirects +/u/* https://api.freecodecamp.org/u/:splat +/unsunscribe/* https://api.freecodecamp.org/unsunscribe/:splat +/ue/* https://api.freecodecamp.org/ue/:splat \ No newline at end of file