--- a/client/.env Fri Jul 28 19:40:35 2017 +0200
+++ b/client/.env Sun Jul 30 01:02:09 2017 +0200
@@ -2,3 +2,4 @@
REACT_APP_BASENAME =
REACT_APP_NETWORK_STATUS_INTERVAL = 2000
REACT_APP_NETWORK_STATUS_TIMEOUT = 2000
+REACT_APP_SYNC_INTERVAL = 20000
--- a/client/package.json Fri Jul 28 19:40:35 2017 +0200
+++ b/client/package.json Sun Jul 30 01:02:09 2017 +0200
@@ -22,7 +22,7 @@
"redux-persist": "^4.8.2",
"redux-persist-immutable": "^4.3.0",
"redux-persist-transform-immutable": "^4.3.0",
- "redux-saga": "^0.15.3",
+ "redux-saga": "^0.15.6",
"slate": "^0.20.1",
"uuid": "^3.0.1"
},
--- a/client/src/actions/syncActions.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/actions/syncActions.js Sun Jul 30 01:02:09 2017 +0200
@@ -12,3 +12,9 @@
type: types.SYNC_END_SYNC
}
}
+
+export const startSynchronize = () => {
+ return {
+ type: types.SYNC_START_SYNC
+ }
+}
--- a/client/src/components/Navbar.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/components/Navbar.js Sun Jul 30 01:02:09 2017 +0200
@@ -8,6 +8,7 @@
import * as authActions from '../actions/authActions';
import { forceSync } from '../actions/networkActions';
import { ActionEnum } from '../constants';
+import './Navbar.css';
const LoginNav = ({isAuthenticated, currentUser, history, authActions, onLogout}) => {
@@ -42,11 +43,22 @@
)
}
-const SyncButton = ({ onSyncClick, isSynchronizing }) => {
+const SyncButton = ({ onSyncClick, isSynchronizing, isSynchronized, id }) => {
+ const classnames = "material-icons"
+ + ((!isSynchronized)?" sync-button-not-synchronized":"")
+ + ((isSynchronizing)?" sync-button-synchronizing":"");
+ let title = "Synchronize";
+ let clickCb = onSyncClick;
+ if(isSynchronizing) {
+ title = "Synchronizing...";
+ clickCb = () => {};
+ } else if (!isSynchronized) {
+ title += ": not synchronized";
+ }
+
return (
- <NavItem onClick={onSyncClick}>
- Sync
- {isSynchronizing && <span className="material-icons"></span>}
+ <NavItem title={title} onClick={clickCb} id={id || null}>
+ <span className={classnames}></span>
</NavItem>
)
}
@@ -118,7 +130,7 @@
<NavItem onClick={this.onClickSessions} href="/sessions">Sessions</NavItem>
</Nav>
<Nav pullRight>
- <SyncButton onSyncClick={this.onSyncClick} isSynchronizing={this.props.isSynchronizing}/>
+ <SyncButton id='sync-button' onSyncClick={this.onSyncClick} isSynchronizing={this.props.isSynchronizing} isSynchronized={this.props.isSynchronized} />
<Online {...this.props} />
<LoginNav {...this.props} onLogout={this.onClickLogout} />
</Nav>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/Navbar.scss Sun Jul 30 01:02:09 2017 +0200
@@ -0,0 +1,7 @@
+.sync-button-synchronizing {
+ color: green;
+}
+
+.sync-button-not-synchronized {
+ color: orange;
+}
--- a/client/src/config.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/config.js Sun Jul 30 01:02:09 2017 +0200
@@ -4,5 +4,6 @@
apiRootUrl: process.env.REACT_APP_API_ROOT_URL || 'http://localhost:8000',
basename: process.env.REACT_APP_BASENAME || '',
networkStatusTimeout: parseInt(process.env.REACT_APP_NETWORK_STATUS_TIMEOUT, 10) || 2000,
- networkStatusInterval: parseInt(process.env.REACT_APP_NETWORK_STATUS_INTERVAL, 10) || 20000,
+ networkStatusInterval: parseInt(process.env.REACT_APP_NETWORK_STATUS_INTERVAL, 10) || 2000,
+ syncInterval: parseInt(process.env.REACT_APP_SYNC_INTERVAL, 10) || 20000,
}
--- a/client/src/constants/actionTypes.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/constants/actionTypes.js Sun Jul 30 01:02:09 2017 +0200
@@ -50,6 +50,7 @@
export const DATA_FETCH_SUCCESS = 'DATA_FETCH_SUCCESS';
export const SYNC_DO_SYNC = 'SYNC_DO_SYNC';
+export const SYNC_START_SYNC = 'SYNC_START_SYNC';
export const SYNC_END_SYNC = 'SYNC_END_SYNC';
export const SYNC_SET_LAST_SYNC = 'SYNC_SET_LAST_SYNC';
export const SYNC_RESET_ALL = 'SYNC_RESET_ALL';
--- a/client/src/reducers/authReducer.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/reducers/authReducer.js Sun Jul 30 01:02:09 2017 +0200
@@ -103,7 +103,7 @@
export const groups = (state = Immutable.List([]), action) => {
switch (action.type) {
case types.GROUP_LOAD_SUCCESS:
- return Immutable.List(action.groups);
+ return Immutable.List(action.groups.results);
case types.GROUP_CREATE_SUCCESS:
return state.push(action.group);
default:
--- a/client/src/reducers/syncReducer.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/reducers/syncReducer.js Sun Jul 30 01:02:09 2017 +0200
@@ -13,7 +13,7 @@
export const isSynchronizing = (state = false, action) => {
switch (action.type) {
- case types.SYNC_DO_SYNC:
+ case types.SYNC_START_SYNC:
return true;
case types.SYNC_END_SYNC:
return false;
--- a/client/src/sagas/networkSaga.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/sagas/networkSaga.js Sun Jul 30 01:02:09 2017 +0200
@@ -4,7 +4,7 @@
import * as persistConstants from 'redux-persist/constants';
import jwt_decode from 'jwt-decode';
import moment from 'moment';
-import { delay } from './utils';
+import { delay } from 'redux-saga';
import { setOnlineStatus } from '../actions/networkActions';
import { getToken } from './selectors';
@@ -94,11 +94,11 @@
yield take(persistConstants.REHYDRATE);
while (true) {
- yield race([
- all([call(pollData, context), call(callDelay, context)]),
- take(types.AUTH_LOGOUT),
- take(types.AUTH_LOGIN_SUCCESS)
- ]);
+ yield race({
+ poll: all([call(pollData, context), call(callDelay, context)]),
+ logout: take(types.AUTH_LOGOUT),
+ login: take(types.AUTH_LOGIN_SUCCESS)
+ });
}
}
--- a/client/src/sagas/selectors.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/sagas/selectors.js Sun Jul 30 01:02:09 2017 +0200
@@ -4,6 +4,8 @@
export const getLastSync = state => state.getIn(['authStatus', 'lastSync']) || 0
+export const getOnline = state => state.getIn(["status", 'online'])
+
export const getToken = state => state.getIn(['authStatus','token'])
const getSessionMapSelector = actionVal => state =>
--- a/client/src/sagas/syncSaga.js Fri Jul 28 19:40:35 2017 +0200
+++ b/client/src/sagas/syncSaga.js Sun Jul 30 01:02:09 2017 +0200
@@ -1,19 +1,25 @@
-import { put, take, all, select } from 'redux-saga/effects'
+import { put, take, all, select, spawn, race, call} from 'redux-saga/effects'
+import { delay } from 'redux-saga';
import * as types from '../constants/actionTypes';
-import { getLastSync } from './selectors';
+import { getLastSync, getOnline } from './selectors';
import moment from 'moment';
-import { endSynchronize, updateLastSync } from '../actions/syncActions';
+import { startSynchronize, endSynchronize, updateLastSync } from '../actions/syncActions';
+import { forceSync } from '../actions/networkActions';
import SessionSynchronizer from './SessionSyncronizer';
import NoteSynchronizer from './NoteSyncronizer';
+import config from '../config';
+function* doSync(context) {
-function* watchSync(context) {
- while (true) {
- yield take(types.SYNC_DO_SYNC);
+ const online = yield select(getOnline);
+ if(!online) {
+ yield put(endSynchronize());
+ return;
+ }
+
const lastSync = yield select(getLastSync);
-
+ yield put(startSynchronize());
- //const sessions = yield context.client.get('/api/notes/sessions/', {modified_since: lastSync});
const nextLastSync = moment().unix()
// TODO: manage errors
@@ -30,12 +36,39 @@
} finally {
yield put(endSynchronize());
}
+
+}
+
+function* watchDoSync(context) {
+ while (true) {
+ yield take(types.SYNC_DO_SYNC);
+ yield spawn(doSync, context);
+ yield take(types.SYNC_END_SYNC);
+ }
+}
+
+function* delayPutDoSync() {
+ yield put(forceSync());
+ yield call(delay, config.syncInterval);
+}
+
+function* loopDoSync() {
+ while(true) {
+ const online = yield select(getOnline);
+ if(!online) {
+ yield take(types.STATUS_ONLINE);
+ }
+ yield race({
+ delaySync: call(delayPutDoSync),
+ offline: take(types.STATUS_OFFLINE)
+ });
}
}
//--- The root saga
export default function* rootSaga(context) {
yield all([
- watchSync(context)
+ watchDoSync(context),
+ loopDoSync()
]);
}
--- a/client/src/sagas/utils.js Fri Jul 28 19:40:35 2017 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-// Utility function to delay effects
-export const delay = function(millis) {
- const promise = new Promise(resolve => {
- setTimeout(() => resolve(true), millis)
- });
- return promise;
-}
--- a/client/yarn.lock Fri Jul 28 19:40:35 2017 +0200
+++ b/client/yarn.lock Sun Jul 30 01:02:09 2017 +0200
@@ -5549,9 +5549,9 @@
lodash "^4.17.4"
lodash-es "^4.17.4"
-redux-saga@^0.15.3:
- version "0.15.3"
- resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.15.3.tgz#be2b86b4ad46bf0d84fcfcb0ca96cfc33db91acb"
+redux-saga@^0.15.6:
+ version "0.15.6"
+ resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-0.15.6.tgz#8638dc522de6c6c0a496fe8b2b5466287ac2dc4d"
redux@^3.6.0:
version "3.6.0"
--- a/src/notes/api/views/auth.py Fri Jul 28 19:40:35 2017 +0200
+++ b/src/notes/api/views/auth.py Sun Jul 30 01:02:09 2017 +0200
@@ -15,7 +15,7 @@
lookup_field = 'name'
def get_queryset(self):
- return Group.objects.all()
+ return Group.objects.all().order_by('name')
serializers = {
'create': WriteGroupSerializer,