93 lines
2.9 KiB
JavaScript
93 lines
2.9 KiB
JavaScript
import { Observable } from 'rx';
|
|
import debug from 'debug';
|
|
import { push } from 'react-router-redux';
|
|
|
|
import types from './types';
|
|
import {
|
|
updateMyCurrentChallenge,
|
|
createErrorObservable
|
|
} from './actions';
|
|
import {
|
|
userSelector,
|
|
firstChallengeSelector
|
|
} from './selectors';
|
|
import { updateCurrentChallenge } from '../routes/challenges/redux/actions';
|
|
import getActionsOfType from '../../utils/get-actions-of-type';
|
|
import combineSagas from '../../utils/combine-sagas';
|
|
import { postJSON$ } from '../../utils/ajax-stream';
|
|
|
|
const log = debug('fcc:app/redux/load-current-challenge-saga');
|
|
export function updateMyCurrentChallengeSaga(actions, getState) {
|
|
const updateChallenge$ = getActionsOfType(
|
|
actions,
|
|
updateCurrentChallenge.toString()
|
|
)
|
|
.map(({ payload: { id } }) => id)
|
|
.filter(() => {
|
|
const { app: { user: username } } = getState();
|
|
return !!username;
|
|
})
|
|
.distinctUntilChanged();
|
|
const optimistic = updateChallenge$.map(id => {
|
|
const { app: { user: username } } = getState();
|
|
return updateMyCurrentChallenge(username, id);
|
|
});
|
|
const ajaxUpdate = updateChallenge$
|
|
.debounce(250)
|
|
.flatMapLatest(currentChallengeId => {
|
|
const { app: { csrfToken: _csrf } } = getState();
|
|
return postJSON$(
|
|
'/update-my-current-challenge',
|
|
{ _csrf, currentChallengeId }
|
|
)
|
|
.map(({ message }) => log(message))
|
|
.catch(createErrorObservable);
|
|
});
|
|
return Observable.merge(optimistic, ajaxUpdate);
|
|
}
|
|
|
|
export function loadCurrentChallengeSaga(actions, getState) {
|
|
return getActionsOfType(actions, types.loadCurrentChallenge)
|
|
.flatMap(() => {
|
|
let finalChallenge;
|
|
const state = getState();
|
|
const {
|
|
entities: { challenge: challengeMap, challengeIdToName },
|
|
challengesApp: { id: currentlyLoadedChallengeId },
|
|
locationBeforeTransition: { pathname } = {}
|
|
} = state;
|
|
const firstChallenge = firstChallengeSelector(state);
|
|
const { user: { currentChallengeId } } = userSelector(state);
|
|
const isOnAChallenge = (/^\/[^\/]{2,6}\/challenges/).test(pathname);
|
|
|
|
if (!currentChallengeId) {
|
|
finalChallenge = firstChallenge;
|
|
} else {
|
|
finalChallenge = challengeMap[
|
|
challengeIdToName[ currentChallengeId ]
|
|
];
|
|
}
|
|
if (
|
|
// data might not be there yet, ignore for now
|
|
!finalChallenge ||
|
|
// are we already on that challenge?
|
|
(isOnAChallenge && finalChallenge.id === currentlyLoadedChallengeId)
|
|
) {
|
|
// don't reload if the challenge is already loaded.
|
|
// This may change to toast to avoid user confusion
|
|
return Observable.empty();
|
|
}
|
|
return Observable.of(
|
|
updateCurrentChallenge(finalChallenge),
|
|
push(
|
|
`/challenges/${finalChallenge.block}/${finalChallenge.dashedName}`
|
|
)
|
|
);
|
|
});
|
|
}
|
|
|
|
export default combineSagas(
|
|
updateMyCurrentChallengeSaga,
|
|
loadCurrentChallengeSaga
|
|
);
|