# HG changeset patch # User ymh # Date 1501369329 -7200 # Node ID 78246db1cbac5a40334eda5e9baa0f9ad70f3ac9 # Parent d48946d164c61aef7744b967826412718fd405ef make synchronization recurent, improve synchronization status display diff -r d48946d164c6 -r 78246db1cbac client/.env --- 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 diff -r d48946d164c6 -r 78246db1cbac client/package.json --- 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" }, diff -r d48946d164c6 -r 78246db1cbac client/src/actions/syncActions.js --- 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 + } +} diff -r d48946d164c6 -r 78246db1cbac client/src/components/Navbar.js --- 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 ( - - Sync - {isSynchronizing && } + + ) } @@ -118,7 +130,7 @@ Sessions diff -r d48946d164c6 -r 78246db1cbac client/src/components/Navbar.scss --- /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; +} diff -r d48946d164c6 -r 78246db1cbac client/src/config.js --- 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, } diff -r d48946d164c6 -r 78246db1cbac client/src/constants/actionTypes.js --- 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'; diff -r d48946d164c6 -r 78246db1cbac client/src/reducers/authReducer.js --- 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: diff -r d48946d164c6 -r 78246db1cbac client/src/reducers/syncReducer.js --- 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; diff -r d48946d164c6 -r 78246db1cbac client/src/sagas/networkSaga.js --- 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) + }); } } diff -r d48946d164c6 -r 78246db1cbac client/src/sagas/selectors.js --- 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 => diff -r d48946d164c6 -r 78246db1cbac client/src/sagas/syncSaga.js --- 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() ]); } diff -r d48946d164c6 -r 78246db1cbac client/src/sagas/utils.js --- 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; -} diff -r d48946d164c6 -r 78246db1cbac client/yarn.lock --- 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" diff -r d48946d164c6 -r 78246db1cbac src/notes/api/views/auth.py --- 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,