Skip to content
Open

Ht2 #13

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 changes: 2 additions & 0 deletions admin/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { Route } from 'react-router-dom'
import ProtectedRoute from './components/common/protected-route'
import Auth from './routes/auth'
import Admin from './routes/admin'
import Events from './routes/events'

class App extends Component {
render() {
return (
<div>
<Route path="/auth" component={Auth} />
<ProtectedRoute path="/events" component={Events} />
<ProtectedRoute path="/admin" component={Admin} />
</div>
)
Expand Down
3 changes: 3 additions & 0 deletions admin/src/components/common/loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import React from 'react'

export default () => <h4>loading...</h4>
7 changes: 7 additions & 0 deletions admin/src/components/events/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react'

const EventsList = ({ events }) => {
return <ul>{events.map((event, i) => <li key={i}>{event.title}</li>)}</ul>
}

export default EventsList
107 changes: 107 additions & 0 deletions admin/src/ducks/auth.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { call, take, put, apply } from 'redux-saga/effects'
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно еще редюсер протестить

import firebase from 'firebase/app'
import {
SIGN_IN_REQUEST,
SIGN_IN_ERROR,
SIGN_UP_SUCCESS,
SIGN_UP_ERROR,
SIGN_IN_MAX_TRIES_ERROR,
signInSaga,
signUpSaga
} from './auth'

describe('Saga', () => {
describe('sign in', () => {
it('should success', () => {
const saga = signInSaga()

expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST))

const payload = { email: 'test@test.com', password: 'test password' }
const auth = firebase.auth()

expect(saga.next({ payload }).value).toEqual(
apply(auth, auth.signInWithEmailAndPassword, [
payload.email,
payload.password
])
)
})

const checkIncorrectSignIn = (saga) => {
expect(saga.next().value).toEqual(take(SIGN_IN_REQUEST))

const payload = { email: 'test@test.com', password: 'test password' }
const auth = firebase.auth()

expect(saga.next({ payload }).value).toEqual(
apply(auth, auth.signInWithEmailAndPassword, [
payload.email,
payload.password
])
)
const error = new Error('error')
expect(saga.throw(error).value).toEqual(
put({ type: SIGN_IN_ERROR, error })
)
}

it('should error', () => {
const saga = signInSaga()

for (let i = 0; i < 3; i++) {
checkIncorrectSignIn(saga)
}

expect(saga.next().value).toEqual(
put({
type: SIGN_IN_MAX_TRIES_ERROR
})
)
})
})

describe('sign up', () => {
it('should success', () => {
const action = {
payload: { email: 'test@test.conm', password: 'pass12345678' }
}
const { payload } = action
const auth = firebase.auth()

const saga = signUpSaga(action)
expect(saga.next().value).toEqual(
call(
[auth, auth.createUserWithEmailAndPassword],
payload.email,
payload.password
)
)
const user = { firstName: 'testName' }
expect(saga.next(user).value).toEqual(
put({ type: SIGN_UP_SUCCESS, payload: { user } })
)
})

it('should error', () => {
const action = {
payload: { email: 'test@test.conm', password: 'pass12345678' }
}
const { payload } = action
const auth = firebase.auth()

const saga = signUpSaga(action)
expect(saga.next().value).toEqual(
call(
[auth, auth.createUserWithEmailAndPassword],
payload.email,
payload.password
)
)
const error = new Error('error msg')
expect(saga.throw(error).value).toEqual(
put({ type: SIGN_UP_ERROR, error })
)
})
})
})
90 changes: 90 additions & 0 deletions admin/src/ducks/events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { appName } from '../config'
import { Record, List } from 'immutable'
import { createSelector } from 'reselect'
import firebase from 'firebase/app'
import { put, takeEvery, call } from 'redux-saga/effects'

/**
* Constants
* */
export const moduleName = 'events'
const prefix = `${appName}/${moduleName}`
export const EVENTS_REQUEST = `${prefix}/EVENTS_REQUES`
export const EVENTS_REQUES_SUCCESS = `${prefix}/EVENTS_REQUES_SUCCESS`

/**
* Reducer
* */
const ReducerState = Record({
entities: new List([]),
loading: false
})

const EventRecord = Record({
title: null,
url: null,
where: null,
when: null,
month: null,
submissionDeadline: null
})

export default function reducer(state = new ReducerState(), action) {
const { type, payload } = action

switch (type) {
case EVENTS_REQUEST:
return state.set('loading', true)
case EVENTS_REQUES_SUCCESS:
const { events } = payload
const newState = state.set('loading', false)
return Object.keys(events).reduce((acc, key) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше .map, а еще лучше просто об'ектом и хранить

return acc.update('entities', (entities) =>
entities.push(new EventRecord(events[key]))
)
}, newState)
default:
return state
}
}
/**
* Selectors
* */

export const stateSelector = (state) => state[moduleName]
export const eventsSelector = createSelector(stateSelector, (state) =>
state.entities.valueSeq().toArray()
)
export const loadingSelector = createSelector(
stateSelector,
(state) => state.loading
)

/**
* Action Creators
* */

export function loadEvents() {
return {
type: EVENTS_REQUEST
}
}

/**
* Sagas
*/

export function* loadEvensSaga(action) {
const ref = firebase.database().ref('events')
const data = yield call([ref, ref.once], 'value')
const events = data.val()

yield put({
type: EVENTS_REQUES_SUCCESS,
payload: { events }
})
}

export function* saga() {
yield takeEvery(EVENTS_REQUEST, loadEvensSaga)
}
4 changes: 3 additions & 1 deletion admin/src/redux/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { combineReducers } from 'redux'
import { reducer as form } from 'redux-form'
import authReducer, { moduleName as authModule } from '../ducks/auth'
import peopleReducer, { moduleName as peopleModule } from '../ducks/people'
import eventsReducer, { moduleName as eventsModule } from '../ducks/events'

export default combineReducers({
form,
[authModule]: authReducer,
[peopleModule]: peopleReducer
[peopleModule]: peopleReducer,
[eventsModule]: eventsReducer
})
3 changes: 2 additions & 1 deletion admin/src/redux/saga.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { all } from 'redux-saga/effects'
import { saga as peopleSaga } from '../ducks/people'
import { saga as authSaga } from '../ducks/auth'
import { saga as eventsSaga } from '../ducks/events'

export default function*() {
yield all([authSaga(), peopleSaga()])
yield all([authSaga(), peopleSaga(), eventsSaga()])
}
31 changes: 31 additions & 0 deletions admin/src/routes/events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { Component } from 'react'
import { connect } from 'react-redux'

import { loadEvents, loadingSelector, eventsSelector } from '../ducks/events'
import Loader from '../components/common/loader'
import EventsList from '../components/events/list'

class EventsRoute extends Component {
componentDidMount() {
this.props.loadEvents()
}

render() {
const { loading, events } = this.props
return (
<div>
<h1>Events Page</h1>
{loading && <Loader />}
<EventsList events={events} />
</div>
)
}
}

export default connect(
(state) => ({
loading: loadingSelector(state),
events: eventsSelector(state)
}),
{ loadEvents }
)(EventsRoute)