add group to sync + create groups + various component cleaning
authorymh <ymh.work@gmail.com>
Tue, 01 Aug 2017 12:20:14 +0200
changeset 132 906a6c7c7943
parent 131 adad5563603c
child 133 6f3078f7fd47
add group to sync + create groups + various component cleaning
client/src/actions/groupActions.js
client/src/actions/notesActions.js
client/src/actions/sessionsActions.js
client/src/components/GroupForm.js
client/src/components/SessionForm.js
client/src/constants/actionTypes.js
client/src/reducers/authReducer.js
client/src/reducers/notesReducer.js
client/src/reducers/sessionsReducer.js
client/src/sagas/SessionSyncronizer.js
client/src/sagas/groupSaga.js
client/src/sagas/syncSaga.js
client/src/store/configureStore.js
client/src/store/groupRecord.js
client/src/store/sessionRecord.js
client/src/store/userRecord.js
src/notes/api/views/core.py
src/notes/api/views/sync.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/actions/groupActions.js	Tue Aug 01 12:20:14 2017 +0200
@@ -0,0 +1,17 @@
+import * as types from '../constants/actionTypes';
+
+
+export const groupCreateSuccess = (group) =>
+  ({ type: types.GROUP_CREATE_SUCCESS, group });
+
+export const groupCreateError = (error) =>
+  ({ type: types.GROUP_CREATE_ERROR, error });
+
+export const groupLoadSucess = (groups) =>
+  ({ type: types.GROUP_LOAD_SUCCESS, groups });
+
+export const groupLoadError = (error) =>
+  ({ type: types.GROUP_LOAD_ERROR, error });
+
+export const groupLoadAsync = () =>
+  ({ type: types.GROUP_LOAD_ASYNC });
--- a/client/src/actions/notesActions.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/actions/notesActions.js	Tue Aug 01 12:20:14 2017 +0200
@@ -45,6 +45,10 @@
   return { type: types.DO_DELETE_NOTE, noteId };
 }
 
+export const doDeleteAllNote = () => {
+  return { type: types.DO_DELETE_ALL_NOTE };
+}
+
 export const loadNote = (note) => {
   return { type: types.LOAD_NOTE, note };
 }
--- a/client/src/actions/sessionsActions.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/actions/sessionsActions.js	Tue Aug 01 12:20:14 2017 +0200
@@ -39,6 +39,10 @@
   return { type: types.DO_DELETE_SESSION, sessionId };
 }
 
+export const doDeleteAllSession = () => (
+  { type: types.DO_DELETE_ALL_SESSION }
+)
+
 export const loadSessions = () => {
   return {
     type: types.LOAD_SESSIONS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/GroupForm.js	Tue Aug 01 12:20:14 2017 +0200
@@ -0,0 +1,106 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import { FormGroup, FormControl, Button, InputGroup, HelpBlock, Glyphicon } from 'react-bootstrap';
+import * as authActions from '../actions/authActions';
+import * as sessionsActions from '../actions/sessionsActions';
+
+class GroupForm extends Component {
+
+  state = {
+    createGroup: false,
+    groupName: ''
+  }
+
+  toggleCreateGroup = (e) => {
+    e.preventDefault();
+    const { createGroup } = this.state;
+    this.setState({ createGroup: !createGroup });
+  }
+
+  onClickCreateGroup = (e) => {
+    e.preventDefault();
+    const groupName = this.state.groupName;
+    this.props.sessionsActions.createGroupAndUpdateSession(this.props.session, groupName);
+    this.setState({
+      createGroup: false,
+      groupName: ''
+    })
+  }
+
+  handleInputChange = (e) => {
+    const target = e.target;
+    const value = target.value;
+    const name = target.name;
+
+    this.setState({
+      [name]: value
+    });
+  }
+
+
+  render = () => {
+    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 (
+          <HelpBlock key={ key }>{ message }</HelpBlock>
+        )
+      })
+    }
+
+    if (this.state.createGroup) {
+      return (
+        <FormGroup validationState={ hasErrors ? 'error' : null }>
+          <InputGroup>
+            <FormControl
+              type="text"
+              placeholder="Enter a name for your group"
+              onChange={this.handleInputChange}
+              name="groupName"
+              value={this.state.groupName} />
+            <InputGroup.Button>
+              <Button bsStyle="primary" onClick={ this.onClickCreateGroup }>Create</Button>
+            </InputGroup.Button>
+          </InputGroup>
+          { errors }
+          <hr />
+          <Button onClick={ this.toggleCreateGroup }>Cancel</Button>
+        </FormGroup>
+      )
+    }
+
+    if(this.props.online) {
+      return (
+        <FormGroup>
+          <Button className="pull-right" bsSize="small" onClick={ this.toggleCreateGroup }>
+            <Glyphicon glyph="plus" />  Create a new group
+          </Button>
+        </FormGroup>
+      )
+    }
+    return null;
+  }
+
+}
+
+function mapStateToProps(state, props) {
+
+  return {
+    createGroup: state.get('createGroup'),
+    online: state.getIn(['status', 'online']),
+  };
+}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    sessionsActions: bindActionCreators(sessionsActions, dispatch),
+    authActions: bindActionCreators(authActions, dispatch),
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(GroupForm);
--- a/client/src/components/SessionForm.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/components/SessionForm.js	Tue Aug 01 12:20:14 2017 +0200
@@ -1,7 +1,8 @@
 import React, { Component } from 'react';
 import { connect } from 'react-redux';
 import { bindActionCreators } from 'redux';
