# HG changeset patch # User Alexandre Segura # Date 1498579930 -7200 # Node ID 6fd752d98933c16b078ce579a9292e9fb440d92d # Parent 18fa4a1fa9e9b1c6b9b728c26c6470b4e0c6fba1 Introduce group creation. diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/actions/authActions.js --- a/client/src/actions/authActions.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/actions/authActions.js Tue Jun 27 18:12:10 2017 +0200 @@ -23,3 +23,14 @@ password2 }; } + +export const createGroup = (name) => { + const group = { + name + }; + + return { + type: types.GROUP_CREATE_ASYNC, + group, + }; +} diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/api/WebAnnotationSerializer.js --- a/client/src/api/WebAnnotationSerializer.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/api/WebAnnotationSerializer.js Tue Jun 27 18:12:10 2017 +0200 @@ -1,5 +1,3 @@ -import Note from '../store/noteRecord'; - class WebAnnotationSerializer { static serialize = (note) => { diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/components/SessionForm.js --- a/client/src/components/SessionForm.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/components/SessionForm.js Tue Jun 27 18:12:10 2017 +0200 @@ -1,13 +1,30 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Panel, FormGroup, ControlLabel, FormControl } from 'react-bootstrap'; +import { Panel, FormGroup, ControlLabel, FormControl, Button, InputGroup, HelpBlock } from 'react-bootstrap'; import '../App.css'; import * as sessionsActions from '../actions/sessionsActions'; +import * as authActions from '../actions/authActions'; import _ from 'lodash'; class SessionForm extends Component { + state = { + createGroup: false + } + + toggleCreateGroup = (e) => { + e.preventDefault(); + const { createGroup } = this.state; + this.setState({ createGroup: !createGroup }); + } + + onClickCreateGroup = (e) => { + e.preventDefault(); + const groupName = this.groupName.value; + this.props.authActions.createGroup(groupName); + } + onChange = (e) => { const { name, value } = e.target; const changes = { [name]: value } @@ -18,6 +35,46 @@ this.props.sessionsActions.updateSession(this.props.currentSession, changes); }, 750) + renderCreateGroup = () => { + const { createGroup } = this.props; + const hasErrors = true === createGroup.get('error') && createGroup.get('errorMessages').has('name'); + + let errors = []; + if (hasErrors) { + const errorMessages = createGroup.get('errorMessages').toArray(); + errors = errorMessages.map((message, key) => { + return ( + { message } + ) + }) + } + + if (this.state.createGroup) { + return ( + + + { this.groupName = ref; } } /> + + + + + { errors } +
+ +
+ ) + } + + return ( + + + + ) + } + render() { if (!this.props.currentSession) { @@ -51,6 +108,9 @@ inputRef={ ref => { this.description = ref; } } /> + + { this.renderCreateGroup() } + ); @@ -60,12 +120,14 @@ function mapStateToProps(state, props) { return { currentSession: props.session, + createGroup: state.createGroup }; } function mapDispatchToProps(dispatch) { return { sessionsActions: bindActionCreators(sessionsActions, dispatch), + authActions: bindActionCreators(authActions, dispatch), } } diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/constants/actionTypes.js --- a/client/src/constants/actionTypes.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/constants/actionTypes.js Tue Jun 27 18:12:10 2017 +0200 @@ -25,6 +25,10 @@ export const AUTH_LOGOUT = 'AUTH_LOGOUT'; export const AUTH_DEAUTHENTICATE = 'AUTH_DEAUTHENTICATE'; +export const GROUP_CREATE_ASYNC = 'GROUP_CREATE_ASYNC'; +export const GROUP_CREATE_SUCCESS = 'GROUP_CREATE_SUCCESS'; +export const GROUP_CREATE_ERROR = 'GROUP_CREATE_ERROR'; + export const USER_UPDATE_SETTINGS_ASYNC = 'USER_UPDATE_SETTINGS_ASYNC'; export const USER_UPDATE_SETTINGS = 'USER_UPDATE_SETTINGS' export const USER_TOGGLE_AUTO_SUBMIT = 'USER_TOGGLE_AUTO_SUBMIT'; diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/reducers/authReducer.js --- a/client/src/reducers/authReducer.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/reducers/authReducer.js Tue Jun 27 18:12:10 2017 +0200 @@ -85,3 +85,31 @@ return state } } + +export const groups = (state = Immutable.List([]), action) => { + switch (action.type) { + default: + return state + } +} + +export const createGroup = (state = asyncRequest, action) => { + switch (action.type) { + case types.GROUP_CREATE_ASYNC: + return Immutable.Map({ + loading: true, + success: false, + error: false, + }) + case types.GROUP_CREATE_SUCCESS: + case types.GROUP_CREATE_ERROR: + return Immutable.Map({ + loading: false, + success: action.type === types.GROUP_CREATE_SUCCESS, + error: action.type === types.GROUP_CREATE_ERROR, + errorMessages: action.type === types.GROUP_CREATE_ERROR ? Immutable.Map(action.error) : Immutable.Map({}) + }) + default: + return state + } +} diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/reducers/index.js --- a/client/src/reducers/index.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/reducers/index.js Tue Jun 27 18:12:10 2017 +0200 @@ -4,7 +4,7 @@ import notes from './notesReducer'; import { sessions } from './sessionsReducer'; -import { isAuthenticated, currentUser, login, register, token } from './authReducer'; +import { isAuthenticated, currentUser, login, register, token, groups, createGroup } from './authReducer'; import { autoSubmit } from './miscReducer'; const rootReducer = combineReducers({ @@ -16,7 +16,9 @@ register, token, router: routerReducer, - autoSubmit + autoSubmit, + groups, + createGroup }); export default rootReducer; diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/sagas/groupSaga.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/sagas/groupSaga.js Tue Jun 27 18:12:10 2017 +0200 @@ -0,0 +1,21 @@ +import { put, take, all } from 'redux-saga/effects' +import * as types from '../constants/actionTypes'; + +function* watchCreateGroup(context) { + while (true) { + const { group } = yield take(types.GROUP_CREATE_ASYNC); + const client = context.client; + try { + const response = yield client.post('/api/auth/group/', group); + yield put({ type: types.GROUP_CREATE_SUCCESS, group: response }); + } catch (e) { + yield put({ type: types.GROUP_CREATE_ERROR, error: e }); + } + } +} + +export default function* rootSaga(context) { + yield all([ + watchCreateGroup(context), + ]) +} diff -r 18fa4a1fa9e9 -r 6fd752d98933 client/src/store/configureStore.js --- a/client/src/store/configureStore.js Tue Jun 27 18:11:40 2017 +0200 +++ b/client/src/store/configureStore.js Tue Jun 27 18:12:10 2017 +0200 @@ -1,5 +1,6 @@ import rootReducer from '../reducers'; import rootAuthSaga from '../sagas/authSaga'; +import rootGroupSaga from '../sagas/groupSaga'; import networkSaga from '../sagas/networkSaga'; import { compose, createStore, applyMiddleware } from 'redux'; import { routerMiddleware } from 'react-router-redux'; @@ -28,12 +29,14 @@ const defaultState = { sessions: Immutable.List([]), notes: Immutable.List([]), + groups: Immutable.List([]), isAuthenticated: false, currentUser: null, token: '', autoSubmit: false, login: asyncRequest, register: asyncRequest, + createGroup: asyncRequest }; const immutableTransformConfig = { @@ -77,6 +80,7 @@ } saga.run(rootAuthSaga, context); + saga.run(rootGroupSaga, context); store.dispatch(offlineConfigInitialized({ client: apiClient }))