diff --git a/src/common/api/user.js b/src/common/api/user.js index 26c7319..0d2b7bd 100644 --- a/src/common/api/user.js +++ b/src/common/api/user.js @@ -6,7 +6,7 @@ export default (apiEngine) => ({ }), login: (user) => apiEngine.post('/api/users/login', { data: user }), logout: () => apiEngine.get('/api/users/logout'), - show: () => apiEngine.get('/api/users/me'), + read: () => apiEngine.get('/api/users/me'), update: (user) => apiEngine.put('/api/users/me', { data: user }), updateAvatarURL: (form) => apiEngine.put('/api/users/me/avatarURL', { data: form, diff --git a/src/common/components/forms/AvatarForm.js b/src/common/components/forms/user/AvatarForm.js similarity index 77% rename from src/common/components/forms/AvatarForm.js rename to src/common/components/forms/user/AvatarForm.js index abb9e63..0d9edb6 100644 --- a/src/common/components/forms/AvatarForm.js +++ b/src/common/components/forms/user/AvatarForm.js @@ -1,12 +1,15 @@ -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; import { Field, reduxForm } from 'redux-form'; -import Image from 'react-bootstrap/lib/Image'; import Button from 'react-bootstrap/lib/Button'; -import configs from '../../../../configs/project/client'; -import firebaseAPI from '../../api/firebase'; -import userAPI from '../../api/user'; -import { pushErrors } from '../../actions/errorActions'; -import { Form, FormField, FormFooter } from '../utils/BsForm'; +import configs from '../../../../../configs/project/client'; +import firebaseAPI from '../../../api/firebase'; +import userAPI from '../../../api/user'; +import { pushErrors } from '../../../actions/errorActions'; +import { setCookies } from '../../../actions/cookieActions'; +import { Form, FormField, FormFooter } from '../../utils/BsForm'; +import RefreshImage from '../../utils/RefreshImage'; +import toRefreshURL from '../../../utils/toRefreshURL'; const initialValues = { storage: 'local', @@ -36,7 +39,9 @@ class AvatarForm extends Component { } _uploadToLocal(formData) { - return userAPI(this.context.store.getState().apiEngine) + let { apiEngine } = this.props; + + return userAPI(apiEngine) .uploadAvatar(formData.avatar[0]) .catch((err) => { return Promise.reject(err); @@ -47,10 +52,12 @@ class AvatarForm extends Component { } _signInFirebase() { - return firebaseAPI(this.context.store.getState().apiEngine) + let { dispatch, apiEngine } = this.props; + + return firebaseAPI(apiEngine) .readToken() .catch((err) => { - this.context.store.dispatch(pushErrors([{ + dispatch(pushErrors([{ title: 'Fail To Read Token', detail: 'Read firebase token fail.', }])); @@ -69,7 +76,7 @@ class AvatarForm extends Component { return firebase.auth() .signInWithCustomToken(json.token) .catch(function(err) { - this.context.store.dispatch(pushErrors([{ + dispatch(pushErrors([{ title: 'Fail To Signin Firebase', detail: 'Signin firebase fail.', }])); @@ -80,15 +87,14 @@ class AvatarForm extends Component { _uploadToFirebase(formData) { let _this = this; - let { store } = this.context; - let userId = JSON.parse(store.getState().cookies.user)._id; + let { user } = this.props; return new Promise((resolve, reject) => { _this.signInFirebase().then(() => { // ref: let storageRef = firebase.storage().ref(); let avatarRef = storageRef.child( - `${process.env.NODE_ENV}/${userId}/avatar.jpg`); + `${process.env.NODE_ENV}/${user._id}/avatar.jpg`); let uploadTask = avatarRef.put(formData.avatar[0]); uploadTask.on('state_changed', function(snapshot) { @@ -114,13 +120,14 @@ class AvatarForm extends Component { } _handleSubmit(formData) { - let { store: { dispatch, getState } } = this.context; + let { dispatch, apiEngine } = this.props; let uploadProcedure; if (formData.storage === 'firebase') { uploadProcedure = this.uploadToFirebase(formData); } else if (formData.storage === 'local') { uploadProcedure = this.uploadToLocal(formData); } + return uploadProcedure .catch((err) => { dispatch(pushErrors([{ @@ -131,7 +138,7 @@ class AvatarForm extends Component { throw err; }) .then((downloadURL) => { - return userAPI(getState().apiEngine) + return userAPI(apiEngine) .updateAvatarURL({ avatarURL: downloadURL, }) @@ -144,11 +151,11 @@ class AvatarForm extends Component { throw err; }) .then((json) => { - let forceUpdate = (downloadURL.indexOf('?') >= 0 ? - '&' : '?') + `forceUpdate=${Math.random()}`; - this.setState({ - avatarURL: downloadURL + forceUpdate, - }); + let newAvatarURL = toRefreshURL(downloadURL); + json.user.avatarURL = newAvatarURL; + dispatch(setCookies({ + user: json.user, + })); this.clearFileField(); }); }); @@ -156,16 +163,17 @@ class AvatarForm extends Component { render() { const { + user, handleSubmit, pristine, submitting, invalid, } = this.props; - let avatarURL = this.state.avatarURL || this.props.avatarURL; + let avatarURL = this.state.avatarURL || user.avatarURL; return (
- {avatarURL && } + {avatarURL && } ({ + apiEngine: apiEngine, + user: (user && JSON.parse(user)) || {}, +}))(AvatarForm)); diff --git a/src/common/components/forms/user/EditForm.js b/src/common/components/forms/user/EditForm.js index 5f59cfb..6a95c03 100644 --- a/src/common/components/forms/user/EditForm.js +++ b/src/common/components/forms/user/EditForm.js @@ -37,7 +37,7 @@ class EditForm extends Component { let { dispatch, apiEngine } = this.props; userAPI(apiEngine) - .show() + .read() .catch((err) => { dispatch(pushErrors(err)); throw err; diff --git a/src/common/components/forms/LoginForm.js b/src/common/components/forms/user/LoginForm.js similarity index 77% rename from src/common/components/forms/LoginForm.js rename to src/common/components/forms/user/LoginForm.js index 05c5066..3a0358b 100644 --- a/src/common/components/forms/LoginForm.js +++ b/src/common/components/forms/user/LoginForm.js @@ -1,13 +1,14 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; -import { Field, reduxForm } from 'redux-form'; +import { Field, reduxForm, SubmissionError } from 'redux-form'; +import Alert from 'react-bootstrap/lib/Alert'; import Button from 'react-bootstrap/lib/Button'; // import validator from 'validator'; -import userAPI from '../../api/user'; -import { pushErrors } from '../../actions/errorActions'; -import { loginUser } from '../../actions/userActions'; -import { Form, FormField, FormFooter } from '../utils/BsForm'; +import userAPI from '../../../api/user'; +import { pushErrors } from '../../../actions/errorActions'; +import { loginUser } from '../../../actions/userActions'; +import { Form, FormField, FormFooter } from '../../utils/BsForm'; const validate = (values) => { const errors = {}; @@ -59,10 +60,11 @@ class LoginForm extends Component { dispatch(push(next || '/')); }); } else { - dispatch(pushErrors([{ - title: 'User Not Exists', - detail: 'You may type wrong email or password.', - }])); + throw new SubmissionError({ + email: 'You may type wrong email or password', + password: 'You may type wrong email or password', + _error: 'Login failed', + }); } }); } @@ -70,6 +72,8 @@ class LoginForm extends Component { render() { const { handleSubmit, + submitFailed, + error, pristine, submitting, invalid, @@ -77,6 +81,7 @@ class LoginForm extends Component { return ( + {submitFailed && error && ({error})} { const errors = {}; @@ -66,6 +67,8 @@ class RegisterForm extends Component { render() { const { handleSubmit, + submitFailed, + error, pristine, asyncValidating, submitting, @@ -74,6 +77,7 @@ class RegisterForm extends Component { return ( + {submitFailed && error && ({error})} { @@ -24,6 +25,10 @@ let EditPage = () => { Edit Profile + + Upload Avatar + + Change Password diff --git a/src/common/components/pages/user/LoginPage.js b/src/common/components/pages/user/LoginPage.js index 78708f7..69c2665 100644 --- a/src/common/components/pages/user/LoginPage.js +++ b/src/common/components/pages/user/LoginPage.js @@ -6,7 +6,7 @@ import Row from 'react-bootstrap/lib/Row'; import Col from 'react-bootstrap/lib/Col'; import PageLayout from '../../layouts/PageLayout'; import Head from '../../widgets/Head'; -import LoginForm from '../../forms/LoginForm'; +import LoginForm from '../../forms/user/LoginForm'; let LoginPage = ({ location }) => { let { next } = location.query; diff --git a/src/common/components/pages/user/RegisterPage.js b/src/common/components/pages/user/RegisterPage.js index c8a6eef..86b73e5 100644 --- a/src/common/components/pages/user/RegisterPage.js +++ b/src/common/components/pages/user/RegisterPage.js @@ -1,7 +1,7 @@ import React from 'react'; import PageHeader from 'react-bootstrap/lib/PageHeader'; import PageLayout from '../../layouts/PageLayout'; -import RegisterForm from '../../forms/RegisterForm'; +import RegisterForm from '../../forms/user/RegisterForm'; const RegisterPage = (props) => ( diff --git a/src/common/components/pages/user/ShowPage.js b/src/common/components/pages/user/ShowPage.js index fabb148..68a5708 100644 --- a/src/common/components/pages/user/ShowPage.js +++ b/src/common/components/pages/user/ShowPage.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import { connect } from 'react-redux'; import { Link } from 'react-router'; import PageHeader from 'react-bootstrap/lib/PageHeader'; import Row from 'react-bootstrap/lib/Row'; @@ -8,23 +9,24 @@ import userAPI from '../../../api/user'; import { pushErrors } from '../../../actions/errorActions'; import Head from '../../widgets/Head'; import PageLayout from '../../layouts/PageLayout'; -import AvatarForm from '../../forms/AvatarForm'; import Time from '../../widgets/Time'; +import RefreshImage from '../../utils/RefreshImage'; class ShowPage extends Component { constructor(props) { super(props); this.state = { - user: {}, + user: props.initialUser, }; } componentDidMount() { - let { store } = this.context; - userAPI(store.getState().apiEngine) - .show() + let { dispatch, apiEngine } = this.props; + + userAPI(apiEngine) + .read() .catch((err) => { - store.dispatch(pushErrors(err)); + dispatch(pushErrors(err)); throw err; }) .then((json) => { @@ -56,7 +58,9 @@ class ShowPage extends Component {
_id
{user._id}
avatar
-
+
+ {user.avatarURL && } +
name
{user.name}
email
@@ -79,8 +83,7 @@ class ShowPage extends Component { } }; -ShowPage.contextTypes = { - store: React.PropTypes.object.isRequired, -}; - -export default ShowPage; +export default connect(({ apiEngine, cookies: { user } }) => ({ + apiEngine: apiEngine, + initialUser: (user && JSON.parse(user)) || {}, +}))(ShowPage); diff --git a/src/common/components/utils/Navigation.js b/src/common/components/utils/Navigation.js index 51d1cee..05d1e5a 100644 --- a/src/common/components/utils/Navigation.js +++ b/src/common/components/utils/Navigation.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router'; import Grid from 'react-bootstrap/lib/Grid'; +import Image from 'react-bootstrap/lib/Image'; import Roles from '../../constants/Roles'; import { updateLocale } from '../../actions/intlActions'; import { pushErrors } from '../../actions/errorActions'; @@ -67,7 +68,13 @@ class Navigation extends Component { title={ !isAuth ? : - (user.name || user.email)} + user.avatarURL ? ( + + ) : (user.name || user.email) + } > {!isAuth && diff --git a/src/common/components/utils/RefreshImage.js b/src/common/components/utils/RefreshImage.js new file mode 100644 index 0000000..4368ad8 --- /dev/null +++ b/src/common/components/utils/RefreshImage.js @@ -0,0 +1,9 @@ +import React from 'react'; +import Image from 'react-bootstrap/lib/Image'; +import toRefreshURL from '../../utils/toRefreshURL'; + +let RefreshImage = ({ src, ...rest }) => ( + +); + +export default RefreshImage; diff --git a/src/common/utils/toRefreshURL.js b/src/common/utils/toRefreshURL.js new file mode 100644 index 0000000..d6ed7c8 --- /dev/null +++ b/src/common/utils/toRefreshURL.js @@ -0,0 +1,5 @@ +export default (URL) => { + let forceUpdate = (URL.indexOf('?') >= 0 ? + '&' : '?') + `forceUpdate=${Math.random()}`; + return URL + forceUpdate; +};