-import { Panel, FormGroup, ControlLabel, FormControl, Button, InputGroup, HelpBlock, Glyphicon } from 'react-bootstrap';
+import { Panel, FormGroup, ControlLabel, FormControl } from 'react-bootstrap';
+import GroupForm from './GroupForm';
 import '../App.css';
 import * as sessionsActions from '../actions/sessionsActions';
 import * as authActions from '../actions/authActions';
@@ -13,18 +14,6 @@
     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.sessionsActions.createGroupAndUpdateSession(this.props.currentSession, groupName);
-  }
-
   onChange = (e) => {
     const { name, value } = e.target;
     const changes = { [name]: value }
@@ -36,10 +25,9 @@
   }, 750)
 
   onGroupChange = (e) => {
-    const groupId = parseInt(this.group.value, 10);
-    const group = this.props.groups.find(group => group.id === groupId);
+    const groupName = e.target.value;
 
-    this.props.sessionsActions.updateSession(this.props.currentSession, { group });
+    this.props.sessionsActions.updateSession(this.props.currentSession, { group: groupName });
   }
 
   componentWillUpdate = (nextProps, nextState) => {
@@ -48,48 +36,6 @@
     }
   }
 
-  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 (
-          <HelpBlock key={ key }>{ message }</HelpBlock>
-        )
-      })
-    }
-
-    if (this.state.createGroup) {
-      return (
-        <FormGroup validationState={ hasErrors ? 'error' : null }>
-          <InputGroup>
-            <FormControl
-              type="text"
-              placeholder="Enter a name for your group"
-              inputRef={ ref => { this.groupName = ref; } } />
-            <InputGroup.Button>
-              <Button bsStyle="primary" onClick={ this.onClickCreateGroup }>Create</Button>
-            </InputGroup.Button>
-          </InputGroup>
-          { errors }
-          <hr />
-          <Button onClick={ this.toggleCreateGroup }>Cancel</Button>
-        </FormGroup>
-      )
-    }
-
-    return (
-      <FormGroup>
-        <Button className="pull-right" bsSize="small" onClick={ this.toggleCreateGroup }>
-          <Glyphicon glyph="plus" />  Create a new group
-        </Button>
-      </FormGroup>
-    )
-  }
-
   render() {
 
     if (!this.props.currentSession) {
@@ -109,7 +55,6 @@
               onChange={ this.onChange }
               type="text"
               placeholder="Enter a title"
-              inputRef={ ref => { this.title = ref; } }
             />
           </FormGroup>
           <FormGroup>
@@ -120,7 +65,6 @@
               defaultValue={ this.props.currentSession.description }
               onChange={ this.onChange }
               placeholder="Enter a description"
-              inputRef={ ref => { this.description = ref; } }
             />
           </FormGroup>
           <FormGroup>
@@ -128,17 +72,15 @@
             <FormControl
               name="group"
               componentClass="select"
-              value={ this.props.group ? this.props.group.id : '' }
-              onChange={ this.onGroupChange }
-              inputRef={ ref => { this.group = ref; } }>
-              <option />
+              value={ this.props.group ? this.props.group.get('name') : '' }
+              onChange={ this.onGroupChange }>
               { this.props.groups.map((group, key) =>
-                <option key={ key } value={ group.id }>{ group.name }</option>
+                <option key={ key } value={ group.get('name') }>{ group.get('name') }</option>
               ) }
             </FormControl>
           </FormGroup>
           <FormGroup>
-            { this.renderCreateGroup() }
+            <GroupForm session={this.props.session} />
           </FormGroup>
         </form>
       </Panel>
@@ -149,8 +91,8 @@
 function mapStateToProps(state, props) {
 
   let group;
-  if (props.session && props.session.group) {
-    group = state.groups.find(group => props.session.group.get('id') === group.id)
+  if (props.session && props.session.get('group')) {
+    group = state.get('groups').find(group => props.session.get('group') === group.get('name'))
   }
 
   return {
--- a/client/src/constants/actionTypes.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/constants/actionTypes.js	Tue Aug 01 12:20:14 2017 +0200
@@ -3,6 +3,7 @@
 export const ADD_NOTE = 'ADD_NOTE';
 export const DELETE_NOTE = 'DELETE_NOTE';
 export const DO_DELETE_NOTE = 'DO_DELETE_NOTE';
+export const DO_DELETE_ALL_NOTE = 'DO_DELETE_ALL_NOTE';
 export const UPDATE_NOTE = 'UPDATE_NOTE';
 export const RESET_ACTION_NOTE = 'RESET_ACTION_NOTE';
 export const LOAD_NOTE = 'LOAD_NOTE';
@@ -11,6 +12,7 @@
 export const UPDATE_SESSION = 'UPDATE_SESSION';
 export const DELETE_SESSION = 'DELETE_SESSION';
 export const DO_DELETE_SESSION = 'DO_DELETE_SESSION';
+export const DO_DELETE_ALL_SESSION = 'DO_DELETE_ALL_SESSION';
 export const LOAD_SESSIONS = 'LOAD_SESSIONS';
 export const LOAD_SESSION = 'LOAD_SESSION';
 export const RESET_ACTION_SESSION = 'RESET_ACTION_SESSION';
--- a/client/src/reducers/authReducer.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/reducers/authReducer.js	Tue Aug 01 12:20:14 2017 +0200
@@ -1,8 +1,10 @@
 import Immutable from 'immutable';
 import * as types from '../constants/actionTypes';
 import UserRecord from '../store/userRecord';
+import GroupRecord from '../store/groupRecord';
 import asyncRequest from '../constants/asyncRequest';
 import uuidV4 from 'uuid/v4';
+import _ from 'lodash';
 
 export const isAuthenticated = (state = false, action) => {
   switch (action.type) {
@@ -103,9 +105,20 @@
 export const groups = (state = Immutable.List([]), action) => {
   switch (action.type) {
     case types.GROUP_LOAD_SUCCESS:
-      return Immutable.List(action.groups.results);
+      return Immutable.List(
+        action.groups.map((group) => GroupRecord({
+          ...(_.omit(group, 'is_personal')),
+          isPersonal: group['is_personal']
+        }))
+      );
     case types.GROUP_CREATE_SUCCESS:
-      return state.push(action.group);
+      return state.push(GroupRecord({
+          ...(_.omit(action.group, 'is_personal')),
+          isPersonal: action.group['is_personal']
+        }));
+    case types.AUTH_LOGOUT: {
+      return Immutable.List(); // empty note list on logout
+    }
     default:
       return state
   }
--- a/client/src/reducers/notesReducer.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/reducers/notesReducer.js	Tue Aug 01 12:20:14 2017 +0200
@@ -81,6 +81,9 @@
         return state.push(noteRec);
       }
     }
+    case types.AUTH_LOGOUT: {
+      return Immutable.List(); // empty note list on logout
+    }
     default:
       return state;
   }
--- a/client/src/reducers/sessionsReducer.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/reducers/sessionsReducer.js	Tue Aug 01 12:20:14 2017 +0200
@@ -75,6 +75,9 @@
       const session = state.get(index);
       return state.set(index, session.merge({action: ActionEnum.NONE}));
     }
+    case types.AUTH_LOGOUT: {
+      return Immutable.List(); // empty session list on logout
+    }
     default:
       return state;
   }
--- a/client/src/sagas/SessionSyncronizer.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/sagas/SessionSyncronizer.js	Tue Aug 01 12:20:14 2017 +0200
@@ -42,18 +42,20 @@
       date: localObjInst.get('date'),
       title: localObjInst.get('title'),
       description: localObjInst.get('description'),
+      group: localObjInst.get('group'),
       protocol: ''
     };
   }
 
   getLocalRecord(remoteObj) {
-    return new SessionRecord({
+    return SessionRecord({
         _id: remoteObj.ext_id,
         title: remoteObj.title,
         description: remoteObj.description,
         date: moment(remoteObj.date).toDate(),
         action: ActionEnum.NONE,
-        group: null
+        group: remoteObj.group,
+        protocol: remoteObj.protocol,
     });
   }
 
--- a/client/src/sagas/groupSaga.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/sagas/groupSaga.js	Tue Aug 01 12:20:14 2017 +0200
@@ -1,5 +1,7 @@
 import { put, take, all } from 'redux-saga/effects'
 import * as types from '../constants/actionTypes';
+import { groupCreateSuccess, groupCreateError, groupLoadSucess, groupLoadError } from '../actions/groupActions';
+import { updateSession } from '../actions/sessionsActions';
 
 function* watchCreateGroup(context) {
   while (true) {
@@ -7,9 +9,9 @@
     const client = context.client;
     try {
       const response = yield client.post('/api/auth/group/', group);
-      yield put({ type: types.GROUP_CREATE_SUCCESS, group: response });
+      yield put(groupCreateSuccess(response));
     } catch (e) {
-      yield put({ type: types.GROUP_CREATE_ERROR, error: e });
+      yield put(groupCreateError(e));
     }
   }
 }
@@ -22,21 +24,13 @@
       const response = yield client.post('/api/auth/group/', group);
 
       const actions = [
-        {
-          type: types.GROUP_CREATE_SUCCESS,
-          group: response
-        },
-        {
-          type: types.UPDATE_SESSION,
-          session: session,
-          values: { group: response },
-        }
+        groupCreateSuccess(response),
+        updateSession(session, {group: response.name})
       ];
-
       yield all(actions.map(action => put(action)));
 
     } catch (e) {
-      yield put({ type: types.GROUP_CREATE_ERROR, error: e });
+      yield put(groupCreateError(e));
     }
   }
 }
