diff --git a/package-lock.json b/package-lock.json index ff95d9d..97ef54d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,6 +39,7 @@ "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", "lodash.pickby": "4.6.0", + "lodash.snakecase": "4.1.1", "long": "5.2.3", "memoize-one": "5.2.1", "prop-types": "15.8.1", diff --git a/package.json b/package.json index 89ecb48..fd4daa4 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "lodash.omit": "4.5.0", "lodash.pick": "4.4.0", "lodash.pickby": "4.6.0", + "lodash.snakecase": "4.1.1", "long": "5.2.3", "memoize-one": "5.2.1", "prop-types": "15.8.1", diff --git a/src/notification-preferences/NotificationCourses.jsx b/src/notification-preferences/NotificationCourses.jsx index 4697b90..236150b 100644 --- a/src/notification-preferences/NotificationCourses.jsx +++ b/src/notification-preferences/NotificationCourses.jsx @@ -5,21 +5,32 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Container, Icon, Spinner } from '@edx/paragon'; import { ArrowForwardIos } from '@edx/paragon/icons'; import { fetchCourseList } from './data/thunks'; -import { courseListStatus, getCourseList } from './data/selectors'; -import { IDLE_STATUS, LOADING_STATUS } from '../constants'; +import { selectCourseListStatus, selectCourseList } from './data/selectors'; +import { + IDLE_STATUS, + LOADING_STATUS, + SUCCESS_STATUS, +} from '../constants'; import { messages } from './messages'; +import { NotFoundPage } from '../account-settings'; const NotificationCourses = ({ intl }) => { const dispatch = useDispatch(); - const courseStatus = useSelector(courseListStatus()); - const coursesList = useSelector(getCourseList()); + const coursesList = useSelector(selectCourseList()); + const courseListStatus = useSelector(selectCourseListStatus()); + useEffect(() => { - if (courseStatus === IDLE_STATUS || coursesList.length === 0) { + if (courseListStatus === IDLE_STATUS) { dispatch(fetchCourseList()); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [courseStatus]); - if (courseStatus === LOADING_STATUS) { + }, []); + + if (courseListStatus === SUCCESS_STATUS && coursesList.length === 0) { + return ; + } + + if (courseListStatus === LOADING_STATUS) { return (
{ { coursesList.map(course => (
diff --git a/src/notification-preferences/NotificationPreferenceGroup.jsx b/src/notification-preferences/NotificationPreferenceApp.jsx similarity index 60% rename from src/notification-preferences/NotificationPreferenceGroup.jsx rename to src/notification-preferences/NotificationPreferenceApp.jsx index c7a0929..adc3536 100644 --- a/src/notification-preferences/NotificationPreferenceGroup.jsx +++ b/src/notification-preferences/NotificationPreferenceApp.jsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useIntl } from '@edx/frontend-platform/i18n'; @@ -6,46 +6,50 @@ import { Collapsible } from '@edx/paragon'; import { messages } from './messages'; import ToggleSwitch from './ToggleSwitch'; import { - getPreferenceGroup, - getSelectedCourse, + selectPreferenceAppToggleValue, + selectPreferencesOfApp, + selectSelectedCourseId, } from './data/selectors'; import NotificationPreferenceRow from './NotificationPreferenceRow'; -import { updateGroupValue } from './data/actions'; +import { updateAppPreferenceToggle } from './data/thunks'; -const NotificationPreferenceGroup = ({ groupId }) => { +const NotificationPreferenceApp = ({ appId }) => { const dispatch = useDispatch(); const intl = useIntl(); - const courseId = useSelector(getSelectedCourse()); - const preferenceGroup = useSelector(getPreferenceGroup(groupId)); - const [groupToggle, setGroupToggle] = useState(true); + const courseId = useSelector(selectSelectedCourseId()); + const appPreferences = useSelector(selectPreferencesOfApp(appId)); + const appToggle = useSelector(selectPreferenceAppToggleValue(appId)); const preferences = useMemo(() => ( - preferenceGroup.map(preference => ( + appPreferences.map(preference => ( - ))), [groupId, preferenceGroup]); + ))), [appId, appPreferences]); - const onChangeGroupSettings = useCallback((checked) => { - setGroupToggle(checked); - dispatch(updateGroupValue(courseId, groupId, checked)); + const onChangeAppSettings = useCallback((event) => { + dispatch(updateAppPreferenceToggle(courseId, appId, event.target.checked)); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [groupId]); + }, [appId]); if (!courseId) { return null; } return ( - +
- {intl.formatMessage(messages.notificationGroupTitle, { key: groupId })} + {intl.formatMessage(messages.notificationAppTitle, { key: appId })} - +

@@ -67,8 +71,8 @@ const NotificationPreferenceGroup = ({ groupId }) => { ); }; -NotificationPreferenceGroup.propTypes = { - groupId: PropTypes.string.isRequired, +NotificationPreferenceApp.propTypes = { + appId: PropTypes.string.isRequired, }; -export default React.memo(NotificationPreferenceGroup); +export default React.memo(NotificationPreferenceApp); diff --git a/src/notification-preferences/NotificationPreferenceRow.jsx b/src/notification-preferences/NotificationPreferenceRow.jsx index fd6eda7..a16e586 100644 --- a/src/notification-preferences/NotificationPreferenceRow.jsx +++ b/src/notification-preferences/NotificationPreferenceRow.jsx @@ -1,50 +1,91 @@ import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { useDispatch, useSelector } from 'react-redux'; import { useIntl } from '@edx/frontend-platform/i18n'; +import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon'; +import { InfoOutline } from '@edx/paragon/icons'; import { messages } from './messages'; import ToggleSwitch from './ToggleSwitch'; -import { getPreferenceAttribute } from './data/selectors'; -import { updatePreferenceValue } from './data/actions'; +import { + selectPreference, + selectPreferenceNonEditableChannels, + selectSelectedCourseId, +} from './data/selectors'; +import { updatePreferenceToggle } from './data/thunks'; -const NotificationPreferenceRow = ({ groupId, preferenceName }) => { +const NotificationPreferenceRow = ({ appId, preferenceName }) => { const dispatch = useDispatch(); const intl = useIntl(); - const preference = useSelector(getPreferenceAttribute(groupId, preferenceName)); - const onToggle = useCallback((checked, notificationChannel) => { - dispatch(updatePreferenceValue(groupId, preferenceName, notificationChannel, checked)); - }, [dispatch, groupId, preferenceName]); + const courseId = useSelector(selectSelectedCourseId()); + const preference = useSelector(selectPreference(appId, preferenceName)); + const nonEditable = useSelector(selectPreferenceNonEditableChannels(appId, preferenceName)); + + const onToggle = useCallback((event) => { + const { + checked, + name: notificationChannel, + } = event.target; + dispatch(updatePreferenceToggle( + courseId, + appId, + preferenceName, + notificationChannel, + checked, + )); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [appId, preferenceName]); + + const tooltipId = `${preferenceName}-tooltip`; return (
- + {intl.formatMessage(messages.notificationTitle, { text: preferenceName })} + { + preference.info !== '' && ( + + {preference.info} + + )} + > + + + + + ) + } - - onToggle(checked, 'web')} - /> - - - onToggle(checked, 'email')} - /> - - - onToggle(checked, 'push')} - /> - + { + ['web', 'email', 'push'].map((channel) => ( + + + + )) + }
); }; NotificationPreferenceRow.propTypes = { - groupId: PropTypes.string.isRequired, + appId: PropTypes.string.isRequired, preferenceName: PropTypes.string.isRequired, }; diff --git a/src/notification-preferences/NotificationPreferences.jsx b/src/notification-preferences/NotificationPreferences.jsx index 6341b66..db5ec35 100644 --- a/src/notification-preferences/NotificationPreferences.jsx +++ b/src/notification-preferences/NotificationPreferences.jsx @@ -6,40 +6,44 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { Container, Icon, Spinner } from '@edx/paragon'; import { ArrowBack } from '@edx/paragon/icons'; import { - courseListStatus, - getCourse, - getPreferenceGroupIds, - notificationPreferencesStatus, + selectCourseListStatus, + selectCourse, + selectPreferenceAppsId, + selectNotificationPreferencesStatus, + selectCourseList, } from './data/selectors'; import { fetchCourseList, fetchCourseNotificationPreferences } from './data/thunks'; import { messages } from './messages'; -import NotificationPreferenceGroup from './NotificationPreferenceGroup'; -import { updateSelectedCourse } from './data/actions'; -import { LOADING_STATUS, SUCCESS_STATUS } from '../constants'; +import NotificationPreferenceApp from './NotificationPreferenceApp'; +import { + FAILURE_STATUS, + IDLE_STATUS, + LOADING_STATUS, + SUCCESS_STATUS, +} from '../constants'; +import { NotFoundPage } from '../account-settings'; const NotificationPreferences = () => { const { courseId } = useParams(); const dispatch = useDispatch(); const intl = useIntl(); - const courseStatus = useSelector(courseListStatus()); - const notificationStatus = useSelector(notificationPreferencesStatus()); - const course = useSelector(getCourse(courseId)); - const preferenceGroups = useSelector(getPreferenceGroupIds()); + const courseStatus = useSelector(selectCourseListStatus()); + const coursesList = useSelector(selectCourseList()); + const course = useSelector(selectCourse(courseId)); + const notificationStatus = useSelector(selectNotificationPreferencesStatus()); + const preferenceAppsIds = useSelector(selectPreferenceAppsId()); const preferencesList = useMemo(() => ( - preferenceGroups.map(key => ( - + preferenceAppsIds.map(appId => ( + )) - ), [preferenceGroups]); + ), [preferenceAppsIds]); useEffect(() => { - dispatch(updateSelectedCourse(courseId)); - if (courseStatus !== SUCCESS_STATUS) { + if ([IDLE_STATUS, FAILURE_STATUS].includes(courseStatus)) { dispatch(fetchCourseList()); } - if (notificationStatus !== SUCCESS_STATUS) { - dispatch(fetchCourseNotificationPreferences(courseId)); - } + dispatch(fetchCourseNotificationPreferences(courseId)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [courseId]); @@ -55,6 +59,14 @@ const NotificationPreferences = () => {
); } + + if ( + (courseStatus === SUCCESS_STATUS && coursesList.length === 0) + || (notificationStatus === FAILURE_STATUS && coursesList.length !== 0) + ) { + return ; + } + return (

diff --git a/src/notification-preferences/ToggleSwitch.jsx b/src/notification-preferences/ToggleSwitch.jsx index 373e0ba..913cbfd 100644 --- a/src/notification-preferences/ToggleSwitch.jsx +++ b/src/notification-preferences/ToggleSwitch.jsx @@ -2,17 +2,30 @@ import { Form } from '@edx/paragon'; import React from 'react'; import PropTypes from 'prop-types'; -const ToggleSwitch = ({ value, onChange }) => ( - onChange(event.target.checked)} /> +const ToggleSwitch = ({ + name, + value, + disabled, + onChange, +}) => ( + ); ToggleSwitch.propTypes = { + name: PropTypes.string.isRequired, value: PropTypes.bool.isRequired, + disabled: PropTypes.bool, onChange: PropTypes.func, }; ToggleSwitch.defaultProps = { onChange: () => null, + disabled: false, }; export default React.memo(ToggleSwitch); diff --git a/src/notification-preferences/data/actions.js b/src/notification-preferences/data/actions.js index 7ccbba8..e59cdc9 100644 --- a/src/notification-preferences/data/actions.js +++ b/src/notification-preferences/data/actions.js @@ -7,7 +7,7 @@ export const Actions = { FAILED_COURSE_LIST: 'failedCourseList', UPDATE_SELECTED_COURSE: 'updateSelectedCourse', UPDATE_PREFERENCE: 'updatePreference', - UPDATE_GROUP_PREFERENCE: 'updateGroupValue', + UPDATE_APP_PREFERENCE: 'updateAppValue', }; export const fetchNotificationPreferenceSuccess = (courseId, payload) => dispatch => ( @@ -38,21 +38,21 @@ export const updateSelectedCourse = courseId => dispatch => ( dispatch({ type: Actions.UPDATE_SELECTED_COURSE, courseId }) ); -export const updatePreferenceValue = (groupName, preferenceName, notificationChannel, value) => dispatch => ( +export const updatePreferenceValue = (appId, preferenceName, notificationChannel, value) => dispatch => ( dispatch({ type: Actions.UPDATE_PREFERENCE, - groupName, + appId, preferenceName, notificationChannel, value, }) ); -export const updateGroupValue = (courseId, groupName, value) => dispatch => ( +export const updateAppToggle = (courseId, appId, value) => dispatch => ( dispatch({ - type: Actions.UPDATE_GROUP_PREFERENCE, + type: Actions.UPDATE_APP_PREFERENCE, courseId, - groupName, + appId, value, }) ); diff --git a/src/notification-preferences/data/reducers.js b/src/notification-preferences/data/reducers.js index da0ac8b..189b832 100644 --- a/src/notification-preferences/data/reducers.js +++ b/src/notification-preferences/data/reducers.js @@ -15,13 +15,14 @@ export const defaultState = { status: IDLE_STATUS, selectedCourse: null, preferences: [], - groups: [], + apps: [], + notEditable: {}, }, }; const notificationPreferencesReducer = (state = defaultState, action = {}) => { const { - courseId, groupName, notificationChannel, preferenceName, value, + courseId, appId, notificationChannel, preferenceName, value, } = action; switch (action.type) { case Actions.FETCHING_COURSE_LIST: @@ -54,7 +55,9 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { preferences: { ...state.preferences, status: LOADING_STATUS, - preferences: {}, + preferences: [], + apps: [], + notEditable: {}, }, }; case Actions.FETCHED_PREFERENCES: @@ -70,8 +73,11 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { return { ...state, preferences: { + ...state.preferences, status: FAILURE_STATUS, - preferences: {}, + preferences: [], + apps: [], + notEditable: {}, }, }; case Actions.UPDATE_SELECTED_COURSE: @@ -87,27 +93,22 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { ...state, preferences: { ...state.preferences, - preferences: state.preferences.preferences.map((element) => ( - element.id === preferenceName - ? { ...element, [notificationChannel]: value } - : element + preferences: state.preferences.preferences.map((preference) => ( + preference.id === preferenceName + ? { ...preference, [notificationChannel]: value } + : preference )), }, }; - case Actions.UPDATE_GROUP_PREFERENCE: + case Actions.UPDATE_APP_PREFERENCE: return { ...state, preferences: { ...state.preferences, - preferences: state.preferences.preferences.map((element) => ( - element.groupId === groupName - ? { - ...element, - web: value, - email: value, - push: value, - } - : element + apps: state.preferences.apps.map(app => ( + app.id === appId + ? { ...app, enabled: value } + : app )), }, }; diff --git a/src/notification-preferences/data/selectors.js b/src/notification-preferences/data/selectors.js index 0f8b270..728f1aa 100644 --- a/src/notification-preferences/data/selectors.js +++ b/src/notification-preferences/data/selectors.js @@ -1,41 +1,55 @@ -export const notificationPreferencesStatus = () => state => ( +export const selectNotificationPreferencesStatus = () => state => ( state.notificationPreferences.preferences.status ); -export const getPreferences = () => state => ( - state.notificationPreferences?.preferences?.preferences +export const selectPreferences = () => state => ( + state.notificationPreferences.preferences?.preferences ); -export const courseListStatus = () => state => ( +export const selectCourseListStatus = () => state => ( state.notificationPreferences.courses.status ); -export const getCourseList = () => state => ( +export const selectCourseList = () => state => ( state.notificationPreferences.courses.courses ); -export const getCourse = courseId => state => ( - getCourseList()(state).find( - element => element.id === courseId, +export const selectCourse = courseId => state => ( + selectCourseList()(state).find( + course => course.id === courseId, ) ); -export const getPreferenceGroupIds = () => state => ( - state.notificationPreferences.preferences.groups +export const selectPreferenceAppsId = () => state => ( + state.notificationPreferences.preferences.apps.map(app => app.id) ); -export const getPreferenceGroup = group => state => ( - getPreferences()(state).filter((preference) => ( - preference.groupId === group +export const selectPreferencesOfApp = appId => state => ( + selectPreferences()(state).filter(preference => ( + preference.appId === appId )) ); -export const getPreferenceAttribute = (group, name) => state => ( - getPreferenceGroup(group)(state).find((preference) => ( - preference.id === name +export const selectPreferenceApp = appId => state => ( + state.notificationPreferences.preferences.apps.find(app => ( + app.id === appId )) ); -export const getSelectedCourse = () => state => ( +export const selectPreferenceAppToggleValue = appId => state => ( + selectPreferenceApp(appId)(state).enabled +); + +export const selectPreference = (appId, name) => state => ( + selectPreferences()(state).find((preference) => ( + preference.id === name && preference.appId === appId + )) +); + +export const selectPreferenceNonEditableChannels = (appId, name) => state => ( + state?.notificationPreferences.preferences.notEditable[appId]?.[name] || [] +); + +export const selectSelectedCourseId = () => state => ( state.notificationPreferences.preferences.selectedCourse ); diff --git a/src/notification-preferences/data/service.js b/src/notification-preferences/data/service.js index e53ba9f..4b6e939 100644 --- a/src/notification-preferences/data/service.js +++ b/src/notification-preferences/data/service.js @@ -1,46 +1,43 @@ -/* eslint-disable no-unused-vars */ +import { getConfig, snakeCaseObject } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import snakeCase from 'lodash.snakecase'; -// import { getConfig } from '@edx/frontend-platform'; -// import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - -export async function getCourseNotificationPreferences(courseId) { - // const url = `${getConfig().LMS_BASE_URL}/api/notifications/${courseId}`; - // const { data } = await getAuthenticatedHttpClient().get(url); - const data = { - discussion: { - new_post: { - web: true, - push: false, - email: false, - }, - new_comment: { - web: true, - push: false, - email: false, - }, - }, - coursework: { - new_assignment: { - web: true, - push: false, - email: false, - }, - new_grade: { - web: true, - push: false, - email: false, - }, - }, - }; +export const getCourseNotificationPreferences = async (courseId) => { + const url = `${getConfig().LMS_BASE_URL}/api/notifications/configurations/${courseId}`; + const { data } = await getAuthenticatedHttpClient().get(url); return data; -} +}; -export async function getCourseList() { - // const url = `${getConfig().LMS_BASE_URL}/api/notifications/${courseId}`; - // const { data } = await getAuthenticatedHttpClient().get(url); - return [ - { id: 'course-v1:edX+Supply+Demo_Course', name: 'Supply Chain Analytics' }, - { id: 'course-v1:edX+Happiness+At+Work_Course', name: 'The Foundation of Happiness At Work' }, - { id: 'course-v1:edX+Empathy+At+Work_Course', name: 'Empathy and Emotional Intelligence At Work' }, - ]; -} +export const getCourseList = async () => { + const url = `${getConfig().LMS_BASE_URL}/api/notifications/enrollments/`; + const { data } = await getAuthenticatedHttpClient().get(url); + return data; +}; + +export const patchAppPreferenceToggle = async (courseId, appId, value) => { + const patchData = snakeCaseObject({ + notificationApp: appId, + value, + }); + const url = `${getConfig().LMS_BASE_URL}/api/notifications/configurations/${courseId}`; + const { data } = await getAuthenticatedHttpClient().patch(url, patchData); + return data; +}; + +export const patchPreferenceToggle = async ( + courseId, + notificationApp, + notificationType, + notificationChannel, + value, +) => { + const patchData = snakeCaseObject({ + notificationApp, + notificationType: snakeCase(notificationType), + notificationChannel, + value, + }); + const url = `${getConfig().LMS_BASE_URL}/api/notifications/configurations/${courseId}`; + const { data } = await getAuthenticatedHttpClient().patch(url, patchData); + return data; +}; diff --git a/src/notification-preferences/data/thunks.js b/src/notification-preferences/data/thunks.js index 8640981..e980283 100644 --- a/src/notification-preferences/data/thunks.js +++ b/src/notification-preferences/data/thunks.js @@ -6,30 +6,56 @@ import { fetchNotificationPreferenceFailed, fetchNotificationPreferenceFetching, fetchNotificationPreferenceSuccess, + updateAppToggle, + updatePreferenceValue, + updateSelectedCourse, } from './actions'; import { getCourseList, getCourseNotificationPreferences, + patchAppPreferenceToggle, + patchPreferenceToggle, } from './service'; -const normalizePreferences = (preferences) => { - const groups = Object.keys(preferences); - const preferenceList = groups.map(groupId => { - const preferencesKeys = Object.keys(preferences[groupId]); +const normalizeCourses = (responseData) => ( + responseData.map((enrollment) => ({ + id: enrollment.course.id, + name: enrollment.course.displayName, + })) +); + +const normalizePreferences = (responseData) => { + const preferences = responseData.notificationPreferenceConfig; + + const appKeys = Object.keys(preferences); + const apps = appKeys.map((appId) => ({ + id: appId, + enabled: preferences[appId].enabled, + })); + + const notEditable = {}; + const preferenceList = appKeys.map(appId => { + const preferencesKeys = Object.keys(preferences[appId].notificationTypes); const flatPreferences = preferencesKeys.map(preferenceId => ( { id: preferenceId, - groupId, - web: preferences?.[groupId]?.[preferenceId].web, - push: preferences?.[groupId]?.[preferenceId].push, - mobile: preferences?.[groupId]?.[preferenceId].mobile, + appId, + web: preferences[appId].notificationTypes[preferenceId].web, + push: preferences[appId].notificationTypes[preferenceId].push, + email: preferences[appId].notificationTypes[preferenceId].email, + info: preferences[appId].notificationTypes[preferenceId].info || '', } )); + + notEditable[appId] = preferences[appId].notEditable; + return flatPreferences; }).flat(); + const normalizedPreferences = { - groups, + apps, preferences: preferenceList, + notEditable, }; return normalizedPreferences; }; @@ -39,7 +65,8 @@ export const fetchCourseList = () => ( try { dispatch(fetchCourseListFetching()); const data = await getCourseList(); - dispatch(fetchCourseListSuccess(data)); + const normalizedData = normalizeCourses(camelCaseObject(data)); + dispatch(fetchCourseListSuccess(normalizedData)); } catch (errors) { dispatch(fetchCourseListFailed()); } @@ -49,6 +76,7 @@ export const fetchCourseList = () => ( export const fetchCourseNotificationPreferences = (courseId) => ( async (dispatch) => { try { + dispatch(updateSelectedCourse(courseId)); dispatch(fetchNotificationPreferenceFetching()); const data = await getCourseNotificationPreferences(courseId); const normalizedData = normalizePreferences(camelCaseObject(data)); @@ -58,3 +86,53 @@ export const fetchCourseNotificationPreferences = (courseId) => ( } } ); + +export const updateAppPreferenceToggle = (courseId, appId, value) => ( + async (dispatch) => { + try { + dispatch(updateAppToggle(courseId, appId, value)); + const data = await patchAppPreferenceToggle(courseId, appId, value); + const normalizedData = normalizePreferences(camelCaseObject(data)); + dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData)); + } catch (errors) { + dispatch(updateAppToggle(courseId, appId, !value)); + dispatch(fetchNotificationPreferenceFailed()); + } + } +); + +export const updatePreferenceToggle = ( + courseId, + notificationApp, + notificationType, + notificationChannel, + value, +) => ( + async (dispatch) => { + try { + dispatch(updatePreferenceValue( + notificationApp, + notificationType, + notificationChannel, + value, + )); + const data = await patchPreferenceToggle( + courseId, + notificationApp, + notificationType, + notificationChannel, + value, + ); + const normalizedData = normalizePreferences(camelCaseObject(data)); + dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData)); + } catch (errors) { + dispatch(updatePreferenceValue( + notificationApp, + notificationType, + notificationChannel, + !value, + )); + dispatch(fetchNotificationPreferenceFailed()); + } + } +); diff --git a/src/notification-preferences/messages.js b/src/notification-preferences/messages.js index d0dbf4e..fdd8346 100644 --- a/src/notification-preferences/messages.js +++ b/src/notification-preferences/messages.js @@ -7,8 +7,8 @@ export const messages = defineMessages({ defaultMessage: 'Notifications', description: 'Notification title', }, - notificationGroupTitle: { - id: 'notification.preference.group.title', + notificationAppTitle: { + id: 'notification.preference.app.title', defaultMessage: `{ key, select, discussion {Discussions} @@ -21,8 +21,12 @@ export const messages = defineMessages({ id: 'notification.preference.title', defaultMessage: `{ text, select, + core {Core} newPost {New Post} newComment {New Comment} + newCommentOnPost {New Comment On Post} + newResponseOnPost {New Response On Post} + newResponseOnComment {New Response On Comment} newAssignment {New Assignment} newGrade {New Grade} other {{text}}