Feature(challenges): save users current challenge to db
This allows us to automatically load their current challenge
This commit is contained in:
@ -61,6 +61,10 @@ export const addUser = createAction(
|
|||||||
export const updateThisUser = createAction(types.updateThisUser);
|
export const updateThisUser = createAction(types.updateThisUser);
|
||||||
export const showSignIn = createAction(types.showSignIn);
|
export const showSignIn = createAction(types.showSignIn);
|
||||||
export const loadCurrentChallenge = createAction(types.loadCurrentChallenge);
|
export const loadCurrentChallenge = createAction(types.loadCurrentChallenge);
|
||||||
|
export const updateMyCurrentChallenge = createAction(
|
||||||
|
types.updateMyCurrentChallenge,
|
||||||
|
(username, currentChallengeId) => ({ username, currentChallengeId })
|
||||||
|
);
|
||||||
|
|
||||||
// updateUserPoints(username: String, points: Number) => Action
|
// updateUserPoints(username: String, points: Number) => Action
|
||||||
export const updateUserPoints = createAction(
|
export const updateUserPoints = createAction(
|
||||||
|
@ -85,5 +85,18 @@ export default function entities(state = initialState, action) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === types.updateMyCurrentChallenge) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user: {
|
||||||
|
...state.user,
|
||||||
|
[username]: {
|
||||||
|
...state.user[username],
|
||||||
|
currentChallengeId: action.payload.currentChallengeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,51 @@
|
|||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
|
import debug from 'debug';
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
|
|
||||||
import types from './types';
|
import types from './types';
|
||||||
|
import {
|
||||||
|
updateMyCurrentChallenge,
|
||||||
|
createErrorObservable
|
||||||
|
} from './actions';
|
||||||
import {
|
import {
|
||||||
userSelector,
|
userSelector,
|
||||||
firstChallengeSelector
|
firstChallengeSelector
|
||||||
} from './selectors';
|
} from './selectors';
|
||||||
import getActionsOfType from '../../utils/get-actions-of-type';
|
|
||||||
import { updateCurrentChallenge } from '../routes/challenges/redux/actions';
|
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';
|
||||||
|
|
||||||
export default function loadCurrentChallengeSaga(actions, getState) {
|
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;
|
||||||
|
});
|
||||||
|
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)
|
return getActionsOfType(actions, types.loadCurrentChallenge)
|
||||||
.flatMap(() => {
|
.flatMap(() => {
|
||||||
let finalChallenge;
|
let finalChallenge;
|
||||||
@ -40,3 +76,8 @@ export default function loadCurrentChallengeSaga(actions, getState) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default combineSagas(
|
||||||
|
updateMyCurrentChallengeSaga,
|
||||||
|
loadCurrentChallengeSaga
|
||||||
|
);
|
||||||
|
@ -15,6 +15,7 @@ export default createTypes([
|
|||||||
'updateCompletedChallenges',
|
'updateCompletedChallenges',
|
||||||
'showSignIn',
|
'showSignIn',
|
||||||
'loadCurrentChallenge',
|
'loadCurrentChallenge',
|
||||||
|
'updateMyCurrentChallenge',
|
||||||
|
|
||||||
'handleError',
|
'handleError',
|
||||||
'toggleNightMode',
|
'toggleNightMode',
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ifNoUser401 } from '../utils/middleware';
|
import { ifNoUser401 } from '../utils/middleware';
|
||||||
|
import { isMongoId } from 'validator';
|
||||||
import supportedLanguages from '../../common/utils/supported-languages.js';
|
import supportedLanguages from '../../common/utils/supported-languages.js';
|
||||||
|
|
||||||
export default function settingsController(app) {
|
export default function settingsController(app) {
|
||||||
@ -49,6 +50,20 @@ export default function settingsController(app) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateMyCurrentChallenge(req, res, next) {
|
||||||
|
const { user, body: { currentChallengeId } } = req;
|
||||||
|
if (!isMongoId('' + currentChallengeId)) {
|
||||||
|
return next(new Error(`${currentChallengeId} is not a valid ObjectId`));
|
||||||
|
}
|
||||||
|
return user.update$({ currentChallengeId }).subscribe(
|
||||||
|
() => res.json({
|
||||||
|
message:
|
||||||
|
`your current challenge has been updated to ${currentChallengeId}`
|
||||||
|
}),
|
||||||
|
next
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
api.post(
|
api.post(
|
||||||
'/toggle-lockdown',
|
'/toggle-lockdown',
|
||||||
toggleUserFlag('isLocked')
|
toggleUserFlag('isLocked')
|
||||||
@ -78,5 +93,11 @@ export default function settingsController(app) {
|
|||||||
ifNoUser401,
|
ifNoUser401,
|
||||||
updateMyLang
|
updateMyLang
|
||||||
);
|
);
|
||||||
|
|
||||||
|
api.post(
|
||||||
|
'/update-my-current-challenge',
|
||||||
|
ifNoUser401,
|
||||||
|
updateMyCurrentChallenge
|
||||||
|
);
|
||||||
app.use(api);
|
app.use(api);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ const publicUserProps = [
|
|||||||
'sendNotificationEmail',
|
'sendNotificationEmail',
|
||||||
'sendQuincyEmail',
|
'sendQuincyEmail',
|
||||||
|
|
||||||
'currentChallenge',
|
'currentChallengeId',
|
||||||
'challengeMap'
|
'challengeMap'
|
||||||
];
|
];
|
||||||
const log = debug('fcc:services:user');
|
const log = debug('fcc:services:user');
|
||||||
|
Reference in New Issue
Block a user