@@ -47,9 +41,9 @@
     yield take(types.GROUP_LOAD_ASYNC);
     try {
       const response = yield client.get('/api/auth/group/');
-      yield put({ type: types.GROUP_LOAD_SUCCESS, groups: response });
+      yield put(groupLoadSucess(response.results));
     } catch (e) {
-      yield put({ type: types.GROUP_LOAD_ERROR, error: e });
+      yield put(groupLoadError(e));
     }
   }
 }
--- a/client/src/sagas/syncSaga.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/sagas/syncSaga.js	Tue Aug 01 12:20:14 2017 +0200
@@ -1,10 +1,11 @@
-import { put, take, all, select, spawn, race, call} from 'redux-saga/effects'
+import { put, take, all, select, spawn, race, call, fork} from 'redux-saga/effects'
 import { delay } from 'redux-saga';
 import * as types from '../constants/actionTypes';
 import { getLastSync, getOnline } from './selectors';
 import moment from 'moment';
 import { startSynchronize, endSynchronize, updateLastSync } from '../actions/syncActions';
 import { forceSync } from '../actions/networkActions';
+import { groupLoadAsync } from '../actions/groupActions';
 import SessionSynchronizer from './SessionSyncronizer';
 import NoteSynchronizer from './NoteSyncronizer';
 import config from '../config';
