-
{input.name}
+
+ {input.name}
{errorText}
diff --git a/src/components/common/ProtectedRoute.js b/src/components/common/ProtectedRoute.js
index da53475..fe4e302 100644
--- a/src/components/common/ProtectedRoute.js
+++ b/src/components/common/ProtectedRoute.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import {Route} from 'react-router-dom'
import {connect} from 'react-redux'
-import {userSelector} from '../../ducks/auth'
+import {userAuthorized} from '../../ducks/auth'
class ProtectedRoute extends Component {
static propTypes = {
@@ -21,5 +21,5 @@ class ProtectedRoute extends Component {
}
export default connect(state => ({
- authorized: !!userSelector(state)
+ authorized: userAuthorized(state)
}), null, null, { pure: false })(ProtectedRoute)
\ No newline at end of file
diff --git a/src/components/common/Submenu.js b/src/components/common/Submenu.js
new file mode 100644
index 0000000..370f97e
--- /dev/null
+++ b/src/components/common/Submenu.js
@@ -0,0 +1,30 @@
+import React, { Component } from 'react'
+import { NavLink } from 'react-router-dom'
+
+class Submenu extends Component {
+ render() {
+ return (
+
+ {this.props.links.map(this.renderSubmenuItem.bind(this))}
+
+ )
+ }
+
+ renderSubmenuItem(item, index) {
+ const { to, title } = item
+
+ const props = {
+ key: index,
+ activeClassName: 'active',
+ to,
+ }
+
+ return (
+
+ {title}
+
+ )
+ }
+}
+
+export default Submenu
diff --git a/src/components/routes/Auth.js b/src/components/routes/Auth.js
index a183ff9..d06aa85 100644
--- a/src/components/routes/Auth.js
+++ b/src/components/routes/Auth.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import {Route, NavLink} from 'react-router-dom'
import {connect} from 'react-redux'
-import {signUp} from '../../ducks/auth'
+import {signUp, signIn, errorMessage, userAuthorized} from '../../ducks/auth'
import SignIn from '../auth/SignIn'
import SignUp from '../auth/SignUp'
@@ -14,16 +14,27 @@ class AuthPage extends Component {
return (
Auth page
- sign in
- sign up
- }/>
+
}/>
)
}
- handleSignIn = (values) => console.log('---', 'sign in', values)
+ renderSignIn() {
+ const props = {
+ onSubmit: this.handleSignIn,
+ signInError: this.props.error,
+ shouldReset: this.props.authorized,
+ }
+
+ return
+ }
+
+ handleSignIn = ({email, password}) => this.props.signIn(email, password)
handleSignUp = ({email, password}) => this.props.signUp(email, password)
}
-export default connect(null, { signUp })(AuthPage)
\ No newline at end of file
+export default connect(state => ({
+ error: errorMessage(state),
+ authorized: userAuthorized(state),
+}), { signIn, signUp, errorMessage })(AuthPage)
diff --git a/src/config.js b/src/config.js
index 2782a0b..6ce5a80 100644
--- a/src/config.js
+++ b/src/config.js
@@ -1,12 +1,12 @@
import firebase from 'firebase'
-export const appName = 'advreact-1610'
+export const appName = 'advreact-1610-9cb0c'
firebase.initializeApp({
- apiKey: "AIzaSyB31xpTtp4Jln_hb2kAbE4PGf6Mi8EgLyA",
+ apiKey: "AIzaSyBNaOQkLL75ZGoeaNtqbe63wEjjWLzLOPY",
authDomain: `${appName}.firebaseapp.com`,
databaseURL: `https://${appName}.firebaseio.com`,
projectId: appName,
storageBucket: "",
- messagingSenderId: "397157634637"
+ messagingSenderId: "266748171955"
})
\ No newline at end of file
diff --git a/src/ducks/auth.js b/src/ducks/auth.js
index 25f3bf7..cb6a563 100644
--- a/src/ducks/auth.js
+++ b/src/ducks/auth.js
@@ -3,6 +3,7 @@ import {Record} from 'immutable'
import firebase from 'firebase'
import {createSelector} from 'reselect'
import {call, put, all, take} from 'redux-saga/effects'
+import history from '../history'
/**
* Constants
@@ -14,7 +15,10 @@ export const SIGN_UP_START = `${prefix}/SIGN_UP_START`
export const SIGN_UP_SUCCESS = `${prefix}/SIGN_UP_SUCCESS`
export const SIGN_UP_ERROR = `${prefix}/SIGN_UP_ERROR`
+export const SIGN_IN_START = `${prefix}/SIGN_IN_START`
export const SIGN_IN_SUCCESS = `${prefix}/SIGN_IN_SUCCESS`
+export const SIGN_IN_ERROR = `${prefix}/SIGN_IN_ERROR`
+
/**
* Reducer
@@ -30,7 +34,10 @@ export default function reducer(state = new ReducerRecord(), action) {
switch (type) {
case SIGN_UP_START:
- return state.set('loading', true)
+ case SIGN_IN_START:
+ return state
+ .set('loading', true)
+ .set('error', null)
case SIGN_UP_SUCCESS:
case SIGN_IN_SUCCESS:
@@ -38,6 +45,12 @@ export default function reducer(state = new ReducerRecord(), action) {
.set('user', payload.user)
.set('loading', false)
+ case SIGN_UP_ERROR:
+ case SIGN_IN_ERROR:
+ return state
+ .set('error', payload.error)
+ .set('loading', false)
+
default:
return state
}
@@ -49,6 +62,8 @@ export default function reducer(state = new ReducerRecord(), action) {
export const stateSelector = state => state[moduleName]
export const userSelector = createSelector(stateSelector, state => state.user)
+export const userAuthorized = createSelector(stateSelector, state => !!state.user)
+export const errorMessage = createSelector(stateSelector, state => state.error && state.error.message || '')
/**
* Action Creators
@@ -60,6 +75,13 @@ export function signUp(email, password) {
}
}
+export function signIn(email, password) {
+ return {
+ type: SIGN_IN_START,
+ payload: { email, password }
+ }
+}
+
firebase.auth().onAuthStateChanged(user => {
if (!user) return
@@ -96,8 +118,35 @@ export function * signUpSaga() {
}
}
+export function * signInSaga() {
+ const auth = firebase.auth()
+
+ while (true) {
+ const action = yield take(SIGN_IN_START)
+ const { email, password } = action.payload
+
+ try {
+ const user = yield call([auth, auth.signInWithEmailAndPassword], email, password)
+
+ yield put({
+ type: SIGN_IN_SUCCESS,
+ payload: {user},
+ })
+
+ yield call([history, history.push], '/people')
+ }
+ catch (error) {
+ yield put({
+ type: SIGN_IN_ERROR,
+ payload: {error},
+ })
+ }
+ }
+}
+
export function * saga() {
yield all([
- signUpSaga()
+ signUpSaga(),
+ signInSaga(),
])
-}
\ No newline at end of file
+}
diff --git a/src/ducks/auth.test.js b/src/ducks/auth.test.js
new file mode 100644
index 0000000..3f22b0c
--- /dev/null
+++ b/src/ducks/auth.test.js
@@ -0,0 +1,124 @@
+import firebase from 'firebase'
+import history from '../history'
+import {all, call, put, take} from 'redux-saga/effects'
+import {
+ SIGN_UP_START, SIGN_UP_SUCCESS, SIGN_UP_ERROR,
+ SIGN_IN_START, SIGN_IN_SUCCESS, SIGN_IN_ERROR,
+ signUp, signIn,
+ saga, signUpSaga, signInSaga,
+} from './auth'
+
+describe('Sagas', () => {
+ test('combining saga', () => {
+ const gen = saga()
+
+ const { value } = gen.next()
+
+ // this one fails. how to make it work? TY
+ /*expect(value).toEqual(all([
+ signUpSaga(),
+ signInSaga(),
+ ]))*/
+
+ expect(gen.next().done).toBeTruthy()
+ })
+
+ test('signUpSaga success', () => {
+ expect.assertions(4)
+
+ const auth = firebase.auth()
+ const email = 'email'
+ const password = 'pswd'
+ const user = {the: 'user'}
+ const payload = {email, password}
+ const request = signUp(email, password)
+ const gen = signUpSaga(request)
+
+ expect(gen.next().value).toEqual(take(SIGN_UP_START))
+
+ expect(gen.next({payload}).value).toEqual(call(
+ [auth, auth.createUserWithEmailAndPassword], email, password))
+
+ expect(gen.next(user).value).toEqual(put({
+ type: SIGN_UP_SUCCESS,
+ payload: {user}
+ }))
+
+ expect(gen.next().value).toEqual(take(SIGN_UP_START))
+ })
+
+ test('signUpSaga error', () => {
+ expect.assertions(4)
+
+ const auth = firebase.auth()
+ const email = 'email'
+ const password = 'pswd'
+ const error = new Error('fail')
+ const payload = {email, password}
+ const request = signUp(email, password)
+ const gen = signUpSaga(request)
+
+ expect(gen.next().value).toEqual(take(SIGN_UP_START))
+
+ expect(gen.next({payload}).value).toEqual(call(
+ [auth, auth.createUserWithEmailAndPassword], email, password))
+
+ expect(gen.throw(error).value).toEqual(put({
+ type: SIGN_UP_ERROR,
+ payload: {error}
+ }))
+
+ expect(gen.next().value).toEqual(take(SIGN_UP_START))
+ })
+
+ test('signInSaga success', () => {
+ expect.assertions(5);
+
+ const auth = firebase.auth()
+ const email = 'email'
+ const password = 'pswd'
+ const payload = {email, password}
+ const request = signIn(payload)
+ const gen = signInSaga(request)
+ const user = {the: 'user'}
+
+ expect(gen.next().value).toEqual(take(SIGN_IN_START))
+
+ expect(gen.next({payload}).value).toEqual(call(
+ [auth, auth.signInWithEmailAndPassword], email, password))
+
+ expect(gen.next(user).value).toEqual(put({
+ type: SIGN_IN_SUCCESS,
+ payload: {user},
+ }))
+
+ expect(gen.next().value).toEqual(call(
+ [history, history.push], '/people'))
+
+ expect(gen.next().value).toEqual(take(SIGN_IN_START))
+ })
+
+ test('signInSaga error', () => {
+ expect.assertions(4);
+
+ const auth = firebase.auth()
+ const email = 'email'
+ const password = 'pswd'
+ const payload = {email, password}
+ const request = signIn(payload)
+ const gen = signInSaga(request)
+ const error = new Error('fail')
+
+ expect(gen.next().value).toEqual(take(SIGN_IN_START))
+
+ expect(gen.next({payload}).value).toEqual(call(
+ [auth, auth.signInWithEmailAndPassword], email, password))
+
+ expect(gen.throw(error).value).toEqual(put({
+ type: SIGN_IN_ERROR,
+ payload: {error},
+ }))
+
+ expect(gen.next().value).toEqual(take(SIGN_IN_START))
+ })
+})