diff --git a/.gitignore b/.gitignore index f0c3b2a4b..11e738d57 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ coverage/ plugins.json plugins/* !plugins/coral-plugin-facebook-auth +!plugins/coral-plugin-auth !plugins/coral-plugin-respect !plugins/coral-plugin-offtopic !plugins/coral-plugin-like diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 29971e294..d5a1eeea1 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,6 +1,6 @@ import * as actions from '../constants/auth'; -import * as Storage from 'coral-framework/helpers/storage'; import coralApi from 'coral-framework/helpers/response'; +import * as Storage from 'coral-framework/helpers/storage'; import {handleAuthToken} from 'coral-framework/actions/auth'; //============================================================================== @@ -52,7 +52,9 @@ const forgotPassowordFailure = () => ({ export const requestPasswordReset = (email) => (dispatch) => { dispatch(forgotPassowordRequest(email)); - return coralApi('/account/password/reset', {method: 'POST', body: {email}}) + const redirectUri = location.href; + + return coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}}) .then(() => dispatch(forgotPassowordSuccess())) .catch((error) => dispatch(forgotPassowordFailure(error))); }; diff --git a/client/coral-embed-stream/src/AppRouter.js b/client/coral-embed-stream/src/AppRouter.js index e2553889c..6ee63caab 100644 --- a/client/coral-embed-stream/src/AppRouter.js +++ b/client/coral-embed-stream/src/AppRouter.js @@ -2,12 +2,12 @@ import React from 'react'; import {Router, Route, browserHistory} from 'react-router'; import Embed from './containers/Embed'; -import SignInContainer from 'coral-sign-in/containers/SignInContainer'; +import {LoginContainer} from 'coral-sign-in/containers/LoginContainer'; const routes = (
- - + +
); diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 5f27293d7..7d135d7f0 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,74 +1,68 @@ import React from 'react'; +const lang = new I18n(translations); +import Stream from '../containers/Stream'; +import Slot from 'coral-framework/components/Slot'; +import {can} from 'coral-framework/services/perms'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from 'coral-framework/translations'; -import {can} from 'coral-framework/services/perms'; -const lang = new I18n(translations); - import {TabBar, Tab, TabContent, Button} from 'coral-ui'; - -import Stream from '../containers/Stream'; import Count from 'coral-plugin-comment-count/CommentCount'; -import UserBox from 'coral-sign-in/components/UserBox'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; -import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; +import ConfigureStreamContainer + from 'coral-configure/containers/ConfigureStreamContainer'; export default class Embed extends React.Component { changeTab = (tab) => { - switch(tab) { + switch (tab) { case 0: this.props.setActiveTab('stream'); break; case 1: this.props.setActiveTab('profile'); - // TODO: move data fetching to profile container. + // TODO: move data fetching to profile container. this.props.data.refetch(); break; case 2: this.props.setActiveTab('config'); - // TODO: move data fetching to config container. + // TODO: move data fetching to config container. this.props.data.refetch(); break; } - } + }; handleShowProfile = () => this.props.setActiveTab('profile'); - render () { - const {activeTab, logout, viewAllComments, commentId} = this.props; + render() { + const {activeTab, viewAllComments, commentId} = this.props; const {asset: {totalCommentCount}} = this.props.root; - const {loggedIn, user} = this.props.auth; - - const userBox = ; + const {user} = this.props.auth; return (
- + {lang.t('myProfile')} Configure Stream - { - commentId && - - } + {commentId && + } + - { loggedIn ? userBox : null } - { loggedIn ? userBox : null }
@@ -81,5 +75,5 @@ Embed.propTypes = { data: React.PropTypes.shape({ loading: React.PropTypes.bool, error: React.PropTypes.object - }).isRequired, + }).isRequired }; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index be47b5c90..53f716954 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -1,22 +1,18 @@ import React, {PropTypes} from 'react'; - -import {Button} from 'coral-ui'; import LoadMore from './LoadMore'; import NewCount from './NewCount'; + import Comment from '../containers/Comment'; +import SuspendedAccount from './SuspendedAccount'; +import Slot from 'coral-framework/components/Slot'; import InfoBox from 'coral-plugin-infobox/InfoBox'; +import {can} from 'coral-framework/services/perms'; +import I18n from 'coral-framework/modules/i18n/i18n'; import {ModerationLink} from 'coral-plugin-moderation'; +import translations from 'coral-framework/translations'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; import QuestionBox from 'coral-plugin-questionbox/QuestionBox'; import IgnoredCommentTombstone from './IgnoredCommentTombstone'; -import SuspendedAccount from './SuspendedAccount'; -import RestrictedMessageBox - from 'coral-framework/components/RestrictedMessageBox'; -import {can} from 'coral-framework/services/perms'; -import ChangeUsernameContainer - from 'coral-sign-in/containers/ChangeUsernameContainer'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from 'coral-framework/translations'; const lang = new I18n(translations); @@ -55,7 +51,10 @@ class Stream extends React.Component { : comment; const banned = user && user.status === 'BANNED'; - const temporarilySuspended = user && user.suspension.until && new Date(user.suspension.until) > new Date(); + const temporarilySuspended = + user && + user.suspension.until && + new Date(user.suspension.until) > new Date(); const hasOlderComments = !!(asset && asset.lastComment && @@ -66,10 +65,15 @@ class Stream extends React.Component { ? asset.comments[0].created_at : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); const commentIsIgnored = (comment) => { - return me && me.ignoredUsers && me.ignoredUsers.find((u) => u.id === comment.user.id); + return ( + me && + me.ignoredUsers && + me.ignoredUsers.find((u) => u.id === comment.user.id) + ); }; return (
+ {open ?
- {!banned && temporarilySuspended && + {!banned && + temporarilySuspended && - { - lang.t('temporarilySuspended', - this.props.root.settings.organizationName, - lang.timeago(user.suspension.until), - ) - } - - } + {lang.t( + 'temporarilySuspended', + this.props.root.settings.organizationName, + lang.timeago(user.suspension.until) + )} + } {banned && - } - {loggedIn && !banned && !temporarilySuspended && + />} + {loggedIn && + !banned && + !temporarilySuspended && - } + addNotification={this.props.addNotification} + postComment={this.props.postComment} + appendItemArray={this.props.appendItemArray} + updateItem={this.props.updateItem} + setCommentCountCache={this.props.setCommentCountCache} + commentCountCache={commentCountCache} + assetId={asset.id} + premod={asset.settings.moderation} + isReply={false} + authorId={user.id} + charCountEnable={asset.settings.charCountEnable} + maxCharCount={asset.settings.charCount} + />}
:

{asset.settings.closedMessage}

} - {!loggedIn && - } {loggedIn && - user && - } - {loggedIn && } + } {/* the highlightedComment is isolated after the user followed a permalink */} {highlightedComment @@ -163,41 +159,38 @@ class Stream extends React.Component { setCommentCountCache={this.props.setCommentCountCache} />
- {comments.map( - (comment) => { - return (commentIsIgnored(comment) - ? - : - ); - } - )} + {comments.map((comment) => { + return commentIsIgnored(comment) + ? + : ; + })}
-
- - - -
-
- -
- {}} - parentCommentId={'commentID'} - currentUserId={{}} - banned={false} - /> -
-
-
- -
-
- -
-
-
- ); - } -} - -export default FakeComment; diff --git a/client/coral-sign-in/components/UserBox.js b/client/coral-sign-in/components/UserBox.js deleted file mode 100644 index a44c3d4fb..000000000 --- a/client/coral-sign-in/components/UserBox.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import styles from './styles.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; -const lang = new I18n(translations); - -const UserBox = ({className, user, onLogout, onShowProfile}) => ( -
- {lang.t('signIn.loggedInAs')} - {user.username}. {lang.t('signIn.notYou')} - {lang.t('signIn.logout')} -
-); - -export default UserBox; diff --git a/client/coral-sign-in/containers/ChangeUsernameContainer.js b/client/coral-sign-in/containers/ChangeUsernameContainer.js deleted file mode 100644 index cebd9605b..000000000 --- a/client/coral-sign-in/containers/ChangeUsernameContainer.js +++ /dev/null @@ -1,135 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; - -import validate from 'coral-framework/helpers/validate'; -import errorMsj from 'coral-framework/helpers/error'; - -import CreateUsernameDialog from '../components/CreateUsernameDialog'; - -import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; -const lang = new I18n(translations); - -import { - showCreateUsernameDialog, - hideCreateUsernameDialog, - invalidForm, - validForm, - createUsername -} from '../../coral-framework/actions/auth'; - -class ChangeUsernameContainer extends Component { - initialState = { - formData: { - username: '', - }, - errors: {}, - showErrors: false - }; - - constructor(props) { - super(props); - this.initialState.formData.username = props.user.username; - this.state = this.initialState; - this.handleChange = this.handleChange.bind(this); - this.handleSubmitUsername = this.handleSubmitUsername.bind(this); - this.handleClose = this.handleClose.bind(this); - this.addError = this.addError.bind(this); - } - - 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('createdisplay.requiredField')); - } else if (!validate[name](value)) { - addError(name, errorMsj[name]); - } 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}); - } - - handleSubmitUsername(e) { - e.preventDefault(); - const {errors} = this.state; - const {validForm, invalidForm} = this.props; - this.displayErrors(); - if (this.isCompleted() && !Object.keys(errors).length) { - this.props.createUsername(this.props.user.id, this.state.formData); - validForm(); - } else { - invalidForm(lang.t('createdisplay.checkTheForm')); - } - } - - handleClose() { - this.props.hideCreateUsernameDialog(); - } - - render() { - const {loggedIn, auth} = this.props; - return ( -
- -
- ); - } -} - -const mapStateToProps = (state) => ({ - auth: state.auth.toJS() -}); - -const mapDispatchToProps = (dispatch) => ({ - createUsername: (userid, formData) => dispatch(createUsername(userid, formData)), - showCreateUsernameDialog: () => dispatch(showCreateUsernameDialog()), - hideCreateUsernameDialog: () => dispatch(hideCreateUsernameDialog()), - invalidForm: (error) => dispatch(invalidForm(error)), - validForm: () => dispatch(validForm()) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ChangeUsernameContainer); diff --git a/client/coral-sign-in/containers/LoginContainer.js b/client/coral-sign-in/containers/LoginContainer.js new file mode 100644 index 000000000..8d19e5198 --- /dev/null +++ b/client/coral-sign-in/containers/LoginContainer.js @@ -0,0 +1,6 @@ +import React from 'react'; +import Slot from 'coral-framework/components/Slot'; + +export const LoginContainer = () => ( + +); diff --git a/plugins/coral-plugin-auth/client/.babelrc b/plugins/coral-plugin-auth/client/.babelrc new file mode 100644 index 000000000..60be246eb --- /dev/null +++ b/plugins/coral-plugin-auth/client/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/.eslintrc.json b/plugins/coral-plugin-auth/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/coral-plugin-auth/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/coral-plugin-auth/client/components/ChangeUsername.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js new file mode 100644 index 000000000..9b0cbb1a6 --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js @@ -0,0 +1,132 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import errorMsj from 'coral-framework/helpers/error'; +import validate from 'coral-framework/helpers/validate'; +import CreateUsernameDialog from './CreateUsernameDialog'; + +const lang = new I18n(translations); + +import { + showCreateUsernameDialog, + hideCreateUsernameDialog, + invalidForm, + validForm, + createUsername +} from 'coral-framework/actions/auth'; + +class ChangeUsernameContainer extends React.Component { + constructor(props) { + super(props); + + this.state = { + formData: { + username: props.user.username + }, + errors: {}, + showErrors: false + }; + } + + 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('createdisplay.requiredField')); + } else if (!validate[name](value)) { + addError(name, errorMsj[name]); + } 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}); + }; + + handleSubmitUsername = e => { + e.preventDefault(); + const {errors} = this.state; + const {validForm, invalidForm} = this.props; + this.displayErrors(); + if (this.isCompleted() && !Object.keys(errors).length) { + this.props.createUsername(this.props.user.id, this.state.formData); + validForm(); + } else { + invalidForm(lang.t('createdisplay.checkTheForm')); + } + }; + + handleClose = () => { + this.props.hideCreateUsernameDialog(); + }; + + render() { + const {loggedIn, auth} = this.props; + return ( +
+ +
+ ); + } +} + +const mapStateToProps = ({auth, user}) => ({auth, user}); + +const mapDispatchToProps = dispatch => + bindActionCreators( + { + createUsername, + showCreateUsernameDialog, + hideCreateUsernameDialog, + invalidForm, + validForm + }, + dispatch + ); + +export default connect(mapStateToProps, mapDispatchToProps)( + ChangeUsernameContainer +); diff --git a/client/coral-sign-in/components/CreateUsernameDialog.js b/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js similarity index 61% rename from client/coral-sign-in/components/CreateUsernameDialog.js rename to plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js index b94b47c42..c352552ae 100644 --- a/client/coral-sign-in/components/CreateUsernameDialog.js +++ b/plugins/coral-plugin-auth/client/components/CreateUsernameDialog.js @@ -1,21 +1,26 @@ import React from 'react'; -import TextField from 'coral-ui/components/TextField'; -import Button from 'coral-ui/components/Button'; -import {Dialog, Alert} from 'coral-ui'; -import FakeComment from './FakeComment'; - import styles from './styles.css'; - -import I18n from 'coral-framework/modules/i18n/i18n'; +import {Dialog, Alert, TextField} from 'coral-ui'; +import {FakeComment} from './FakeComment'; import translations from '../translations'; +import Button from 'coral-ui/components/Button'; +import I18n from 'coral-framework/modules/i18n/i18n'; + const lang = new I18n(translations); -const CreateUsernameDialog = ({open, handleClose, formData, handleSubmitUsername, handleChange, ...props}) => { - return ( +const CreateUsernameDialog = ({ + open, + handleClose, + formData, + handleSubmitUsername, + handleChange, + ...props +}) => ( + open={open} + > ×
@@ -24,17 +29,24 @@ const CreateUsernameDialog = ({open, handleClose, formData, handleSubmitUsername
-

{lang.t('createdisplay.yourusername')}

+

+ {lang.t('createdisplay.yourusername')} +

-

{lang.t('createdisplay.ifyoudontchangeyourname')}

- { props.auth.error && {props.auth.error} } +

+ {lang.t('createdisplay.ifyoudontchangeyourname')} +

+ {props.auth.error && {props.auth.error}}
- { props.errors.username && {lang.t('createdisplay.specialCharacters')} } + {props.errors.username && + + {' '}{lang.t('createdisplay.specialCharacters')}{' '} + }
- +
-
+
- ); -}; +); export default CreateUsernameDialog; diff --git a/plugins/coral-plugin-auth/client/components/FakeComment.js b/plugins/coral-plugin-auth/client/components/FakeComment.js new file mode 100644 index 000000000..01cc6863e --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/FakeComment.js @@ -0,0 +1,72 @@ +import React from 'react'; +import translations from '../translations'; +import {ReplyButton} from 'coral-plugin-replies'; +import PubDate from 'coral-plugin-pubdate/PubDate'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import AuthorName from 'coral-plugin-author-name/AuthorName'; +import Content from 'coral-plugin-commentcontent/CommentContent'; +import styles from 'coral-embed-stream/src/components/Comment.css'; + +const lang = new I18n(translations); + +export const FakeComment = ({username, created_at, body}) => ( +
+
+ + + +
+
+ +
+ {}} + parentCommentId={'commentID'} + currentUserId={{}} + banned={false} + /> +
+
+
+ +
+
+ +
+
+
+); \ No newline at end of file diff --git a/client/coral-sign-in/components/ForgotContent.js b/plugins/coral-plugin-auth/client/components/ForgotContent.js similarity index 50% rename from client/coral-sign-in/components/ForgotContent.js rename to plugins/coral-plugin-auth/client/components/ForgotContent.js index 76246fa00..2099330bc 100644 --- a/client/coral-sign-in/components/ForgotContent.js +++ b/plugins/coral-plugin-auth/client/components/ForgotContent.js @@ -1,22 +1,18 @@ import React from 'react'; import styles from './styles.css'; +import translations from '../translations'; import Button from 'coral-ui/components/Button'; import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../translations'; + const lang = new I18n(translations); class ForgotContent extends React.Component { - constructor (props) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - } - - handleSubmit (e) { + handleSubmit = e => { e.preventDefault(); this.props.fetchForgotPassword(this.emailInput.value); - } + }; - render () { + render() { const {changeView, auth} = this.props; const {passwordRequestSuccess, passwordRequestFailure} = auth; @@ -29,29 +25,47 @@ class ForgotContent extends React.Component {
this.emailInput = input} + ref={input => (this.emailInput = input)} type="text" style={{fontSize: 16}} id="email" - name="email" /> + name="email" + />
- - { - passwordRequestSuccess - ?

{passwordRequestSuccess}

- : null - } - { - passwordRequestFailure - ?

{passwordRequestFailure}

- : null - } + {passwordRequestSuccess + ?

+ {passwordRequestSuccess} +

+ : null} + {passwordRequestFailure + ?

+ {passwordRequestFailure} +

+ : null}
- {lang.t('signIn.needAnAccount')} changeView('SIGNUP')}>{lang.t('signIn.register')} - {lang.t('signIn.alreadyHaveAnAccount')} changeView('SIGNIN')}>{lang.t('signIn.signIn')} + + {lang.t('signIn.needAnAccount')} + {' '} + changeView('SIGNUP')}> + {lang.t('signIn.register')} + + + + {lang.t('signIn.alreadyHaveAnAccount')} + {' '} + changeView('SIGNIN')}> + {lang.t('signIn.signIn')} + +
); diff --git a/client/coral-sign-in/components/SignDialog.js b/plugins/coral-plugin-auth/client/components/SignDialog.js similarity index 68% rename from client/coral-sign-in/components/SignDialog.js rename to plugins/coral-plugin-auth/client/components/SignDialog.js index ff2464f7c..7f05889a0 100644 --- a/client/coral-sign-in/components/SignDialog.js +++ b/plugins/coral-plugin-auth/client/components/SignDialog.js @@ -6,12 +6,9 @@ import SignInContent from './SignInContent'; import SignUpContent from './SignUpContent'; import ForgotContent from './ForgotContent'; -const SignDialog = ({open, view, handleClose, ...props}) => ( - - × +const SignDialog = ({open, view, hideSignInDialog, ...props}) => ( + + × {view === 'SIGNIN' && } {view === 'SIGNUP' && } {view === 'FORGOT' && } diff --git a/plugins/coral-plugin-auth/client/components/SignInButton.js b/plugins/coral-plugin-auth/client/components/SignInButton.js new file mode 100644 index 000000000..b54cbfedd --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/SignInButton.js @@ -0,0 +1,24 @@ +import React from 'react'; +import {Button} from 'coral-ui'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {showSignInDialog} from 'coral-framework/actions/auth'; + +const SignInButton = ({loggedIn, showSignInDialog}) => ( +
+ {!loggedIn + ? + : null} +
+); + +const mapStateToProps = ({auth}) => ({ + loggedIn: auth.toJS().loggedIn +}); + +const mapDispatchToProps = dispatch => + bindActionCreators({showSignInDialog}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(SignInButton); diff --git a/client/coral-sign-in/containers/SignInContainer.js b/plugins/coral-plugin-auth/client/components/SignInContainer.js similarity index 56% rename from client/coral-sign-in/containers/SignInContainer.js rename to plugins/coral-plugin-auth/client/components/SignInContainer.js index de1d2086e..cbff000ee 100644 --- a/client/coral-sign-in/containers/SignInContainer.js +++ b/plugins/coral-plugin-auth/client/components/SignInContainer.js @@ -1,11 +1,13 @@ -import React, {Component, PropTypes} from 'react'; +import React from 'react'; import {connect} from 'react-redux'; -import SignDialog from '../components/SignDialog'; -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'; import {pym} from 'coral-framework'; +import SignDialog from './SignDialog'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import errorMsj from 'coral-framework/helpers/error'; +import validate from 'coral-framework/helpers/validate'; + const lang = new I18n(translations); import { @@ -23,34 +25,22 @@ import { checkLogin } from 'coral-framework/actions/auth'; -class SignInContainer extends Component { - initialState = { - formData: { - email: '', - username: '', - password: '', - confirmPassword: '' - }, - emailToBeResent: '', - errors: {}, - showErrors: false - }; - +class SignInContainer extends React.Component { constructor(props) { super(props); - this.state = this.initialState; - this.addError = this.addError.bind(this); - this.handleAuth = this.handleAuth.bind(this); - this.handleSignUp = this.handleSignUp.bind(this); - this.handleSignIn = this.handleSignIn.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleChangeEmail = this.handleChangeEmail.bind(this); - this.handleResendVerification = this.handleResendVerification.bind(this); - } - static propTypes = { - requireEmailConfirmation: PropTypes.bool.isRequired - }; + this.state = { + formData: { + email: '', + username: '', + password: '', + confirmPassword: '' + }, + emailToBeResent: '', + errors: {}, + showErrors: false + }; + } componentWillMount() { this.props.checkLogin(); @@ -71,8 +61,7 @@ class SignInContainer extends Component { window.removeEventListener('storage', this.handleAuth); } - handleAuth(e) { - + handleAuth = e => { // Listening to FB changes // FB localStorage key is 'auth' const authCallback = this.props.facebookCallback; @@ -81,12 +70,12 @@ class SignInContainer extends Component { const {err, data} = JSON.parse(e.newValue); authCallback(err, data); } - } + }; - handleChange(e) { + handleChange = e => { const {name, value} = e.target; this.setState( - (state) => ({ + state => ({ ...state, formData: { ...state.formData, @@ -97,14 +86,14 @@ class SignInContainer extends Component { this.validation(name, value); } ); - } + }; - handleChangeEmail(e) { + handleChangeEmail = e => { const {value} = e.target; this.setState({emailToBeResent: value}); - } + }; - handleResendVerification(e) { + handleResendVerification = e => { e.preventDefault(); this.props .requestConfirmEmail( @@ -113,23 +102,22 @@ class SignInContainer extends Component { ) .then(() => { setTimeout(() => { - // allow success UI to be shown for a second, and then close the modal - this.props.handleClose(); + this.props.hideSignInDialog(); }, 2500); }); - } + }; - addError(name, error) { - return this.setState((state) => ({ + addError = (name, error) => { + return this.setState(state => ({ errors: { ...state.errors, [name]: error } })); - } + }; - validation(name, value) { + validation = (name, value) => { const {addError} = this; const {formData} = this.state; @@ -145,20 +133,20 @@ class SignInContainer extends Component { } else { const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line // Removes Error - this.setState((state) => ({...state, errors})); + this.setState(state => ({...state, errors})); } - } + }; - isCompleted() { + isCompleted = () => { const {formData} = this.state; - return !Object.keys(formData).filter((prop) => !formData[prop].length).length; - } + return !Object.keys(formData).filter(prop => !formData[prop].length).length; + }; - displayErrors(show = true) { + displayErrors = (show = true) => { this.setState({showErrors: show}); - } + }; - handleSignUp(e) { + handleSignUp = e => { e.preventDefault(); const {errors} = this.state; const {fetchSignUp, validForm, invalidForm} = this.props; @@ -169,52 +157,54 @@ class SignInContainer extends Component { } else { invalidForm(lang.t('signIn.checkTheForm')); } - } + }; - handleSignIn(e) { + handleSignIn = e => { e.preventDefault(); this.props.fetchSignIn(this.state.formData); - } + }; render() { const {auth, requireEmailConfirmation} = this.props; const {emailVerificationLoading, emailVerificationSuccess} = auth; return ( -
- -
+ ); } } -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ auth: state.auth.toJS() }); -const mapDispatchToProps = (dispatch) => ({ - checkLogin: () => dispatch(checkLogin()), - facebookCallback: (err, data) => dispatch(facebookCallback(err, data)), - fetchSignUp: (formData, url) => dispatch(fetchSignUp(formData, url)), - fetchSignIn: (formData) => dispatch(fetchSignIn(formData)), - fetchSignInFacebook: () => dispatch(fetchSignInFacebook()), - fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), - fetchForgotPassword: (formData) => dispatch(fetchForgotPassword(formData)), - requestConfirmEmail: (email, url) => - dispatch(requestConfirmEmail(email, url)), - changeView: (view) => dispatch(changeView(view)), - handleClose: () => dispatch(hideSignInDialog()), - invalidForm: (error) => dispatch(invalidForm(error)), - validForm: () => dispatch(validForm()) -}); +const mapDispatchToProps = dispatch => + bindActionCreators( + { + checkLogin, + facebookCallback, + fetchSignUp, + fetchSignUp, + fetchSignIn, + fetchSignInFacebook, + fetchSignUpFacebook, + fetchForgotPassword, + requestConfirmEmail, + changeView, + hideSignInDialog, + invalidForm, + validForm + }, + dispatch + ); export default connect(mapStateToProps, mapDispatchToProps)(SignInContainer); diff --git a/client/coral-sign-in/components/SignInContent.js b/plugins/coral-plugin-auth/client/components/SignInContent.js similarity index 74% rename from client/coral-sign-in/components/SignInContent.js rename to plugins/coral-plugin-auth/client/components/SignInContent.js index 7460efb97..6f0027084 100644 --- a/client/coral-sign-in/components/SignInContent.js +++ b/plugins/coral-plugin-auth/client/components/SignInContent.js @@ -18,17 +18,17 @@ const SignInContent = ({ auth, fetchSignInFacebook }) => { - return (

- {auth.emailVerificationFailure ? lang.t('signIn.emailVerifyCTA') : lang.t('signIn.signIn')} + {auth.emailVerificationFailure + ? lang.t('signIn.emailVerifyCTA') + : lang.t('signIn.signIn')}

- { auth.error && {auth.error} } - { - auth.emailVerificationFailure + {auth.error && {auth.error}} + {auth.emailVerificationFailure ?

{lang.t('signIn.requestNewVerifyEmail')}

- + onChange={handleChangeEmail} + /> + {emailVerificationLoading && } {emailVerificationSuccess && } @@ -70,23 +73,29 @@ const SignInContent = ({ onChange={handleChange} />
- { - !auth.isLoading ? - - : - - } + {!auth.isLoading + ? + : }
-
- } + }
- changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')} + + changeView('FORGOT')}> + {lang.t('signIn.forgotYourPass')} + + {lang.t('signIn.needAnAccount')} - changeView('SIGNUP')} id='coralRegister'> + changeView('SIGNUP')} id="coralRegister"> {lang.t('signIn.register')} diff --git a/client/coral-sign-in/components/SignUpContent.js b/plugins/coral-plugin-auth/client/components/SignUpContent.js similarity index 66% rename from client/coral-sign-in/components/SignUpContent.js rename to plugins/coral-plugin-auth/client/components/SignUpContent.js index 921f045be..5f2dd36ea 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/plugins/coral-plugin-auth/client/components/SignUpContent.js @@ -1,39 +1,21 @@ -import React, {PropTypes} from 'react'; -import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; import styles from './styles.css'; -import I18n from 'coral-framework/modules/i18n/i18n'; +import React, {PropTypes} from 'react'; import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; + const lang = new I18n(translations); class SignUpContent extends React.Component { + constructor() { + super(); - constructor (props) { - super(props); - this.successfulSignup = false; + this.state = { + successfulSignup: false + }; } - static propTypes = { - emailVerificationEnabled: PropTypes.bool.isRequired, - fetchSignUpFacebook: PropTypes.func.isRequired, - changeView: PropTypes.func.isRequired, - handleSignUp: PropTypes.func.isRequired, - showErrors: PropTypes.bool, - errors: PropTypes.shape({ - email: PropTypes.string, - username: PropTypes.string, - password: PropTypes.string, - confirmPassword: PropTypes.string, - }), - formData: PropTypes.shape({ - email: PropTypes.string, - username: PropTypes.string, - password: PropTypes.string, - confirmPassword: PropTypes.string - }) - } - - render () { - + render() { const { handleChange, formData, @@ -43,17 +25,20 @@ class SignUpContent extends React.Component { showErrors, changeView, handleSignUp, - fetchSignUpFacebook} = this.props; + fetchSignUpFacebook + } = this.props; const beforeSignup = !auth.isLoading && !auth.successSignUp; const successfulSignup = !auth.isLoading && auth.successSignUp; // the first time we render a successfulSignup, trigger a timer - if ((this.successfulSignup ^ successfulSignup) && !emailVerificationEnabled) { + if (this.successfulSignup ^ successfulSignup && !emailVerificationEnabled) { setTimeout(() => { changeView('SIGNIN'); }, 1000); - this.successfulSignup = true; + this.setState({ + successfulSignup: true + }); } return ( @@ -64,8 +49,8 @@ class SignUpContent extends React.Component {
- { auth.error && {auth.error} } - { beforeSignup && + {auth.error && {auth.error}} + {beforeSignup &&
- { auth.isLoading && } + {auth.isLoading && }
-
- } - { - successfulSignup && + } + {successfulSignup &&
- { - emailVerificationEnabled && -

{lang.t('signIn.verifyEmail')}

{lang.t('signIn.verifyEmail2')}

- } -
- } + {emailVerificationEnabled && +

+ {lang.t('signIn.verifyEmail')} +
+
+ {lang.t('signIn.verifyEmail2')} +

} + } diff --git a/plugins/coral-plugin-auth/client/components/UserBox.js b/plugins/coral-plugin-auth/client/components/UserBox.js new file mode 100644 index 000000000..a4a0e1c99 --- /dev/null +++ b/plugins/coral-plugin-auth/client/components/UserBox.js @@ -0,0 +1,34 @@ +import React from 'react'; +import styles from './styles.css'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import {showSignInDialog, logout} from 'coral-framework/actions/auth'; +const lang = new I18n(translations); + +const UserBox = ({loggedIn, user, logout, onShowProfile}) => ( +
+ { + loggedIn ? ( +
+ {lang.t('signIn.loggedInAs')} + {user.username}. {lang.t('signIn.notYou')} + logout()}> + {lang.t('signIn.logout')} + +
+ ) : null + } +
+); + +const mapStateToProps = ({auth, user}) => ({ + loggedIn: auth.toJS().loggedIn, + user: user.toJS() +}); + +const mapDispatchToProps = dispatch => + bindActionCreators({logout}, dispatch); + +export default connect(mapStateToProps, mapDispatchToProps)(UserBox); diff --git a/client/coral-sign-in/components/styles.css b/plugins/coral-plugin-auth/client/components/styles.css similarity index 100% rename from client/coral-sign-in/components/styles.css rename to plugins/coral-plugin-auth/client/components/styles.css diff --git a/plugins/coral-plugin-auth/client/index.js b/plugins/coral-plugin-auth/client/index.js new file mode 100644 index 000000000..41fa3adbe --- /dev/null +++ b/plugins/coral-plugin-auth/client/index.js @@ -0,0 +1,11 @@ +import UserBox from './components/UserBox'; +import SignInButton from './components/SignInButton'; +import SignInContainer from './components/SignInContainer'; +import ChangeUserNameContainer from './components/ChangeUsername'; + +export default { + slots: { + stream: [UserBox, SignInButton, ChangeUserNameContainer], + login: [SignInContainer] + } +}; \ No newline at end of file diff --git a/plugins/coral-plugin-auth/client/translations.json b/plugins/coral-plugin-auth/client/translations.json new file mode 100644 index 000000000..7ae84b2ff --- /dev/null +++ b/plugins/coral-plugin-auth/client/translations.json @@ -0,0 +1,102 @@ +{ + "en": { + "signIn": { + "emailVerifyCTA": "Please verify your email address.", + "requestNewVerifyEmail": "Request another email:", + "verifyEmail": "Thank you for creating an account! We sent an email to the address you provided to verify your account.", + "verifyEmail2": "You must verify your account before engaging with the community.", + "notYou": "Not you?", + "loggedInAs": "Logged in as", + "facebookSignIn": "Sign in with Facebook", + "facebookSignUp": "Sign up with Facebook", + "logout": "Logout", + "signIn": "Sign in to join the conversation", + "or": "Or", + "email": "E-mail Address", + "password": "Password", + "forgotYourPass": "Forgot your password?", + "needAnAccount": "Need an account?", + "register": "Register", + "signUp": "Sign Up", + "confirmPassword": "Confirm Password", + "username": "Username", + "alreadyHaveAnAccount": "Already have an account?", + "recoverPassword": "Recover password", + "emailInUse": "Email address already in use", + "emailORusernameInUse": "Email address or Username already in use", + "requiredField": "This field is required", + "passwordsDontMatch": "Passwords don't match.", + "specialCharacters": "Usernames can contain letters, numbers and _ only", + "checkTheForm": "Invalid Form. Please, check the fields" + }, + "createdisplay": { + "writeyourusername": "Edit your username", + "yourusername": "Your username appears on every comment you post.", + "ifyoudontchangeyourname": "If you don't change your username at this step, your Facebook display name will appear alongside of all your comments.", + "username": "Username", + "continue": "Continue with the same Facebook username", + "save": "Save", + "fakecommentdate": "1 minute ago", + "fakecommentbody": "This is an example comment. Readers can share their thoughts and opinions with newsrooms in the comments section.", + "requiredField": "Required field", + "errorCreate": "Error when changing username", + "checkTheForm": "Invalid Form. Please, check the fields", + "specialCharacters": "Usernames can contain letters, numbers and _ only" + }, + "permalink": { + "permalink": "Link" + }, + "report": "Report", + "like": "Like" + }, + "es": { + "signIn": { + "emailVerifyCTA": "Por favor verifique su e-mail.", + "requestNewVerifyEmail": "Enviar otro correo:", + "verifyEmail": "¡Gracias por crear una cuenta! Le enviamos un correo a la dirección que dio para verificar su cuenta.", + "verifyEmail2": "Debe verificarla antes de poder involucrarse en la comunidad.", + "notYou": "¿No eres tu?", + "loggedInAs": "Entraste como", + "facebookSignIn": "Entrar con Facebook", + "facebookSignUp": "Regístrate con Facebook", + "logout": "Salir", + "signIn": "Entrar para Unirte a la Conversación", + "or": "o", + "email": "E-mail", + "password": "Contraseña", + "forgotYourPass": "¿Has olvidado tu contraseña?", + "needAnAccount": "¿Necesitas una cuenta?", + "register": "Regístrate", + "signUp": "Registro", + "confirmPassword": "Confirmar Contraseña", + "username": "Nombre", + "alreadyHaveAnAccount": "¿Ya tienes una cuenta?", + "recoverPassword": "Recuperar contraseña", + "emailInUse": "Este e-mail se encuentra en uso", + "emailORusernameInUse": "Este e-mail ó nombre de usuario se encuentran en uso", + "requiredField": "Este campo es requerido", + "passwordsDontMatch": "Las contraseñas no coinciden", + "specialCharacters": "Los nombres pueden contener letras, números y _", + "checkTheForm": "Formulario Inválido. Por favor, completa los campos" + }, + "createdisplay": { + "writeyourusername": "Edita tu nombre", + "yourusername": "Tu nombre aparece en cada comentario que publiques.", + "ifyoudontchangeyourname": "Si no modificas tu nombre de usuario en este paso, tu nombre de Facebook aparecera al lado de cada comentario que publiques.", + "username": "Nombre", + "continue": "Continuar con nombre de Facebook", + "save": "Guardar", + "fakecommentdate": "hace un minuto", + "fakecommentbody": "Este es un comentario de ejemplo. Las lectoras pueden compartir sus ideas y opiniones con los periodistas en la sección de comentarios.", + "requiredField": "Campo necesario", + "errorCreate": "Hubo un error al cambiar el nombre de usuario", + "checkTheForm": "Formulario Inválido. Por favor, verifica los campos", + "specialCharacters": "Sólo pueden contener letras, números y _" + }, + "permalink": { + "permalink": "Enlace" + }, + "report": "Marcar", + "like": "Me gusta" + } +} diff --git a/plugins/coral-plugin-auth/index.js b/plugins/coral-plugin-auth/index.js new file mode 100644 index 000000000..a09954537 --- /dev/null +++ b/plugins/coral-plugin-auth/index.js @@ -0,0 +1 @@ +module.exports = {}; \ No newline at end of file diff --git a/services/users.js b/services/users.js index 496db4528..4c76f179c 100644 --- a/services/users.js +++ b/services/users.js @@ -566,7 +566,6 @@ module.exports = class UsersService { // endpoint. return; } - let redirectDomain; try { redirectDomain = url.parse(loc).hostname;