Prompting user to update name when banned.

This commit is contained in:
David Jay
2017-02-01 19:53:37 -05:00
parent e3da45daeb
commit d703cc2168
14 changed files with 123 additions and 75 deletions
+3
View File
@@ -11,6 +11,7 @@ const {fetchAssetSuccess} = assetActions;
import {queryStream} from 'coral-framework/graphql/queries';
import {postComment, postAction, deleteAction} from 'coral-framework/graphql/mutations';
import {editName} from 'coral-framework/actions/user';
import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework';
import Stream from './Stream';
@@ -123,6 +124,7 @@ class Embed extends Component {
<RestrictedContent restricted={banned} restrictedComp={
<SuspendedAccount
canEditName={user && user.canEditName}
editName={this.props.editName}
/>
}>
{
@@ -200,6 +202,7 @@ const mapDispatchToProps = dispatch => ({
loadAsset: (asset) => dispatch(fetchAssetSuccess(asset)),
addNotification: (type, text) => dispatch(addNotification(type, text)),
clearNotification: () => dispatch(clearNotification()),
editName: (displayName) => dispatch(editName(displayName)),
showSignInDialog: (offset) => dispatch(showSignInDialog(offset)),
logout: () => dispatch(logout()),
dispatch: d => dispatch(d)
+4 -12
View File
@@ -1,4 +1,3 @@
import * as actions from '../constants/user';
import {addNotification} from '../actions/notification';
import coralApi from '../helpers/response';
@@ -6,16 +5,9 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from './../translations';
const lang = new I18n(translations);
const saveBioRequest = () => ({type: actions.SAVE_BIO_REQUEST});
const saveBioSuccess = settings => ({type: actions.SAVE_BIO_SUCCESS, settings});
const saveBioFailure = error => ({type: actions.SAVE_BIO_FAILURE, error});
export const saveBio = (user_id, formData) => dispatch => {
dispatch(saveBioRequest());
coralApi('/account/settings', {method: 'PUT', body: formData})
export const editName = (displayName) => (dispatch) => {
return coralApi('/account/displayname', {method: 'PUT', body: {displayName}})
.then(() => {
dispatch(addNotification('success', lang.t('successBioUpdate')));
dispatch(saveBioSuccess(formData));
})
.catch(error => dispatch(saveBioFailure(error)));
dispatch(addNotification('success', lang.t('successNameUpdate')));
});
};
@@ -2,3 +2,13 @@
background: #D8D8D8;
padding: 25px;
}
.editNameInput {
margin-top: 10px;
margin-bottom: 10px;
}
.alert {
margin-top: 10px;
color: #B71C1C;
}
@@ -1,15 +1,79 @@
import React from 'react';
import React, {Component, PropTypes} from 'react';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-framework/translations.json';
const lang = new I18n(translations);
import styles from './RestrictedContent.css';
import {Button} from 'coral-ui';
import validate from '../helpers/validate';
export default ({canEditName}) => (
<div className={styles.message}>
<span>{
class SuspendedAccount extends Component {
static propTypes = {
canEditName: PropTypes.bool,
editName: PropTypes.func.isRequired
}
state = {
displayName: '',
alert: ''
}
onSubmitClick = (e) => {
const {editName} = this.props;
const {displayName} = this.state;
e.preventDefault();
if (validate.displayName(displayName)) {
editName(displayName)
.then(() => window.reload())
.catch((error) => {
this.setState({alert: lang.t(`error.${error.message}`)});
});
} else {
this.setState({alert: lang.t('editName.error')});
}
}
render () {
const {canEditName} = this.props;
const {displayName, alert} = this.state;
return <div className={styles.message}>
<span>{
canEditName ?
lang.t('editName.msg')
: lang.t('bannedAccountMsg')
}</span>
{
canEditName ?
lang.t('editNameMsg')
: lang.t('bannedAccountMsg')
}</span>
</div>
);
<div>
<div className={styles.alert}>
{alert}
</div>
<label
htmlFor='displayName'
className="screen-reader-text"
aria-hidden={true}>
{lang.t('editName.label')}
</label>
<input
type='text'
className={styles.editNameInput}
value={displayName}
placeholder={lang.t('editName.label')}
id='displayName'
onChange={(e) => this.setState({displayName: e.target.value})}
rows={3}/><br/>
<Button
onClick={this.onSubmitClick}>
{
lang.t('editName.button')
}
</Button>
</div> : null
}
</div>;
}
}
export default SuspendedAccount;
+3 -3
View File
@@ -1,6 +1,6 @@
export const SAVE_BIO_REQUEST = 'SAVE_BIO_REQUEST';
export const SAVE_BIO_SUCCESS = 'SAVE_BIO_SUCCESS';
export const SAVE_BIO_FAILURE = 'SAVE_BIO_FAILURE';
export const EDIT_NAME_REQUEST = 'EDIT_NAME_REQUEST';
export const EDIT_NAME_SUCCESS = 'EDIT_NAME_SUCCESS';
export const EDIT_NAME_FAILURE = 'EDIT_NAME_FAILURE';
export const COMMENTS_BY_USER_REQUEST = 'COMMENTS_BY_USER_REQUEST';
export const COMMENTS_BY_USER_SUCCESS = 'COMMENTS_BY_USER_SUCCESS';
export const COMMENTS_BY_USER_FAILURE = 'COMMENTS_BY_USER_FAILURE';
+7 -2
View File
@@ -1,10 +1,15 @@
{
"en": {
"successUpdateSettings": "The changes you have made have been applied to the comment stream on this article",
"successBioUpdate": "Your Bio has been updated",
"successNameUpdate": "Your display name has been updated",
"contentNotAvailable": "This content is not available",
"bannedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information",
"editNameMsg": "Your account is currently suspended because your username has been deemed inappropriate. To restore your account, please enter a new username. You may contact moderator@fakeurl.com for more information.",
"editName": {
"msg": "Your account is currently suspended because your display name has been deemed inappropriate. To restore your account, please enter a new username. You may contact moderator@fakeurl.com for more information.",
"label": "New Display Name",
"button": "Submit",
"error": "Display names can contain letters, numbers and _ only"
},
"error": {
"email": "Not a valid E-Mail",
"password": "Password must be at least 8 characters",
-2
View File
@@ -2,7 +2,6 @@ const _ = require('lodash');
const Comment = require('./comment');
const Action = require('./action');
const User = require('./user');
module.exports = (context) => {
@@ -10,7 +9,6 @@ module.exports = (context) => {
return _.merge(...[
Comment,
Action,
User,
].map((mutators) => {
// Each set of mutators is a function which takes the context.
-31
View File
@@ -1,31 +0,0 @@
const UsersService = require('../../services/users');
/**
* Updates a users settings.
* @param {Object} user the user performing the request
* @param {String} bio the new user bio
* @return {Promise}
*/
const updateUserSettings = ({user}, {bio}) => {
return UsersService.updateSettings(user.id, {bio});
};
module.exports = (context) => {
// TODO: refactor to something that'll return an error in the event an attempt
// is made to mutate state while not logged in. There's got to be a better way
// to do this.
if (context.user && context.user.can('mutation:updateUserSettings')) {
return {
User: {
updateSettings: (settings) => updateUserSettings(context, settings)
}
};
}
return {
User: {
updateSettings: () => {}
}
};
};
+1 -1
View File
@@ -145,7 +145,7 @@ type Asset {
# The scraped title of the asset.
title: String
# The URL that the asset is locaed on.
# The URL that the asset is located on.
url: String
# The top level comments that are attached to the asset.
+1 -1
View File
@@ -146,7 +146,7 @@ const USER_GRAPH_OPERATIONS = [
'mutation:createComment',
'mutation:createAction',
'mutation:deleteAction',
'mutation:updateUserSettings'
'mutation:editName'
];
/**
+8 -2
View File
@@ -117,11 +117,17 @@ router.put('/password/reset', (req, res, next) => {
router.put('/displayname', authorization.needed(), (req, res, next) => {
UsersService
.editUsername(req.user.id, req.body.displayName)
.editName(req.user.id, req.body.displayName)
.then(() => {
res.status(204).end();
})
.catch(next);
.catch(error => {
if (error.code === 11000) {
next(errors.ErrDisplayTaken);
} else {
next(error);
}
});
});
module.exports = router;
+1 -1
View File
@@ -59,7 +59,7 @@ router.post('/:user_id/status', authorization.needed('ADMIN'), (req, res, next)
router.post('/:user_id/username-enable', authorization.needed('ADMIN'), (req, res, next) => {
UsersService
.toggleUsernameEdit(req.params.user_id, true)
.toggleNameEdit(req.params.user_id, true)
.then(() => {
res.status(204).end();
})
+4 -3
View File
@@ -619,7 +619,7 @@ module.exports = class UsersService {
* @param {Boolean} canEditName sets whether the user can edit their name.
* @return {Promise}
*/
static toggleUsernameEdit(id, canEditName) {
static toggleNameEdit(id, canEditName) {
return UserModel.update({id}, {
$set: {canEditName}
});
@@ -631,14 +631,15 @@ module.exports = class UsersService {
* @param {String} displayName The new displayname for the user.
* @return {Promise}
*/
static editUsername(id, displayName) {
static editName(id, displayName) {
return UserModel.findOne({id})
.then((user) => {
return user.canEditName ?
UserModel.update({id}, {
$set: {
displayName: displayName.toLowerCase(),
canEditName: false
canEditName: false,
status: 'PENDING'
}
})
: Promise.reject(new Error('Display name editing disabled for this account.'));
+8 -8
View File
@@ -209,10 +209,10 @@ describe('services.UsersService', () => {
});
});
describe('#toggleUsernameEdit', () => {
describe('#toggleNameEdit', () => {
it('should toggle the canEditName field', () => {
return UsersService
.toggleUsernameEdit(mockUsers[0].id, true)
.toggleNameEdit(mockUsers[0].id, true)
.then(() => UsersService.findById(mockUsers[0].id))
.then((user) => {
expect(user).to.have.property('canEditName', true);
@@ -220,11 +220,11 @@ describe('services.UsersService', () => {
});
});
describe('#editUsername', () => {
describe('#editName', () => {
it('should let the user edit their username if the proper toggle is set', () => {
return UsersService
.toggleUsernameEdit(mockUsers[0].id, true)
.then(() => UsersService.editUsername(mockUsers[0].id, 'Jojo'))
.toggleNameEdit(mockUsers[0].id, true)
.then(() => UsersService.editName(mockUsers[0].id, 'Jojo'))
.then(() => UsersService.findById(mockUsers[0].id))
.then((user) => {
expect(user).to.have.property('displayName', 'jojo');
@@ -234,7 +234,7 @@ describe('services.UsersService', () => {
it('should return an error if canEditName is false', (done) => {
UsersService
.editUsername(mockUsers[0].id, 'Jojo')
.editName(mockUsers[0].id, 'Jojo')
.then(() => UsersService.findById(mockUsers[0].id))
.then(() => {
done(new Error('Error expected'));
@@ -247,8 +247,8 @@ describe('services.UsersService', () => {
it('should return an error if the username is already taken', (done) => {
UsersService
.toggleUsernameEdit(mockUsers[0].id, true)
.then(() => UsersService.editUsername(mockUsers[0].id, 'Marvel'))
.toggleNameEdit(mockUsers[0].id, true)
.then(() => UsersService.editName(mockUsers[0].id, 'Marvel'))
.then(() => UsersService.findById(mockUsers[0].id))
.then(() => {
done(new Error('Error expected'));