diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index 5922266fa..b6fee1184 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -19,6 +19,7 @@ import Count from 'coral-plugin-comment-count/CommentCount';
import CommentBox from 'coral-plugin-commentbox/CommentBox';
import UserBox from '../../coral-sign-in/components/UserBox';
import SignInContainer from '../../coral-sign-in/containers/SignInContainer';
+import ChangeDisplayNameContainer from '../../coral-framework/containers/ChangeDisplayNameContainer';
import SuspendedAccount from '../../coral-framework/components/SuspendedAccount';
import SettingsContainer from '../../coral-settings/containers/SettingsContainer';
import RestrictedContent from '../../coral-framework/components/RestrictedContent';
@@ -51,15 +52,6 @@ class Embed extends Component {
}
componentDidMount () {
-
- // stream id, logged in user, settings
-
- // Set up messaging between embedded Iframe an parent component
-
- // this.props.getStream(path || window.location);
- // this.path = window.location.href.split('#')[0];
- //
-
pym.sendMessage('childReady');
pym.onMessage('DOMContentLoaded', hash => {
@@ -138,6 +130,7 @@ class Embed extends Component {
:
{asset.settings.closedMessage}
}
{!loggedIn && }
+ {loggedIn && }
({type: actions.SHOW_SIGNIN_DIALOG, offset});
export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG});
+export const showCreateDisplayNameDialog = () => ({type: actions.SHOW_CREATEDISPLAYNAME_DIALOG});
+export const hideCreateDisplayNameDialog = () => ({type: actions.HIDE_CREATEDISPLAYNAME_DIALOG});
+export const createDisplayName = (formData) => (dispatch) => {
+ coralApi('/users', {method: 'POST', body: formData})
+ .then(() => {
+ dispatch(createDisplayNameSuccess());
+ dispatch(hideCreateDisplayNameDialog());
+ })
+ .catch(() => dispatch(createDisplayNameFailure(lang.t('createdisplay.errorCreate'))));
+};
+const createDisplayNameSuccess = () => ({type: actions.CREATEDISPLAYNAME_SUCCESS});
+const createDisplayNameFailure = error => ({type: actions.CREATEDISPLAYNAME_FAILURE, error});
+
export const changeView = view => dispatch =>
dispatch({
type: actions.CHANGE_VIEW,
@@ -59,6 +72,7 @@ export const facebookCallback = (err, data) => dispatch => {
const user = JSON.parse(data);
dispatch(signInFacebookSuccess(user));
dispatch(hideSignInDialog());
+ dispatch(showCreateDisplayNameDialog());
dispatch(addItem(user, 'users'));
} catch (err) {
dispatch(signInFacebookFailure(err));
diff --git a/client/coral-framework/components/CreateDisplayNameDialog.js b/client/coral-framework/components/CreateDisplayNameDialog.js
new file mode 100644
index 000000000..8b61989aa
--- /dev/null
+++ b/client/coral-framework/components/CreateDisplayNameDialog.js
@@ -0,0 +1,44 @@
+import React from 'react';
+import {Dialog} from 'coral-ui';
+import styles from './styles.css';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from '../translations';
+const lang = new I18n(translations);
+
+const CreateDisplayNameDialog = ({open, handleClose, offset, loggedIn}) => (
+
+);
+
+//
+
+export default CreateDisplayNameDialog;
diff --git a/client/coral-framework/components/styles.css b/client/coral-framework/components/styles.css
new file mode 100644
index 000000000..f9d0aec6d
--- /dev/null
+++ b/client/coral-framework/components/styles.css
@@ -0,0 +1,140 @@
+.dialog {
+ border: none;
+ box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
+ width: 280px;
+ top: 10px;
+}
+
+.header {
+ margin-bottom: 20px;
+}
+
+.header h1, .separator h1{
+ text-align: center;
+ font-size: 1.2em;
+}
+
+.formField {
+ margin-top: 15px;
+}
+
+.formField label {
+ font-size: 1.08em;
+ font-weight: bold;
+ margin-bottom: 5px;
+}
+
+.formField input {
+ width: 100%;
+ display: block;
+ border: none;
+ outline: none;
+ border: 1px solid rgba(0,0,0,.12);
+ padding: 10px 6px;
+ box-sizing: border-box;
+ border-radius: 2px;
+ margin: 5px auto;
+}
+
+.footer {
+ margin: 20px auto 10px;
+ text-align: center;
+}
+
+.footer span {
+ display: block;
+ margin-bottom: 5px;
+}
+
+.footer a {
+ color: #2c69b6;
+ cursor: pointer;
+ margin: 0 5px;
+}
+
+.socialConnections {
+ margin-bottom: 20px;
+}
+
+.signInButton {
+ margin-top: 10px;
+}
+
+.close {
+ font-size: 20px;
+ line-height: 14px;
+ top: 10px;
+ right: 10px;
+ position: absolute;
+ display: block;
+ font-weight: bold;
+ color: #363636;
+ cursor: pointer;
+}
+
+.close:hover {
+ color: #6b6b6b;
+}
+
+input.error{
+ border: solid 2px #f44336;
+}
+
+.errorMsg, .hint {
+ color: grey;
+ font-weight: 600;
+ padding: 3px 0 16px;
+}
+
+.alert {
+ padding: 10px;
+ margin-bottom: 20px;
+ border-radius: 2px;
+}
+
+.alert--success {
+ border: solid 1px #1ec00e;
+ background: #cbf1b8;
+ color: #006900;
+}
+
+.alert--error {
+ background: #FFEBEE;
+ color: #B71C1C;
+}
+
+.userBox {
+ padding: 10px 0 20px;
+ letter-spacing: 0.1px;
+}
+
+.userBox a {
+ color: black;
+ font-weight: bold;
+ cursor: pointer;
+ margin: 0px;
+ margin-left: 4px;
+ padding-bottom: 2px;
+ border-bottom: solid 1px black;
+}
+
+.attention {
+ display: inline-block;
+ width: 15px;
+ height: 15px;
+ background: #B71C1C;
+ color: #FFEBEE;
+ font-weight: bolder;
+ padding: 4px;
+ vertical-align: middle;
+ border-radius: 20px;
+ box-sizing: border-box;
+ font-size: 9px;
+ line-height: 7px;
+ text-align: center;
+ margin-right: 5px;
+}
+
+.action {
+ margin-top: 15px;
+}
diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js
index 5742adf75..27fa8890f 100644
--- a/client/coral-framework/constants/auth.js
+++ b/client/coral-framework/constants/auth.js
@@ -4,6 +4,9 @@ export const CLEAN_STATE = 'CLEAN_STATE';
export const SHOW_SIGNIN_DIALOG = 'SHOW_SIGNIN_DIALOG';
export const HIDE_SIGNIN_DIALOG = 'HIDE_SIGNIN_DIALOG';
+export const SHOW_CREATEDISPLAYNAME_DIALOG = 'SHOW_CREATEDISPLAYNAME_DIALOG';
+export const HIDE_CREATEDISPLAYNAME_DIALOG = 'HIDE_CREATEDISPLAYNAME_DIALOG';
+
export const FETCH_SIGNUP_REQUEST = 'FETCH_SIGNUP_REQUEST';
export const FETCH_SIGNUP_FAILURE = 'FETCH_SIGNUP_FAILURE';
export const FETCH_SIGNUP_SUCCESS = 'FETCH_SIGNUP_SUCCESS';
diff --git a/client/coral-framework/containers/ChangeDisplayNameContainer.js b/client/coral-framework/containers/ChangeDisplayNameContainer.js
new file mode 100644
index 000000000..17e681388
--- /dev/null
+++ b/client/coral-framework/containers/ChangeDisplayNameContainer.js
@@ -0,0 +1,140 @@
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+
+import CreateDisplayNameDialog from '../components/CreateDisplayNameDialog';
+
+// import validate from 'coral-framework/helpers/validate';
+// import errorMsj from 'coral-framework/helpers/error';
+import I18n from 'coral-framework/modules/i18n/i18n';
+import translations from '../translations';
+const lang = new I18n(translations);
+
+import {
+ showCreateDisplayNameDialog,
+ hideCreateDisplayNameDialog,
+ invalidForm,
+ validForm,
+ createDisplayName
+} from '../../coral-framework/actions/auth';
+
+class ChangeDisplayNameContainer extends Component {
+ initialState = {
+ formData: {
+ displayName: '',
+ },
+ errors: {},
+ showErrors: false
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = this.initialState;
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmitForm = this.handleSubmitForm.bind(this);
+ this.handleClose = this.handleClose.bind(this);
+ this.addError = this.addError.bind(this);
+ }
+
+ componentDidMount() {
+ const {formData} = this.state;
+ const errors = Object.keys(formData).reduce((map, prop) => {
+ map[prop] = lang.t('createdisplay.requiredField');
+ return map;
+ }, {});
+ this.setState({errors});
+ }
+
+ handleChange(e) {
+ const {name, value} = e.target;
+ this.setState(state => ({
+ ...state,
+ formData: {
+ ...state.formData,
+ [name]: value
+ }
+ }), () => {
+ this.validation(name, value);
+ });
+ }
+
+ addError(name, error) {
+ return this.setState(state => ({
+ errors: {
+ ...state.errors,
+ [name]: error
+ }
+ }));
+ }
+
+ validation(name, value) {
+ const {addError} = this;
+
+ if (!value.length) {
+ addError(name, lang.t('displayName.requiredField'));
+ } else {
+ const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line
+ // Removes Error
+ this.setState(state => ({...state, errors}));
+ }
+ }
+
+ isCompleted() {
+ const {formData} = this.state;
+ return !Object.keys(formData).filter(prop => !formData[prop].length).length;
+ }
+
+ displayErrors(show = true) {
+ this.setState({showErrors: show});
+ }
+
+ handleSubmitForm(e) {
+ e.preventDefault();
+ const {errors} = this.state;
+ const {validForm, invalidForm} = this.props;
+ this.displayErrors();
+ if (this.isCompleted() && !Object.keys(errors).length) {
+ createDisplayName(this.state.formData);
+ validForm();
+ } else {
+ invalidForm(lang.t('signIn.checkTheForm'));
+ }
+ }
+
+ handleClose() {
+ this.props.hideCreateDisplayNameDialog();
+ }
+
+ render() {
+ const {loggedIn, auth, offset, user} = this.props;
+ return (
+
+
+
+ );
+ }
+}
+
+const mapStateToProps = state => ({
+ auth: state.auth.toJS()
+});
+
+const mapDispatchToProps = dispatch => ({
+ createDisplayName: formData => dispatch(createDisplayName(formData)),
+ showCreateDisplayNameDialog: () => dispatch(showCreateDisplayNameDialog()),
+ hideCreateDisplayNameDialog: () => dispatch(hideCreateDisplayNameDialog()),
+ invalidForm: error => dispatch(invalidForm(error)),
+ validForm: () => dispatch(validForm())
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ChangeDisplayNameContainer);
diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js
index 36f8e0764..ff0f72b4a 100644
--- a/client/coral-framework/reducers/auth.js
+++ b/client/coral-framework/reducers/auth.js
@@ -7,6 +7,7 @@ const initialState = Map({
isAdmin: false,
user: null,
showSignInDialog: false,
+ showCreateDisplayNameDialog: false,
view: 'SIGNIN',
error: '',
passwordRequestSuccess: null,
@@ -35,6 +36,13 @@ export default function auth (state = initialState, action) {
passwordRequestSuccess: null,
successSignUp: false
}));
+ case actions.SHOW_CREATEDISPLAYNAME_DIALOG :
+ return state
+ .set('showCreateDisplayNameDialog', true);
+ case actions.HIDE_CREATEDISPLAYNAME_DIALOG :
+ return state.merge(Map({
+ showCreateDisplayNameDialog: false
+ }));
case actions.CHANGE_VIEW :
return state
.set('error', '')
diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json
index 5bf9b40a9..5900aad3e 100644
--- a/client/coral-framework/translations.json
+++ b/client/coral-framework/translations.json
@@ -4,6 +4,14 @@
"successBioUpdate": "Your Bio has been updated",
"contentNotAvailable": "This content is not available",
"suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information",
+ "createdisplay": {
+ "writeyourusername": "Write your username",
+ "yourusername": "Your username is publicly visible on all comments you post. A username is needed before you can post your first comment.",
+ "displayName": "Display Name",
+ "save": "Save",
+ "requiredField": "Required field",
+ "errorCreate": "Error when changing display name"
+ },
"error": {
"email": "Not a valid E-Mail",
"password": "Password must be at least 8 characters",
@@ -26,6 +34,13 @@
"successBioUpdate": "Tu bio fue actualizada",
"contentNotAvailable": "El contenido no se encuentra disponible",
"suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information",
+ "createdisplay": {
+ "writeyourusername": "Escribe tu nombre",
+ "yourusername": "Tu nombre es visible publicamente en todos los comentarios que publiques. Es necesario tener un nombre de usuario antes de poder publicar tu primer comentario.",
+ "displayName": "Nombre",
+ "save": "Guardar",
+ "requiredField": "Campo necesario"
+ },
"error": {
"email": "No es un email válido",
"password": "La contraseña debe tener por lo menos 8 caracteres",
diff --git a/services/users.js b/services/users.js
index 0bf22e9e0..d1e7dce05 100644
--- a/services/users.js
+++ b/services/users.js
@@ -156,6 +156,18 @@ module.exports = class UsersService {
});
}
+ static changeDisplayName(id, displayName) {
+ return UsersService.isValidDisplayName(displayName)
+ .then((displayName) => { // displayName is valid
+ return UserModel.update({id}, {
+ $inc: {__v: 1},
+ $set: {
+ displayName: displayName
+ }
+ });
+ });
+ }
+
/**
* Creates local users.
* @param {Array} users Users to create