diff --git a/packages/component-user-manager/src/Users.js b/packages/component-user-manager/src/Users.js index 95d064990c554472413277282e82986111fc8609..8b95b8c82aef769ce39ab3073bc364a0a5c8fbde 100644 --- a/packages/component-user-manager/src/Users.js +++ b/packages/component-user-manager/src/Users.js @@ -125,6 +125,39 @@ const Users = app => { require('./routes/users/changePassword')(app.locals.models), ) + /** + * @api {patch} /api/users/subscriptions Change user's email subscription flag + * @apiGroup Users + * @apiParamExample {json} Body + * { + * "isUnsubscribed": true, + * } + * @apiSuccessExample {json} Success + * HTTP/1.1 200 OK + * { + * "id": "a6184463-b17a-42f8-b02b-ae1d755cdc6b", + * "type": "user", + * "admin": false, + * "email": "email@example.com", + * "teams": [], + * "username": "email@example.com", + * "fragments": [], + * "collections": [], + * "isConfirmed": true, + * "editorInChief": false, + * "handlingEditor": false, + * "isUnsubscribed": true + * } + * @apiErrorExample {json} Reset password errors + * HTTP/1.1 400 Bad Request + * HTTP/1.1 404 Not Found + */ + app.patch( + '/api/users/subscriptions', + authBearer, + require('./routes/users/subscriptions')(app.locals.models), + ) + // register ORCID authentication strategy orcidRoutes(app) } diff --git a/packages/component-user-manager/src/routes/users/subscriptions.js b/packages/component-user-manager/src/routes/users/subscriptions.js new file mode 100644 index 0000000000000000000000000000000000000000..7633fc64b2f1cb011c7e08786d4fa3ac8e15cfde --- /dev/null +++ b/packages/component-user-manager/src/routes/users/subscriptions.js @@ -0,0 +1,22 @@ +const { services } = require('pubsweet-component-helper-service') + +module.exports = models => async (req, res) => { + const { isUnsubscribed } = req.body + + if (!services.checkForUndefinedParams(isUnsubscribed)) + return res.status(400).json({ error: 'Missing required params.' }) + + let user + try { + user = await models.User.find(req.user) + user.isUnsubscribed = isUnsubscribed + user = await user.save() + + return res.status(200).json({ user }) + } catch (e) { + const notFoundError = await services.handleNotFoundError(e, 'User') + return res.status(notFoundError.status).json({ + error: notFoundError.message, + }) + } +} diff --git a/packages/components-faraday/src/components/UserProfile/AccountDetails.js b/packages/components-faraday/src/components/UserProfile/AccountDetails.js index 768c8a46ecafb78fb2b7ab543e18792365d92b03..81080db096d193b2d48cac5a881ce79fe5eee5a2 100644 --- a/packages/components-faraday/src/components/UserProfile/AccountDetails.js +++ b/packages/components-faraday/src/components/UserProfile/AccountDetails.js @@ -29,7 +29,7 @@ export default compose( withJournal, withState('isEdit', 'setEdit', false), withHandlers({ - setEditMode: ({ setEdit }) => value => setEdit(value), + setEditMode: ({ setEdit }) => value => () => setEdit(value), }), )(AccountDetails) diff --git a/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js b/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js index 22dadc2aa731f7a0406cc81ff7e968b736bc709e..7e085536a808b582f8faa40dfbfc2fb5a1602e95 100644 --- a/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js +++ b/packages/components-faraday/src/components/UserProfile/AccountDetailsCard.js @@ -3,10 +3,10 @@ import React, { Fragment } from 'react' import { Row, RowItem, - LabelHeader, + LinkText, LabelTitle, + LabelHeader, DefaultText, - LinkText, } from '../UIComponents/FormItems' import { getUserTitle } from '../utils' @@ -48,7 +48,7 @@ const AccountDetailsCard = ({ </Row> <Row noMargin> <RowItem> - <LinkText onClick={() => setEditMode(true)}>Edit details</LinkText> + <LinkText onClick={setEditMode(true)}>Edit details</LinkText> <LinkText onClick={() => history.push('/profile/change-password')}> Change Password </LinkText> diff --git a/packages/components-faraday/src/components/UserProfile/AccountDetailsEdit.js b/packages/components-faraday/src/components/UserProfile/AccountDetailsEdit.js index f247552c6e71b68f61cbda82f37ee768f8fb20ee..a88c42c6e5d004d6642e21faa60c45940dde1505 100644 --- a/packages/components-faraday/src/components/UserProfile/AccountDetailsEdit.js +++ b/packages/components-faraday/src/components/UserProfile/AccountDetailsEdit.js @@ -11,7 +11,7 @@ const AccountDetailsEdit = ({ journal, user, setEditMode, handleSubmit }) => ( <Root onSubmit={handleSubmit}> <EditUserForm journal={journal} title="Edit account details" user={user} /> <Row> - <Button onClick={() => setEditMode(false)}>Cancel</Button> + <Button onClick={setEditMode(false)}>Cancel</Button> <Button primary type="submit"> Save </Button> diff --git a/packages/components-faraday/src/components/UserProfile/EmailNotifications.js b/packages/components-faraday/src/components/UserProfile/EmailNotifications.js index 853c24047d9ff22f56f13d0ffa96eb1ff9044961..7f20c07ecc07603373662c5cdf320df56a87a260 100644 --- a/packages/components-faraday/src/components/UserProfile/EmailNotifications.js +++ b/packages/components-faraday/src/components/UserProfile/EmailNotifications.js @@ -1,9 +1,14 @@ import React from 'react' import styled from 'styled-components' +import { compose, withHandlers } from 'recompose' +import { + withModal, + ConfirmationModal, +} from 'pubsweet-component-modal/src/components' import { Row, RowItem, LabelHeader, LinkText } from '../UIComponents/FormItems' -const EmailNotifications = ({ subscribed = '' }) => ( +const EmailNotifications = ({ subscribed = '', subscribe, unsubscribe }) => ( <Root> <Row noMargin> <RowItem> @@ -13,20 +18,48 @@ const EmailNotifications = ({ subscribed = '' }) => ( {!subscribed ? ( <Row noMargin> <RowItem> - <LinkText>Re-subscribe</LinkText> + <LinkText onClick={subscribe}>Re-subscribe</LinkText> </RowItem> </Row> ) : ( <Row noMargin> <RowItem> - <LinkText>Unsubscribe</LinkText> + <LinkText onClick={unsubscribe}>Unsubscribe</LinkText> </RowItem> </Row> )} </Root> ) -export default EmailNotifications +export default compose( + withModal(props => ({ + modalComponent: ConfirmationModal, + })), + withHandlers({ + subscribe: ({ showModal, hideModal, changeEmailSubscription }) => () => { + showModal({ + title: 'Subscribe to emails', + subtitle: 'Are you sure you want to subscribe to emails?', + onConfirm: () => { + changeEmailSubscription(false) + hideModal() + }, + onCancel: hideModal, + }) + }, + unsubscribe: ({ showModal, hideModal, changeEmailSubscription }) => () => { + showModal({ + title: 'Unsubscribe from emails', + subtitle: 'Are you sure you want to unsubscribe from emails?', + onConfirm: () => { + changeEmailSubscription(true) + hideModal() + }, + onCancel: hideModal, + }) + }, + }), +)(EmailNotifications) // #region styles const Root = styled.div` diff --git a/packages/components-faraday/src/components/UserProfile/UserProfilePage.js b/packages/components-faraday/src/components/UserProfile/UserProfilePage.js index 462abc4c48ed0be8bc05cbab2e7a147cc640b537..f45f86e3fd551d289c8e3b57ecb693238f2422c0 100644 --- a/packages/components-faraday/src/components/UserProfile/UserProfilePage.js +++ b/packages/components-faraday/src/components/UserProfile/UserProfilePage.js @@ -6,11 +6,12 @@ import styled from 'styled-components' import { selectCurrentUser } from 'xpub-selectors' import { BreadcrumbsHeader } from 'pubsweet-components-faraday/src/components' -import AccountDetails from './AccountDetails' import LinkOrcID from './LinkOrcID' +import AccountDetails from './AccountDetails' import EmailNotifications from './EmailNotifications' +import { changeEmailSubscription } from '../../redux/users' -const UserProfilePage = ({ history, user }) => ( +const UserProfilePage = ({ history, user, changeEmailSubscription }) => ( <Root> <BreadcrumbsHeader history={history} @@ -20,15 +21,21 @@ const UserProfilePage = ({ history, user }) => ( underlined /> <AccountDetails history={history} user={user} /> - <EmailNotifications subscribed={get(user, 'subscription')} /> + <EmailNotifications + changeEmailSubscription={changeEmailSubscription} + subscribed={!get(user, 'isUnsubscribed')} + /> <LinkOrcID id={get(user, 'id')} orcid={get(user, 'orcid')} /> </Root> ) export default compose( - connect(state => ({ - user: selectCurrentUser(state), - })), + connect( + state => ({ + user: selectCurrentUser(state), + }), + { changeEmailSubscription }, + ), )(UserProfilePage) // #region styles diff --git a/packages/components-faraday/src/redux/users.js b/packages/components-faraday/src/redux/users.js index a4e6d1a12984cde20989701eadbeddae1728779b..973675ef25bdc0a7ef2f5bd0c0fe76e86fdd9a9b 100644 --- a/packages/components-faraday/src/redux/users.js +++ b/packages/components-faraday/src/redux/users.js @@ -1,5 +1,6 @@ import { get } from 'lodash' -import { create } from 'pubsweet-client/src/helpers/api' +import { actions } from 'pubsweet-client' +import { create, update } from 'pubsweet-client/src/helpers/api' const LOGIN_SUCCESS = 'LOGIN_SUCCESS' @@ -20,3 +21,9 @@ export const confirmUser = (userId, confirmationToken) => dispatch => localStorage.setItem('token', user.token) return dispatch(loginSuccess(user)) }) + +export const changeEmailSubscription = isUnsubscribed => dispatch => { + update(`/users/subscriptions`, { + isUnsubscribed, + }).then(() => dispatch(actions.getCurrentUser())) +} diff --git a/packages/xpub-faraday/config/default.js b/packages/xpub-faraday/config/default.js index 522a8f83e554e5fd3085232ad640418d3bcba56b..37367502a5d21c4d8aeb99f715a44c9e8ec16614 100644 --- a/packages/xpub-faraday/config/default.js +++ b/packages/xpub-faraday/config/default.js @@ -46,7 +46,7 @@ module.exports = { API_ENDPOINT: '/api', baseUrl: process.env.CLIENT_BASE_URL || 'http://localhost:3000', 'login-redirect': '/', - 'redux-log': false, + 'redux-log': true, theme: process.env.PUBSWEET_THEME, }, orcid: { diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index 61e603fe92ae1d12ffedecae79d2774207df71ed..f8ca55e0728ebab8c247e0e0edb62b1cb114f1c8 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -131,6 +131,7 @@ module.exports = { confirmationToken: Joi.string().allow(''), agreeTC: Joi.boolean(), isActive: Joi.boolean().default(true), + isUnsubscribed: Joi.boolean(), }, team: { group: Joi.string(),