diff --git a/src/actions/profile.js b/src/actions/profile.js index b7538b3..a2a95c7 100644 --- a/src/actions/profile.js +++ b/src/actions/profile.js @@ -6,6 +6,8 @@ export const FETCH_PROFILE = new AsyncActionType('PROFILE', 'FETCH_PROFILE'); export const SAVE_PROFILE = new AsyncActionType('PROFILE', 'SAVE_PROFILE'); export const SAVE_PROFILE_PHOTO = new AsyncActionType('PROFILE', 'SAVE_PROFILE_PHOTO'); export const DELETE_PROFILE_PHOTO = new AsyncActionType('PROFILE', 'DELETE_PROFILE_PHOTO'); +export const FETCH_PREFERENCES = new AsyncActionType('PROFILE', 'FETCH_PREFERENCES'); +export const SAVE_PREFERENCES = new AsyncActionType('PROFILE', 'SAVE_PREFERENCES'); export const openEditableField = fieldName => ({ type: EDITABLE_FIELD_OPEN, @@ -114,3 +116,50 @@ export const deleteProfilePhoto = username => ({ username, }, }); + + +export const fetchPreferencesBegin = () => ({ + type: FETCH_PREFERENCES.BEGIN, +}); + +export const fetchPreferencesSuccess = preferences => ({ + type: FETCH_PREFERENCES.SUCCESS, + preferences, +}); + +export const fetchPreferencesFailure = error => ({ + type: FETCH_PREFERENCES.FAILURE, + payload: { error }, +}); + +export const fetchPreferencesReset = () => ({ + type: FETCH_PREFERENCES.RESET, +}); + +export const fetchPreferences = username => ({ + type: FETCH_PREFERENCES.BASE, + payload: { username }, +}); + + +export const savePreferencesBegin = () => ({ + type: SAVE_PREFERENCES.BEGIN, +}); + +export const savePreferencesSuccess = () => ({ + type: SAVE_PREFERENCES.SUCCESS, +}); + +export const savePreferencesFailure = error => ({ + type: SAVE_PREFERENCES.FAILURE, + payload: { error }, +}); + +export const savePreferencesReset = () => ({ + type: SAVE_PREFERENCES.RESET, +}); + +export const savePreferences = (username, preferences) => ({ + type: SAVE_PREFERENCES.BASE, + payload: { username, preferences }, +}); diff --git a/src/data/apiClient.js b/src/data/apiClient.js index 812f374..1c3c327 100644 --- a/src/data/apiClient.js +++ b/src/data/apiClient.js @@ -16,8 +16,8 @@ const apiClient = getAuthenticatedAPIClient({ }); -export function getUserPreference(username, preferenceKey) { - const url = `${lmsBaseUrl}/api/user/v1/preferences/${username}/${preferenceKey}`; +export function getPreferences(username) { + const url = `${lmsBaseUrl}/api/user/v1/preferences/${username}`; return new Promise((resolve, reject) => { apiClient.get(url) @@ -30,5 +30,29 @@ export function getUserPreference(username, preferenceKey) { }); } +export function savePreferences(username, preferences) { + const url = `${lmsBaseUrl}/api/user/v1/preferences/${username}`; + // const data = { + // "account_privacy": "custom", + // "visibility.bio": "all_users", + // "visibility.country": "private", + // "visibility.language_proficiencies": "all_users" + // }; + + return new Promise((resolve, reject) => { + apiClient.patch( + url, + preferences, + { headers: { 'Content-Type': 'application/merge-patch+json' } }, + ) + .then((response) => { + resolve(response.data); + }) + .catch((error) => { + reject(error); + }); + }); +} + export default apiClient; diff --git a/src/data/reducers/ProfilePageReducer.js b/src/data/reducers/ProfilePageReducer.js index 5bcf6c5..dcda883 100644 --- a/src/data/reducers/ProfilePageReducer.js +++ b/src/data/reducers/ProfilePageReducer.js @@ -5,6 +5,8 @@ import { EDITABLE_FIELD_CLOSE, EDITABLE_FIELD_OPEN, FETCH_PROFILE, + FETCH_PREFERENCES, + SAVE_PREFERENCES, } from '../../actions/profile'; const initialState = { @@ -13,6 +15,8 @@ const initialState = { savePhotoState: null, currentlyEditingField: null, profile: {}, + fetchPreferencesState: null, + savePreferencesState: null, }; const profilePage = (state = initialState, action) => { @@ -112,6 +116,53 @@ const profilePage = (state = initialState, action) => { error: null, }; + case FETCH_PREFERENCES.BEGIN: + return { + ...state, + fetchPreferencesState: 'pending', + }; + case FETCH_PREFERENCES.SUCCESS: + return { + ...state, + fetchPreferencesState: 'complete', + preferences: action.preferences, + }; + case FETCH_PREFERENCES.FAILURE: + return { + ...state, + fetchPreferencesState: 'error', + }; + case FETCH_PREFERENCES.RESET: + return { + ...state, + fetchPreferencesState: null, + error: null, + }; + + + case SAVE_PREFERENCES.BEGIN: + return { + ...state, + savePreferencesState: 'pending', + }; + case SAVE_PREFERENCES.SUCCESS: + return { + ...state, + savePreferencesState: 'complete', + preferences: action.preferences, + }; + case SAVE_PREFERENCES.FAILURE: + return { + ...state, + savePreferencesState: 'error', + }; + case SAVE_PREFERENCES.RESET: + return { + ...state, + savePreferencesState: null, + error: null, + }; + default: return state; } diff --git a/src/index.jsx b/src/index.jsx index c678d9c..5a1430a 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -19,11 +19,14 @@ import FooterLogo from '../assets/edx-footer.png'; import './App.scss'; import NotFoundPage from './components/NotFoundPage'; +import { fetchPreferences } from './actions/profile'; + class App extends Component { componentDidMount() { const { username } = store.getState().authentication; const userAccountApiService = new UserAccountApiService(apiClient, process.env.LMS_BASE_URL); store.dispatch(fetchUserAccount(userAccountApiService, username)); + store.dispatch(fetchPreferences(username)); } render() { diff --git a/src/sagas/RootSaga.js b/src/sagas/RootSaga.js index 01efb51..79a797c 100644 --- a/src/sagas/RootSaga.js +++ b/src/sagas/RootSaga.js @@ -23,9 +23,44 @@ import { deleteProfilePhotoSuccess, deleteProfilePhotoFailure, deleteProfilePhotoReset, + FETCH_PREFERENCES, + fetchPreferencesBegin, + fetchPreferencesSuccess, + fetchPreferencesFailure, + fetchPreferencesReset, + SAVE_PREFERENCES, + savePreferencesBegin, + savePreferencesSuccess, + savePreferencesFailure, + savePreferencesReset, } from '../actions/profile'; + import * as ProfileApiService from '../services/ProfileApiService'; +import { getPreferences, savePreferences } from '../data/apiClient'; + +let userAccountApiService = null; + +const PROP_TO_STATE_MAP = { + fullName: 'name', + userLocation: 'country', + education: 'levelOfEducation', + socialLinks: socialLinks => socialLinks.filter(({ socialLink }) => socialLink !== null), +}; + +export const mapDataForRequest = (props) => { + const state = {}; + Object.keys(props).forEach((prop) => { + const propModifier = PROP_TO_STATE_MAP[prop] || prop; + if (typeof propModifier === 'function') { + state[prop] = propModifier(props[prop]); + } else { + state[propModifier] = props[prop]; + } + }); + return state; +}; + export function* handleFetchProfile(action) { const { username } = action.payload; @@ -99,9 +134,36 @@ export function* handleDeleteProfilePhoto(action) { } } +export function* handleFetchPreferences(action) { + const { username } = action.payload; + try { + yield put(fetchPreferencesBegin()); + const userPreferences = yield call(getPreferences, username); + yield put(fetchPreferencesSuccess(userPreferences)); + yield put(fetchPreferencesReset()); + } catch (e) { + yield put(fetchPreferencesFailure(e)); + } +} + +export function* handleSavePreferences(action) { + const { username, preferences } = action.payload; + try { + yield put(savePreferencesBegin()); + yield call(savePreferences, username, preferences); + yield put(savePreferencesSuccess()); + yield put(savePreferencesReset()); + } catch (e) { + yield put(savePreferencesFailure(e)); + } +} + + export default function* rootSaga() { yield takeEvery(FETCH_PROFILE.BASE, handleFetchProfile); yield takeEvery(SAVE_PROFILE.BASE, handleSaveProfile); yield takeEvery(SAVE_PROFILE_PHOTO.BASE, handleSaveProfilePhoto); yield takeEvery(DELETE_PROFILE_PHOTO.BASE, handleDeleteProfilePhoto); + yield takeEvery(FETCH_PREFERENCES.BASE, handleFetchPreferences); + yield takeEvery(SAVE_PREFERENCES.BASE, handleSavePreferences); }