@@ -39,10 +40,15 @@
 
 }
 
+function* doLoadGroup() {
+  yield put(groupLoadAsync());
+}
+
 function* watchDoSync(context) {
   while (true) {
     yield take(types.SYNC_DO_SYNC);
-    yield spawn(doSync, context);
+    yield spawn(doSync, context); //we use spawn + take, because we do not want it to be interupted
+    yield fork(doLoadGroup);
     yield take(types.SYNC_END_SYNC);
   }
 }
--- a/client/src/store/configureStore.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/store/configureStore.js	Tue Aug 01 12:20:14 2017 +0200
@@ -13,6 +13,7 @@
 import NoteRecord from './noteRecord';
 import SessionRecord from './sessionRecord';
 import UserRecord from './userRecord';
+import GroupRecord from './groupRecord';
 import APIClient from '../api/APIClient';
 import config from '../config';
 import asyncRequest from '../constants/asyncRequest';
@@ -45,15 +46,15 @@
 };
 
 const immutableTransformConfig = {
-  records: [NoteRecord, SessionRecord, UserRecord],
-  whitelist: ['sessions', 'notes', 'authStatus']
+  records: [NoteRecord, SessionRecord, UserRecord, GroupRecord],
+  whitelist: ['sessions', 'notes', 'authStatus', 'groups']
 }
 
 const persistOptions = {
   storage: localForage,
-  records: [NoteRecord, SessionRecord, UserRecord],
+  records: [NoteRecord, SessionRecord, UserRecord, GroupRecord],
   transforms: [immutableTransform(immutableTransformConfig)],
-  whitelist: ['sessions', 'notes', 'autoSubmit', 'authStatus']
+  whitelist: ['sessions', 'notes', 'autoSubmit', 'authStatus', 'groups']
 }
 
 const apiClient = new APIClient(config.apiRootUrl);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/store/groupRecord.js	Tue Aug 01 12:20:14 2017 +0200
