Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,447 changes: 4 additions & 2,443 deletions admin/README.md

Large diffs are not rendered by default.

136 changes: 136 additions & 0 deletions admin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"redux": "^4.0.0",
"redux-form": "^7.4.2",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
"redux-saga": "^0.16.0",
"redux-thunk": "^2.3.0",
"reselect": "^3.0.1"
},
"scripts": {
"start": "react-scripts start",
Expand All @@ -32,6 +34,7 @@
},
"devDependencies": {
"husky": "^1.0.0-rc.6",
"jest-cli": "^20.0.4",
"lint-staged": "^7.1.0",
"prettier": "^1.12.1"
},
Expand Down
38 changes: 38 additions & 0 deletions admin/src/components/people/new-person-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { Component } from 'react'
import { reduxForm, Field } from 'redux-form'
import validateEmail from 'email-validator'
import ErrorField from '../common/error-field'

class NewPersonForm extends Component {
static propTypes = {}

render() {
return (
<div>
<form onSubmit={this.props.handleSubmit}>
<Field name="firstName" label="first name" component={ErrorField} />
<Field name="lastName" label="last name" component={ErrorField} />
<Field name="email" label="email" component={ErrorField} />
<div>
<input type="submit" />
</div>
</form>
</div>
)
}
}

function validate({ firstName, email }) {
const errors = {}
if (!firstName) errors.firstName = 'first name is required'

if (!email) errors.email = 'email is required'
else if (!validateEmail.validate(email)) errors.email = 'email is invalid'

return errors
}

export default reduxForm({
form: 'person',
validate
})(NewPersonForm)
23 changes: 23 additions & 0 deletions admin/src/components/people/people-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { peopleSelector } from '../../ducks/people'

class PeopleList extends Component {
static propTypes = {}

render() {
return (
<div>
{this.props.people.map((person) => (
<li key={person.id}>
{person.firstName}: {person.email}
</li>
))}
</div>
)
}
}

export default connect((state) => ({
people: peopleSelector(state)
}))(PeopleList)
1 change: 1 addition & 0 deletions admin/src/config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { initializeApp } from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'

export const appName = 'adv-react-25-06'

Expand Down
103 changes: 78 additions & 25 deletions admin/src/ducks/auth.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { appName } from '../config'
import { Record } from 'immutable'
import firebase from 'firebase/app'
import { createSelector } from 'reselect'
import { takeEvery, put, call, apply, take, all } from 'redux-saga/effects'

/**
* Constants
* */
export const moduleName = 'auth'
const prefix = `${appName}/${moduleName}`

export const SIGN_IN_REQUEST = `${prefix}/SIGN_IN_REQUEST`
export const SIGN_IN_SUCCESS = `${prefix}/SIGN_IN_SUCCESS`
export const SIGN_IN_ERROR = `${prefix}/SIGN_IN_ERROR`
export const SIGN_IN_MAX_TRIES_ERROR = `${prefix}/SIGN_IN_MAX_TRIES_ERROR`
export const SIGN_UP_REQUEST = `${prefix}/SIGN_UP_REQUEST`
export const SIGN_UP_SUCCESS = `${prefix}/SIGN_UP_SUCCESS`
export const SIGN_UP_ERROR = `${prefix}/SIGN_UP_ERROR`

/**
* Reducer
Expand All @@ -34,43 +41,89 @@ export default function reducer(state = new ReducerRecord(), action) {
* Selectors
* */

export const isAuthorizedSelector = (state) => !!state[moduleName].user
export const userSelector = (state) => state[moduleName].user
export const isAuthorizedSelector = createSelector(
userSelector,
(user) => !!user
)

/**
* Action Creators
* */

export function signIn(email, password) {
return (dispatch) => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((user) =>
dispatch({
type: SIGN_IN_SUCCESS,
payload: { user }
})
)
return {
type: SIGN_IN_REQUEST,
payload: { email, password }
}
}

export function signUp(email, password) {
return (dispatch) => {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((user) =>
dispatch({
type: SIGN_UP_SUCCESS,
payload: { user }
})
)
return {
type: SIGN_UP_REQUEST,
payload: { email, password }
}
}

firebase.auth().onAuthStateChanged((user) => {
window.store.dispatch({
type: SIGN_IN_SUCCESS,
payload: { user }
/**
* Sagas
*/

export function* signUpSaga({ payload }) {
const auth = firebase.auth()

try {
const user = yield call(
[auth, auth.createUserWithEmailAndPassword],
payload.email,
payload.password
)

yield put({
type: SIGN_UP_SUCCESS,
payload: { user }
})
} catch (error) {
yield put({
type: SIGN_UP_ERROR,
error
})
}
}

export function* signInSaga() {
for (let i = 0; i < 3; i++) {
const { payload } = yield take(SIGN_IN_REQUEST)

const auth = firebase.auth()

try {
yield apply(auth, auth.signInWithEmailAndPassword, [
payload.email,
payload.password
])
} catch (error) {
yield put({
type: SIGN_IN_ERROR,
error
})
}
}

yield put({
type: SIGN_IN_MAX_TRIES_ERROR
})
}

export function* saga() {
yield all([takeEvery(SIGN_UP_REQUEST, signUpSaga), signInSaga()])
}

firebase.auth().onAuthStateChanged((user) => {
if (user) {
window.store.dispatch({
type: SIGN_IN_SUCCESS,
payload: { user }
})
}
})
Loading