-
+
{lang.t('myProfile')}
Configure Stream
- {
- commentId &&
-
- {lang.t('showAllComments')}
-
- }
+ {commentId &&
+
+ {lang.t('showAllComments')}
+ }
+
- { 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
?
:
{asset.settings.closedMessage}
}
- {!loggedIn &&
-
- Sign in to comment
- }
{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)
+ ?
+ : ;
+ })}
(dispatch) => {
+export const showSignInDialog = () => (dispatch, getState) => {
const signInPopUp = window.open(
'/embed/stream/login',
'Login',
@@ -21,6 +21,10 @@ export const showSignInDialog = () => (dispatch) => {
let loaded = false;
signInPopUp.onload = () => {
loaded = true;
+
+ // Fire some actions inside the popups reducer, to initialize required state.
+ const required = getState().asset.toJS().settings.requireEmailConfirmation;
+ signInPopUp.coralStore.dispatch(setRequireEmailVerification(required));
};
// Use `onunload` instead of `onbeforeunload` which is not supported in IOS Safari.
@@ -96,6 +100,11 @@ export const cleanState = () => ({
type: actions.CLEAN_STATE
});
+export const setRequireEmailVerification = (required) => ({
+ type: actions.SET_REQUIRE_EMAIL_VERIFICATION,
+ required,
+});
+
// Sign In Actions
const signInRequest = () => ({
@@ -121,26 +130,31 @@ export const handleAuthToken = (token) => (dispatch) => {
// SIGN IN
//==============================================================================
-export const fetchSignIn = (formData) => (dispatch) => {
- dispatch(signInRequest());
- return coralApi('/auth/local', {method: 'POST', body: formData})
- .then(({token}) => {
- dispatch(handleAuthToken(token));
- dispatch(hideSignInDialog());
- })
- .catch((error) => {
- if (error.metadata) {
+export const fetchSignIn = (formData) => {
+ return (dispatch) => {
+ dispatch(signInRequest());
- // the user might not have a valid email. prompt the user user re-request the confirmation email
- dispatch(
- signInFailure(lang.t('error.emailNotVerified', error.metadata))
- );
- } else {
+ return coralApi('/auth/local', {method: 'POST', body: formData})
+ .then(({token}) => {
+ if (!bowser.safari && !bowser.ios) {
+ dispatch(handleAuthToken(token));
+ }
+ dispatch(hideSignInDialog());
+ })
+ .catch((error) => {
+ if (error.metadata) {
- // invalid credentials
- dispatch(signInFailure(lang.t('error.emailPasswordError')));
- }
- });
+ // the user might not have a valid email. prompt the user user re-request the confirmation email
+ dispatch(
+ signInFailure(lang.t('error.emailNotVerified', error.metadata))
+ );
+ } else {
+
+ // invalid credentials
+ dispatch(signInFailure(lang.t('error.emailPasswordError')));
+ }
+ });
+ };
};
//==============================================================================
@@ -187,7 +201,7 @@ export const fetchSignUpFacebook = () => (dispatch) => {
);
};
-export const facebookCallback = (err, data) => (dispatch, getState) => {
+export const facebookCallback = (err, data) => (dispatch) => {
if (err) {
dispatch(signInFacebookFailure(err));
return;
@@ -196,10 +210,6 @@ export const facebookCallback = (err, data) => (dispatch, getState) => {
dispatch(handleAuthToken(data.token));
dispatch(signInFacebookSuccess(data.user));
dispatch(hideSignInDialog());
- const {user: {canEditName, status}} = getState().auth.toJS();
- if (canEditName && status !== 'BANNED') {
- dispatch(showCreateUsernameDialog());
- }
} catch (err) {
dispatch(signInFacebookFailure(err));
return;
@@ -269,7 +279,9 @@ export const fetchForgotPassword = (email) => (dispatch) => {
export const logout = () => (dispatch) => {
return coralApi('/auth', {method: 'DELETE'}).then(() => {
- Storage.removeItem('token');
+ if (!bowser.safari && !bowser.ios) {
+ Storage.removeItem('token');
+ }
dispatch({type: actions.LOGOUT});
});
};
@@ -292,11 +304,18 @@ export const checkLogin = () => (dispatch) => {
coralApi('/auth')
.then((result) => {
if (!result.user) {
- Storage.removeItem('token');
+ if (!bowser.safari && !bowser.ios) {
+ Storage.removeItem('token');
+ }
throw new Error('Not logged in');
}
dispatch(checkLoginSuccess(result.user));
+
+ // Display create username dialog if necessary.
+ if (result.user.canEditName && result.user.status !== 'BANNED') {
+ dispatch(showCreateUsernameDialog());
+ }
})
.catch((error) => {
console.error(error);
diff --git a/client/coral-framework/actions/user.js b/client/coral-framework/actions/user.js
index 910fe0865..95d4116a6 100644
--- a/client/coral-framework/actions/user.js
+++ b/client/coral-framework/actions/user.js
@@ -1,5 +1,5 @@
import {addNotification} from '../actions/notification';
-import coralApi from '../helpers/response';
+import coralApi from '../helpers/request';
import * as actions from '../constants/auth';
import I18n from 'coral-framework/modules/i18n/i18n';
diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js
index 24599ac50..a9ac52739 100644
--- a/client/coral-framework/constants/auth.js
+++ b/client/coral-framework/constants/auth.js
@@ -46,3 +46,6 @@ export const VERIFY_EMAIL_REQUEST = 'VERIFY_EMAIL_REQUEST';
export const VERIFY_EMAIL_SUCCESS = 'VERIFY_EMAIL_SUCCESS';
export const VERIFY_EMAIL_FAILURE = 'VERIFY_EMAIL_FAILURE';
export const UPDATE_USERNAME = 'UPDATE_USERNAME';
+
+export const SET_REQUIRE_EMAIL_VERIFICATION = 'SET_REQUIRE_EMAIL_VERIFICATION';
+
diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/request.js
similarity index 75%
rename from client/coral-framework/helpers/response.js
rename to client/coral-framework/helpers/request.js
index 794a73e30..0e15003e1 100644
--- a/client/coral-framework/helpers/response.js
+++ b/client/coral-framework/helpers/request.js
@@ -1,22 +1,26 @@
+import bowser from 'bowser';
import * as Storage from './storage';
+import merge from 'lodash/merge';
const buildOptions = (inputOptions = {}) => {
const defaultOptions = {
method: 'GET',
headers: {
Accept: 'application/json',
- Authorization: `Bearer ${Storage.getItem('token')}`,
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
},
credentials: 'same-origin'
};
- let options = Object.assign({}, defaultOptions, inputOptions);
- options.headers = Object.assign(
- {},
- defaultOptions.headers,
- inputOptions.headers
- );
+ let options = merge({}, defaultOptions, inputOptions);
+
+ if (!bowser.safari && !bowser.ios) {
+ let authorization = Storage.getItem('token');
+
+ if (authorization) {
+ options.headers.Authorization = `Bearer ${authorization}`;
+ }
+ }
if (options.method.toLowerCase() !== 'get') {
options.body = JSON.stringify(options.body);
diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js
index 8fa4ed75b..7a1744d59 100644
--- a/client/coral-framework/reducers/auth.js
+++ b/client/coral-framework/reducers/auth.js
@@ -16,7 +16,8 @@ const initialState = Map({
emailVerificationLoading: false,
emailVerificationSuccess: false,
successSignUp: false,
- fromSignUp: false
+ fromSignUp: false,
+ requireEmailConfirmation: false,
});
const purge = (user) => {
@@ -142,6 +143,9 @@ export default function auth (state = initialState, action) {
return state
.set('emailVerificationSuccess', true)
.set('emailVerificationLoading', false);
+ case actions.SET_REQUIRE_EMAIL_VERIFICATION:
+ return state
+ .set('requireEmailConfirmation', action.required);
default :
return state;
}
diff --git a/client/coral-framework/services/transport.js b/client/coral-framework/services/transport.js
index e421ca3da..0c430a021 100644
--- a/client/coral-framework/services/transport.js
+++ b/client/coral-framework/services/transport.js
@@ -1,5 +1,6 @@
import {createNetworkInterface} from 'apollo-client';
import * as Storage from '../helpers/storage';
+import bowser from 'bowser';
//==============================================================================
// NETWORK INTERFACE
@@ -21,7 +22,11 @@ networkInterface.use([{
if (!req.options.headers) {
req.options.headers = {}; // Create the header object if needed.
}
- req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`;
+
+ if (!bowser.safari && !bowser.ios) {
+ req.options.headers['authorization'] = `Bearer ${Storage.getItem('token')}`;
+ }
+
next();
}
}]);
diff --git a/client/coral-sign-in/components/FakeComment.js b/client/coral-sign-in/components/FakeComment.js
deleted file mode 100644
index 37630383d..000000000
--- a/client/coral-sign-in/components/FakeComment.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import styles from 'coral-embed-stream/src/components/Comment.css';
-
-import AuthorName from 'coral-plugin-author-name/AuthorName';
-import Content from 'coral-plugin-commentcontent/CommentContent';
-import PubDate from 'coral-plugin-pubdate/PubDate';
-import {ReplyButton} from 'coral-plugin-replies';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from '../translations';
-
-const lang = new I18n(translations);
-
-class FakeComment extends React.Component {
- constructor (props) {
- super(props);
- }
-
- render () {
- const {username, created_at, body} = this.props;
-
- return (
-
-
-
-
-
-
-
-
- {lang.t('like')}
- thumb_up
-
-
-
{}}
- parentCommentId={'commentID'}
- currentUserId={{}}
- banned={false}
- />
-
-
-
-
- {lang.t('permalink.permalink')}
- link
-
-
-
-
- {lang.t('report')}
- flag
-
-
-
-
- );
- }
-}
-
-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}) => (
-
-);
-
-export default UserBox;
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/package.json b/package.json
index 84d0fdf52..152871fae 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "talk",
- "version": "1.6.0",
+ "version": "1.7.0",
"description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net",
"main": "app.js",
"scripts": {
@@ -54,10 +54,13 @@
"app-module-path": "^2.2.0",
"bcrypt": "^1.0.2",
"body-parser": "^1.17.1",
+ "bowser": "^1.7.0",
"cli-table": "^0.3.1",
"colors": "^1.1.2",
"commander": "^2.9.0",
+ "compression": "^1.6.2",
"connect-redis": "^3.1.0",
+ "cookie-parser": "^1.4.3",
"cross-spawn": "^5.1.0",
"csurf": "^1.9.0",
"dataloader": "^1.3.0",
diff --git a/plugins.default.json b/plugins.default.json
index b3443af48..3f64a383b 100644
--- a/plugins.default.json
+++ b/plugins.default.json
@@ -1,9 +1,13 @@
{
"server": [
"coral-plugin-respect",
- "coral-plugin-facebook-auth"
+ "coral-plugin-like",
+ "coral-plugin-facebook-auth",
+ "coral-plugin-auth"
],
"client": [
- "coral-plugin-respect"
+ "coral-plugin-respect",
+ "coral-plugin-like",
+ "coral-plugin-auth"
]
}
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/client/coral-sign-in/containers/ChangeUsernameContainer.js b/plugins/coral-plugin-auth/client/components/ChangeUsername.js
similarity index 52%
rename from client/coral-sign-in/containers/ChangeUsernameContainer.js
rename to plugins/coral-plugin-auth/client/components/ChangeUsername.js
index cebd9605b..ee02b2e17 100644
--- a/client/coral-sign-in/containers/ChangeUsernameContainer.js
+++ b/plugins/coral-plugin-auth/client/components/ChangeUsername.js
@@ -1,13 +1,12 @@
-import React, {Component} from 'react';
+import React 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 {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 {
@@ -16,50 +15,57 @@ import {
invalidForm,
validForm,
createUsername
-} from '../../coral-framework/actions/auth';
-
-class ChangeUsernameContainer extends Component {
- initialState = {
- formData: {
- username: '',
- },
- errors: {},
- showErrors: false
- };
+} from 'coral-framework/actions/auth';
+class ChangeUsernameContainer extends React.Component {
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,
+ this.state = {
formData: {
- ...state.formData,
- [name]: value
- }
- }), () => {
- this.validation(name, value);
- });
+ username: (props.auth.user && props.auth.user.username) || ''
+ },
+ errors: {},
+ showErrors: false
+ };
}
- addError(name, error) {
+ componentWillReceiveProps(next) {
+ if (!this.props.auth.showCreateUsernameDialog && next.auth.showCreateUsernameDialog) {
+ this.setState({
+ formData: {
+ username: (this.props.auth.user && this.props.auth.user.username) || '',
+ },
+ });
+ }
+ }
+
+ 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) {
+ validation = (name, value) => {
const {addError} = this;
if (!value.length) {
@@ -67,37 +73,37 @@ class ChangeUsernameContainer extends Component {
} else if (!validate[name](value)) {
addError(name, errorMsj[name]);
} else {
- const { [name]: prop, ...errors } = this.state.errors; // eslint-disable-line
+ const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line
// Removes Error
this.setState((state) => ({...state, errors}));
}
- }
+ };
- isCompleted() {
+ isCompleted = () => {
const {formData} = this.state;
return !Object.keys(formData).filter((prop) => !formData[prop].length).length;
- }
+ };
- displayErrors(show = true) {
+ displayErrors = (show = true) => {
this.setState({showErrors: show});
- }
+ };
- handleSubmitUsername(e) {
+ 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);
+ this.props.createUsername(this.props.auth.user.id, this.state.formData);
validForm();
} else {
invalidForm(lang.t('createdisplay.checkTheForm'));
}
- }
+ };
- handleClose() {
+ handleClose = () => {
this.props.hideCreateUsernameDialog();
- }
+ };
render() {
const {loggedIn, auth} = this.props;
@@ -117,19 +123,22 @@ class ChangeUsernameContainer extends Component {
}
}
-const mapStateToProps = (state) => ({
- auth: state.auth.toJS()
+const mapStateToProps = ({auth}) => ({
+ auth: 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())
-});
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators(
+ {
+ createUsername,
+ showCreateUsernameDialog,
+ hideCreateUsernameDialog,
+ invalidForm,
+ validForm
+ },
+ dispatch
+ );
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(ChangeUsernameContainer);
+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} }
+
- );
-};
+);
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..349e7d0a4
--- /dev/null
+++ b/plugins/coral-plugin-auth/client/components/FakeComment.js
@@ -0,0 +1,73 @@
+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}) => (
+
+
+
+
+
+
+
+
+
+ {lang.t('like')}
+
+
+ thumb_up
+
+
+
+
{}}
+ parentCommentId={'commentID'}
+ currentUserId={{}}
+ banned={false}
+ />
+
+
+
+
+
+ {lang.t('permalink.permalink')}
+
+
+ link
+
+
+
+
+
+
+ {lang.t('report')}
+
+
+ flag
+
+
+
+
+
+);
+
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..8bd6b27a6 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 {
{lang.t('signIn.email')}
this.emailInput = input}
+ ref={(input) => (this.emailInput = input)}
type="text"
style={{fontSize: 16}}
id="email"
- name="email" />
+ name="email"
+ />
-
+
{lang.t('signIn.recoverPassword')}
- {
- passwordRequestSuccess
- ? {passwordRequestSuccess}
- : null
- }
- {
- passwordRequestFailure
- ? {passwordRequestFailure}
- : null
- }
+ {passwordRequestSuccess
+ ?
+ {passwordRequestSuccess}
+
+ : null}
+ {passwordRequestFailure
+ ?
+ {passwordRequestFailure}
+
+ : null}
);
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..881233c83
--- /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
+ ?
+ Sign in to comment
+
+ : 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 58%
rename from client/coral-sign-in/containers/SignInContainer.js
rename to plugins/coral-plugin-auth/client/components/SignInContainer.js
index de1d2086e..9ef868f1d 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,7 +61,7 @@ class SignInContainer extends Component {
window.removeEventListener('storage', this.handleAuth);
}
- handleAuth(e) {
+ handleAuth = (e) => {
// Listening to FB changes
// FB localStorage key is 'auth'
@@ -81,9 +71,9 @@ 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) => ({
@@ -97,14 +87,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(
@@ -115,21 +105,21 @@ class SignInContainer extends Component {
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) {
+ 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;
@@ -147,18 +137,18 @@ class SignInContainer extends Component {
// Removes Error
this.setState((state) => ({...state, errors}));
}
- }
+ };
- isCompleted() {
+ isCompleted = () => {
const {formData} = this.state;
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,30 +159,28 @@ 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;
+ const {auth} = this.props;
+ const {requireEmailConfirmation, emailVerificationLoading, emailVerificationSuccess} = auth;
return (
-
-
-
+
);
}
}
@@ -201,20 +189,23 @@ 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,
+ 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
?
@@ -70,23 +73,29 @@ const SignInContent = ({
onChange={handleChange}
/>
- {
- !auth.isLoading ?
-
- {lang.t('signIn.signIn')}
-
- :
-
- }
+ {!auth.isLoading
+ ?
+ {lang.t('signIn.signIn')}
+
+ : }
-
- }
+ }
-
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 60%
rename from client/coral-sign-in/components/SignUpContent.js
rename to plugins/coral-plugin-auth/client/components/SignUpContent.js
index 921f045be..2c0cf1f31 100644
--- a/client/coral-sign-in/components/SignUpContent.js
+++ b/plugins/coral-plugin-auth/client/components/SignUpContent.js
@@ -1,39 +1,26 @@
-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 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 (props) {
- super(props);
- this.successfulSignup = false;
+ componentWillReceiveProps(next) {
+ if (
+ !this.props.emailVerificationEnabled &&
+ !this.props.auth.successSignUp &&
+ next.auth.successSignUp
+ ) {
+ setTimeout(() => {
+ this.props.changeView('SIGNIN');
+ }, 2000);
+ }
}
- 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,18 +30,8 @@ class SignUpContent extends React.Component {
showErrors,
changeView,
handleSignUp,
- 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) {
- setTimeout(() => {
- changeView('SIGNIN');
- }, 1000);
- this.successfulSignup = true;
- }
+ fetchSignUpFacebook
+ } = this.props;
return (
@@ -64,8 +41,8 @@ class SignUpContent extends React.Component {
- { auth.error &&
{auth.error} }
- { beforeSignup &&
+ {auth.error &&
{auth.error} }
+ {!auth.successSignUp &&
@@ -109,7 +86,10 @@ class SignUpContent extends React.Component {
onChange={handleChange}
minLength="8"
/>
- { errors.password && Password must be at least 8 characters. }
+ {errors.password &&
+
+ {' '}Password must be at least 8 characters.{' '}
+ }
-
+
{lang.t('signIn.signUp')}
- { auth.isLoading && }
+ {auth.isLoading && }
-
- }
- {
- successfulSignup &&
+
}
+ {auth.successSignUp &&
- {
- emailVerificationEnabled &&
-
{lang.t('signIn.verifyEmail')} {lang.t('signIn.verifyEmail2')}
- }
-
- }
+ {emailVerificationEnabled &&
+
+ {lang.t('signIn.verifyEmail')}
+
+
+ {lang.t('signIn.verifyEmail2')}
+
}
+
}