@@ -0,0 +1,10 @@
+import Immutable from 'immutable';
+
+export default Immutable.Record({
+  name: '',
+
+  owner: '',
+  isPersonal: false,
+  description:''
+
+}, 'Group');
--- a/client/src/store/sessionRecord.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/store/sessionRecord.js	Tue Aug 01 12:20:14 2017 +0200
@@ -10,6 +10,7 @@
   date: '',
 
   group: null,
+  protocol: '',
 
   action: ActionEnum.NONE,
 
--- a/client/src/store/userRecord.js	Mon Jul 31 23:18:38 2017 +0200
+++ b/client/src/store/userRecord.js	Tue Aug 01 12:20:14 2017 +0200
@@ -6,6 +6,7 @@
 
   email: '',
   first_name: '',
-  last_name: ''
+  last_name: '',
+  default_group: null,
 
 }, 'User');
--- a/src/notes/api/views/core.py	Mon Jul 31 23:18:38 2017 +0200
+++ b/src/notes/api/views/core.py	Tue Aug 01 12:20:14 2017 +0200
@@ -29,6 +29,7 @@
     serializers = {
         'list': ListSessionSerializer,
         'retrieve':  DetailSessionSerializer,
+        'update': CreateSessionSerializer,
         'create':  CreateSessionSerializer,
     }
 
--- a/src/notes/api/views/sync.py	Mon Jul 31 23:18:38 2017 +0200
+++ b/src/notes/api/views/sync.py	Tue Aug 01 12:20:14 2017 +0200
@@ -36,7 +36,6 @@
         Process log entries
         '''
         log_entries = self.__filter_object(model, user, modified_since, client_id)
-        logger.debug("LOG ENTRies %r", list(log_entries))
 
         res = {}
         for log_entry in log_entries: