* fix(files): Decouple files from challenges * feat(server/react): Remove action logger use redux remote devtools instead! * feat(Challenges): Disable js on edit, enable on execute * feat(Challenge/Preview): Show message when js is disabled * refactor(frameEpic): Reduce code by using lodash * feat(frameEpic): Disable js in preview by state * feat(frameEpic): Colocate epic in Challenges/redux * refactor(ExecuteChallengeEpic): CoLocated with Challenges * refactor(executeChallengesEpic): Separate tests from main logic * feat(Challenge/Preview): Update main on edit * feat(frameEpuc): Replace frame on edit/execute This allows for sandbox to work properly * fix(Challenges/Utils): Require utisl * revert(frameEpic): Hoist function to mount code in frame * fix(frameEpic): Ensure new frame is given classname * feat(executeChallenge): Update main on code unlocked * fix(frameEpic): Filter out empty test message * fix(Challenge/Preview): Remove unnessary quote in classname * feat(codeStorageEpic): Separate localstorage from solutions loading * fix(fetchUser): Merge user actions into one prefer many effects from one action over one action to one effect * fix(themes): Centralize theme utils and defs * fix(entities.user): Fix user reducer namespacing * feat(frame): Refactor frameEpic to util * feat(Challenges.redux): Should not attempt to update main from storage * fix(loadPreviousChallengeEpic): Refactor for RFR * fix(Challenges.Modern): Show preview plane
60 lines
1.5 KiB
JavaScript
60 lines
1.5 KiB
JavaScript
import _ from 'lodash';
|
|
import { Observable } from 'rx';
|
|
import { ofType } from 'redux-epic';
|
|
import store from 'store';
|
|
|
|
import { themes } from '../../common/utils/themes.js';
|
|
import { postJSON$ } from '../../common/utils/ajax-stream.js';
|
|
import {
|
|
types,
|
|
|
|
postThemeComplete,
|
|
createErrorObservable,
|
|
|
|
themeSelector,
|
|
usernameSelector,
|
|
csrfSelector
|
|
} from '../../common/app/redux';
|
|
|
|
function persistTheme(theme) {
|
|
store.set('fcc-theme', theme);
|
|
}
|
|
|
|
export default function nightModeSaga(
|
|
actions,
|
|
{ getState },
|
|
{ document: { body } }
|
|
) {
|
|
const toggleBodyClass = actions
|
|
::ofType(
|
|
types.fetchUser.complete,
|
|
types.toggleNightMode,
|
|
types.postThemeComplete
|
|
)
|
|
.map(_.flow(getState, themeSelector))
|
|
// catch existing night mode users
|
|
.do(persistTheme)
|
|
.do(theme => {
|
|
if (theme === themes.night) {
|
|
body.classList.add(themes.night);
|
|
} else {
|
|
body.classList.remove(themes.night);
|
|
}
|
|
})
|
|
.ignoreElements();
|
|
|
|
const postThemeEpic = actions::ofType(types.toggleNightMode)
|
|
.debounce(250)
|
|
.flatMapLatest(() => {
|
|
const _csrf = csrfSelector(getState());
|
|
const theme = themeSelector(getState());
|
|
const username = usernameSelector(getState());
|
|
return postJSON$('/update-my-theme', { _csrf, theme })
|
|
.pluck('updatedTo')
|
|
.map(theme => postThemeComplete(username, theme))
|
|
.catch(createErrorObservable);
|
|
});
|
|
|
|
return Observable.merge(toggleBodyClass, postThemeEpic);
|
|
}
|