2016-07-16 10:38:06 -07:00
|
|
|
import { Observable } from 'rx';
|
2017-07-31 20:04:01 -07:00
|
|
|
import { combineEpics, ofType } from 'redux-epic';
|
2018-02-16 23:18:53 +00:00
|
|
|
import { pick } from 'lodash';
|
|
|
|
import {
|
|
|
|
types,
|
2018-05-15 14:56:26 +01:00
|
|
|
refetchCompletedChallenges,
|
2018-02-16 23:18:53 +00:00
|
|
|
updateUserBackendComplete,
|
2018-05-20 04:07:41 +01:00
|
|
|
updateMyPortfolioComplete,
|
|
|
|
updateMyProfileUIComplete
|
2018-02-16 23:18:53 +00:00
|
|
|
} from './';
|
2017-07-31 20:04:01 -07:00
|
|
|
import { makeToast } from '../../../Toasts/redux';
|
|
|
|
import {
|
|
|
|
doActionOnError,
|
2018-02-16 23:18:53 +00:00
|
|
|
usernameSelector,
|
|
|
|
userSelector,
|
2018-05-15 06:12:05 +01:00
|
|
|
createErrorObservable
|
2017-07-31 20:04:01 -07:00
|
|
|
} from '../../../redux';
|
2016-07-19 16:36:34 -07:00
|
|
|
import {
|
|
|
|
updateUserEmail,
|
2018-02-16 23:18:53 +00:00
|
|
|
updateMultipleUserFlags,
|
|
|
|
regresPortfolio,
|
2018-05-20 04:07:41 +01:00
|
|
|
optoUpdatePortfolio,
|
|
|
|
updateLocalProfileUI
|
2017-07-31 20:04:01 -07:00
|
|
|
} from '../../../entities';
|
|
|
|
|
2016-07-16 10:38:06 -07:00
|
|
|
import { postJSON$ } from '../../../../utils/ajax-stream';
|
|
|
|
|
2018-02-16 23:18:53 +00:00
|
|
|
const endpoints = {
|
|
|
|
email: '/update-my-email',
|
|
|
|
projects: '/update-my-projects',
|
|
|
|
username: '/update-my-username'
|
2016-07-16 10:38:06 -07:00
|
|
|
};
|
2016-07-19 16:36:34 -07:00
|
|
|
|
2018-02-16 23:18:53 +00:00
|
|
|
function backendUserUpdateEpic(actions$, { getState }) {
|
|
|
|
const start = actions$::ofType(types.updateUserBackend.start);
|
|
|
|
const server = start
|
|
|
|
.flatMap(({ payload }) => {
|
|
|
|
const userMap = userSelector(getState());
|
|
|
|
const { username } = userMap;
|
|
|
|
const flagsToCheck = Object.keys(payload);
|
|
|
|
const valuesToCheck = pick(userMap, flagsToCheck);
|
|
|
|
const oldValues = {
|
|
|
|
...flagsToCheck.reduce((accu, current) => ({ ...accu, [current]: '' })),
|
|
|
|
...valuesToCheck
|
|
|
|
};
|
|
|
|
const valuesToUpdate = flagsToCheck.reduce((accu, current) => {
|
|
|
|
if (payload[current] !== valuesToCheck[current]) {
|
|
|
|
return { ...accu, [current]: payload[current] };
|
|
|
|
}
|
|
|
|
return accu;
|
|
|
|
}, {});
|
|
|
|
if (!Object.keys(valuesToUpdate).length) {
|
|
|
|
return Observable.of(
|
|
|
|
makeToast({ message: 'No changes in settings detected' })
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const {
|
|
|
|
app: { csrfToken: _csrf }
|
|
|
|
} = getState();
|
|
|
|
let body = { _csrf };
|
|
|
|
let endpoint = '/update-flags';
|
|
|
|
const updateKeys = Object.keys(valuesToUpdate);
|
|
|
|
if (updateKeys.length === 1 && updateKeys[0] in endpoints) {
|
|
|
|
// there is a specific route for this update
|
|
|
|
const flag = updateKeys[0];
|
|
|
|
endpoint = endpoints[flag];
|
|
|
|
body = {
|
|
|
|
...body,
|
|
|
|
[flag]: valuesToUpdate[flag]
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
body = {
|
|
|
|
...body,
|
|
|
|
values: valuesToUpdate
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return postJSON$(endpoint, body)
|
|
|
|
.map(updateUserBackendComplete)
|
|
|
|
.catch(
|
|
|
|
doActionOnError(
|
|
|
|
() => updateMultipleUserFlags({ username, flags: oldValues })
|
|
|
|
)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
const optimistic = start
|
|
|
|
.flatMap(({ payload }) => {
|
|
|
|
const username = usernameSelector(getState());
|
|
|
|
return Observable.of(
|
|
|
|
updateMultipleUserFlags({ username, flags: payload })
|
|
|
|
);
|
|
|
|
});
|
|
|
|
const complete = actions$::ofType(types.updateUserBackend.complete)
|
|
|
|
.flatMap(({ payload: { message } }) => Observable.if(
|
|
|
|
() => message.includes('project'),
|
2018-05-15 14:56:26 +01:00
|
|
|
Observable.of(refetchCompletedChallenges(), makeToast({ message })),
|
2018-02-16 23:18:53 +00:00
|
|
|
Observable.of(makeToast({ message }))
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
return Observable.merge(server, optimistic, complete);
|
|
|
|
}
|
|
|
|
|
2018-05-15 14:56:26 +01:00
|
|
|
function refetchCompletedChallengesEpic(actions$, { getState }) {
|
|
|
|
return actions$::ofType(types.refetchCompletedChallenges.start)
|
2018-02-16 23:18:53 +00:00
|
|
|
.flatMap(() => {
|
|
|
|
const {
|
|
|
|
app: { csrfToken: _csrf }
|
|
|
|
} = getState();
|
|
|
|
const username = usernameSelector(getState());
|
2018-05-15 14:56:26 +01:00
|
|
|
return postJSON$('/refetch-user-completed-challenges', { _csrf })
|
|
|
|
.map(({ completedChallenges }) =>
|
|
|
|
updateMultipleUserFlags({ username, flags: { completedChallenges } })
|
2018-02-16 23:18:53 +00:00
|
|
|
)
|
|
|
|
.catch(createErrorObservable);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateMyPortfolioEpic(actions$, { getState }) {
|
|
|
|
const edit = actions$::ofType(types.updateMyPortfolio.start);
|
|
|
|
const remove = actions$::ofType(types.deletePortfolio.start);
|
|
|
|
const serverEdit = edit
|
|
|
|
.flatMap(({ payload }) => {
|
|
|
|
const { id } = payload;
|
|
|
|
const {
|
|
|
|
app: { csrfToken: _csrf, username }
|
|
|
|
} = getState();
|
|
|
|
return postJSON$('/update-my-portfolio', { _csrf, portfolio: payload })
|
|
|
|
.map(updateMyPortfolioComplete)
|
|
|
|
.catch(doActionOnError(() => regresPortfolio({ username, id })));
|
|
|
|
});
|
|
|
|
const optimisticEdit = edit
|
|
|
|
.map(({ payload }) => {
|
|
|
|
const username = usernameSelector(getState());
|
|
|
|
return optoUpdatePortfolio({ username, portfolio: payload });
|
|
|
|
});
|
|
|
|
const complete = actions$::ofType(types.updateMyPortfolio.complete)
|
|
|
|
.flatMap(({ payload: { message } }) =>
|
|
|
|
Observable.of(makeToast({ message }))
|
|
|
|
);
|
|
|
|
|
|
|
|
const serverRemove = remove
|
|
|
|
.flatMap(({ payload: { portfolio } }) => {
|
|
|
|
const {
|
|
|
|
app: { csrfToken: _csrf }
|
|
|
|
} = getState();
|
|
|
|
return postJSON$('/update-my-portfolio', { _csrf, portfolio })
|
|
|
|
.map(updateMyPortfolioComplete)
|
|
|
|
.catch(
|
|
|
|
doActionOnError(
|
|
|
|
() => makeToast({
|
|
|
|
message: 'Something went wrong removing a portfolio item.'
|
|
|
|
})
|
|
|
|
)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
const optimisticRemove = remove
|
|
|
|
.flatMap(({ payload: { portfolio: { id } } }) => {
|
|
|
|
const username = usernameSelector(getState());
|
|
|
|
return Observable.of(regresPortfolio({ username, id }));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Observable.merge(
|
|
|
|
serverEdit,
|
|
|
|
optimisticEdit,
|
|
|
|
complete,
|
|
|
|
serverRemove,
|
|
|
|
optimisticRemove
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateUserEmailEpic(actions, { getState }) {
|
2017-07-31 20:04:01 -07:00
|
|
|
return actions::ofType(types.updateMyEmail)
|
2016-07-19 16:36:34 -07:00
|
|
|
.flatMap(({ payload: email }) => {
|
|
|
|
const {
|
|
|
|
app: { user: username, csrfToken: _csrf },
|
|
|
|
entities: { user: userMap }
|
|
|
|
} = getState();
|
|
|
|
const { email: oldEmail } = userMap[username] || {};
|
|
|
|
const body = { _csrf, email };
|
2017-07-31 20:04:01 -07:00
|
|
|
const optimisticUpdate = Observable.just(
|
2016-07-19 16:36:34 -07:00
|
|
|
updateUserEmail(username, email)
|
|
|
|
);
|
2017-07-31 20:04:01 -07:00
|
|
|
const ajaxUpdate = postJSON$('/update-my-email', body)
|
2016-07-19 16:36:34 -07:00
|
|
|
.catch(doActionOnError(() => oldEmail ?
|
2017-12-05 11:55:41 +05:30
|
|
|
updateUserEmail(username, oldEmail) :
|
2016-07-19 16:36:34 -07:00
|
|
|
null
|
2017-12-07 16:13:19 -08:00
|
|
|
))
|
|
|
|
.filter(Boolean);
|
2017-07-31 20:04:01 -07:00
|
|
|
return Observable.merge(optimisticUpdate, ajaxUpdate);
|
2016-07-19 16:36:34 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-20 04:07:41 +01:00
|
|
|
function updateMyProfileUIEpic(action$, { getState }) {
|
|
|
|
const toggle = action$::ofType(types.updateMyProfileUI.start);
|
|
|
|
|
|
|
|
const server = toggle.flatMap(({payload: { profileUI }}) => {
|
|
|
|
const state = getState();
|
|
|
|
const { csrfToken: _csrf } = state.app;
|
|
|
|
const username = usernameSelector(state);
|
|
|
|
const oldUI = { ...userSelector(state).profileUI };
|
|
|
|
return postJSON$('/update-my-profile-ui', { _csrf, profileUI })
|
|
|
|
.map(updateMyProfileUIComplete)
|
|
|
|
.catch(
|
|
|
|
doActionOnError(
|
|
|
|
() => Observable.of(
|
|
|
|
makeToast({
|
|
|
|
message:
|
|
|
|
'Something went wrong saving your privacy settings, ' +
|
|
|
|
'please try again.'
|
|
|
|
}),
|
|
|
|
updateLocalProfileUI({username, profileUI: oldUI })
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
const optimistic = toggle.flatMap(({payload: { profileUI }}) => {
|
|
|
|
const username = usernameSelector(getState());
|
|
|
|
return Observable.of(updateLocalProfileUI({username, profileUI}));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Observable.merge(server, optimistic);
|
|
|
|
}
|
|
|
|
|
2017-07-31 20:04:01 -07:00
|
|
|
export default combineEpics(
|
2018-02-16 23:18:53 +00:00
|
|
|
backendUserUpdateEpic,
|
2018-05-15 14:56:26 +01:00
|
|
|
refetchCompletedChallengesEpic,
|
2018-02-16 23:18:53 +00:00
|
|
|
updateMyPortfolioEpic,
|
2018-05-20 04:07:41 +01:00
|
|
|
updateUserEmailEpic,
|
|
|
|
updateMyProfileUIEpic
|
2016-07-19 16:36:34 -07:00
|
|
|
);
|