Skip to content

Commit

Permalink
Merge pull request freeCodeCamp#10102 from BerkeleyTrue/feature/add-r…
Browse files Browse the repository at this point in the history
…edux-night-mode

Feature(theme): add nightmode react logic
  • Loading branch information
raisedadead authored Aug 6, 2016
2 parents f326acb + 94c4c84 commit 8579c16
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 8 deletions.
4 changes: 3 additions & 1 deletion client/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import codeStorageSaga from './code-storage-saga';
import gitterSaga from './gitter-saga';
import mouseTrapSaga from './mouse-trap-saga';
import analyticsSaga from './analytics-saga';
import nightModeSaga from './night-mode-saga';

export default [
errSaga,
Expand All @@ -19,5 +20,6 @@ export default [
codeStorageSaga,
gitterSaga,
mouseTrapSaga,
analyticsSaga
analyticsSaga,
nightModeSaga
];
47 changes: 47 additions & 0 deletions client/sagas/night-mode-saga.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Observable } from 'rx';
import { postJSON$ } from '../../common/utils/ajax-stream';
import types from '../../common/app/redux/types';
import {
addThemeToBody,
updateTheme,
createErrorObservable
} from '../../common/app/redux/actions';

export default function nightModeSaga(
actions,
getState,
{ document: { body } }
) {
const toggleBodyClass = actions
.filter(({ type }) => types.addThemeToBody === type)
.doOnNext(({ payload: theme }) => {
if (theme === 'night') {
body.classList.add('night');
} else {
body.classList.remove('night');
}
})
.filter(() => false);
const toggle = actions
.filter(({ type }) => types.toggleNightMode === type);

const optimistic = toggle
.flatMap(() => {
const { app: { theme } } = getState();
const newTheme = !theme || theme === 'default' ? 'night' : 'default';
return Observable.of(
updateTheme(newTheme),
addThemeToBody(newTheme)
);
});

const ajax = toggle
.debounce(250)
.flatMapLatest(() => {
const { app: { theme, csrfToken: _csrf } } = getState();
return postJSON$('/update-my-theme', { _csrf, theme })
.catch(createErrorObservable);
});

return Observable.merge(optimistic, toggleBodyClass, ajax);
}
11 changes: 10 additions & 1 deletion common/app/redux/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,13 @@ export const closeHelpChat = createAction(
})
);

export const toggleNightMode = createAction(types.toggleNightMode);
export const toggleNightMode = createAction(
types.toggleNightMode,
// we use this function to avoid hanging onto the eventObject
// so that react can recycle it
() => null
);
// updateTheme(theme: /night|default/) => Action
export const updateTheme = createAction(types.updateTheme);
// addThemeToBody(theme: /night|default/) => Action
export const addThemeToBody = createAction(types.addThemeToBody);
11 changes: 8 additions & 3 deletions common/app/redux/fetch-user-saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {
updateThisUser,
updateCompletedChallenges,
createErrorObservable,
showSignIn
showSignIn,
updateTheme,
addThemeToBody
} from './actions';

const { fetchUser } = types;

export default function getUserSaga(action$, getState, { services }) {
return action$
.filter(action => action.type === fetchUser)
Expand All @@ -19,10 +20,14 @@ export default function getUserSaga(action$, getState, { services }) {
if (!entities || !result) {
return Observable.just(showSignIn());
}
const user = entities.user[result];
const isNightMode = user.theme === 'night';
return Observable.of(
addUser(entities),
updateCompletedChallenges(result),
updateThisUser(result),
updateCompletedChallenges(result)
isNightMode ? updateTheme(user.theme) : null,
isNightMode ? addThemeToBody(user.theme) : null
);
})
.catch(createErrorObservable);
Expand Down
7 changes: 6 additions & 1 deletion common/app/redux/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const initialState = {
windowHeight: 0,
navHeight: 0,
isMainChatOpen: false,
isHelpChatOpen: false
isHelpChatOpen: false,
theme: 'default'
};

export default handleActions(
Expand All @@ -29,6 +30,10 @@ export default handleActions(
...state,
lang: payload
}),
[types.updateTheme]: (state, { payload = 'default' }) => ({
...state,
theme: payload
}),
[types.showSignIn]: state => ({
...state,
shouldShowSignIn: true
Expand Down
8 changes: 6 additions & 2 deletions common/app/redux/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export default createTypes([
'updateMyCurrentChallenge',

'handleError',
'toggleNightMode',
// used to hit the server
'hardGoTo',
'delayedRedirect',
Expand All @@ -44,5 +43,10 @@ export default createTypes([

'openHelpChat',
'closeHelpChat',
'toggleHelpChat'
'toggleHelpChat',

// night mode
'toggleNightMode',
'updateTheme',
'addThemeToBody'
], 'app');
1 change: 1 addition & 0 deletions common/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ module.exports = function(User) {
.toPromise();
};

// deprecated. remove once live
User.remoteMethod(
'updateTheme',
{
Expand Down
25 changes: 25 additions & 0 deletions server/boot/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,26 @@ export default function settingsController(app) {
);
}

function updateMyTheme(req, res, next) {
req.checkBody('theme', 'Theme is invalid.').isLength({ min: 4 });
const { body: { theme } } = req;
const errors = req.validationErrors(true);
if (errors) {
return res.status(403).json({ errors });
}
if (req.user.theme === theme) {
return res.json({ msg: 'Theme already set' });
}
return req.user.updateTheme('' + theme)
.then(
data => res.json(data),
next
);
}

api.post(
'/toggle-lockdown',
ifNoUser401,
toggleUserFlag('isLocked')
);
api.post(
Expand Down Expand Up @@ -99,5 +117,12 @@ export default function settingsController(app) {
ifNoUser401,
updateMyCurrentChallenge
);

api.post(
'/update-my-theme',
ifNoUser401,
updateMyTheme
);

app.use(api);
}

0 comments on commit 8579c16

Please sign in to comment.