diff --git a/.env b/.env index e27becc..6d485f1 100644 --- a/.env +++ b/.env @@ -37,4 +37,3 @@ SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/ar COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]' # Fallback in local style files PARAGON_THEME_URLS={} -ENABLE_PREFERENCES_V2='false' diff --git a/.env.development b/.env.development index f013121..0a12856 100644 --- a/.env.development +++ b/.env.development @@ -38,4 +38,3 @@ SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/ar COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]' # Fallback in local style files PARAGON_THEME_URLS={} -ENABLE_PREFERENCES_V2='false' diff --git a/.env.test b/.env.test index 4c9fc47..1c7a3b3 100644 --- a/.env.test +++ b/.env.test @@ -34,4 +34,3 @@ LEARNER_FEEDBACK_URL='' SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/article/How-do-I-link-or-unlink-my-edX-account-to-a-social-media-account' COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]' PARAGON_THEME_URLS={} -ENABLE_PREFERENCES_V2='false' diff --git a/src/account-settings/AccountSettingsPage.jsx b/src/account-settings/AccountSettingsPage.jsx index 71c0e39..b74ee03 100644 --- a/src/account-settings/AccountSettingsPage.jsx +++ b/src/account-settings/AccountSettingsPage.jsx @@ -50,7 +50,7 @@ import { FIELD_LABELS, } from './data/constants'; import { fetchSiteLanguages } from './site-language'; -import { fetchCourseList } from '../notification-preferences/data/thunks'; +import { fetchNotificationPreferences } from '../notification-preferences/data/thunks'; import NotificationSettings from '../notification-preferences/NotificationSettings'; import { withLocation, withNavigate } from './hoc'; @@ -75,7 +75,7 @@ class AccountSettingsPage extends React.Component { } componentDidMount() { - this.props.fetchCourseList(); + this.props.fetchNotificationPreferences(); this.props.fetchSettings(); this.props.fetchSiteLanguages(this.props.navigate); sendTrackingLogEvent('edx.user.settings.viewed', { @@ -945,7 +945,7 @@ AccountSettingsPage.propTypes = { saveSettings: PropTypes.func.isRequired, fetchSettings: PropTypes.func.isRequired, beginNameChange: PropTypes.func.isRequired, - fetchCourseList: PropTypes.func.isRequired, + fetchNotificationPreferences: PropTypes.func.isRequired, tpaProviders: PropTypes.arrayOf(PropTypes.shape({ connected: PropTypes.bool, })), @@ -1010,7 +1010,7 @@ AccountSettingsPage.defaultProps = { }; export default withLocation(withNavigate(connect(accountSettingsPageSelector, { - fetchCourseList, + fetchNotificationPreferences, fetchSettings, saveSettings, saveMultipleSettings, diff --git a/src/index.jsx b/src/index.jsx index 0b50016..d15b9da 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -74,7 +74,6 @@ initialize({ MARKETING_EMAILS_OPT_IN: (process.env.MARKETING_EMAILS_OPT_IN || false), PASSWORD_RESET_SUPPORT_LINK: process.env.PASSWORD_RESET_SUPPORT_LINK, LEARNER_FEEDBACK_URL: process.env.LEARNER_FEEDBACK_URL, - ENABLE_PREFERENCES_V2: process.env.ENABLE_PREFERENCES_V2 || false, }, 'App loadConfig override handler'); }, }, diff --git a/src/notification-preferences/NotificationCoursesDropdown.jsx b/src/notification-preferences/NotificationCoursesDropdown.jsx deleted file mode 100644 index 5432dbc..0000000 --- a/src/notification-preferences/NotificationCoursesDropdown.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useCallback, useEffect, useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; - -import { useIntl } from '@edx/frontend-platform/i18n'; -import { Dropdown } from '@openedx/paragon'; - -import { IDLE_STATUS, SUCCESS_STATUS } from '../constants'; -import { selectCourseList, selectCourseListStatus, selectSelectedCourseId } from './data/selectors'; -import { fetchCourseList, setSelectedCourse } from './data/thunks'; -import messages from './messages'; - -const NotificationCoursesDropdown = () => { - const intl = useIntl(); - const dispatch = useDispatch(); - const coursesList = useSelector(selectCourseList()); - const courseListStatus = useSelector(selectCourseListStatus()); - const selectedCourseId = useSelector(selectSelectedCourseId()); - const selectedCourse = useMemo( - () => coursesList.find((course) => course.id === selectedCourseId), - [coursesList, selectedCourseId], - ); - - const handleCourseSelection = useCallback((courseId) => { - dispatch(setSelectedCourse(courseId)); - }, [dispatch]); - - const fetchCourses = useCallback((page = 1, pageSize = 99999) => { - dispatch(fetchCourseList(page, pageSize)); - }, [dispatch]); - - useEffect(() => { - if (courseListStatus === IDLE_STATUS) { - fetchCourses(); - } - }, [courseListStatus, fetchCourses]); - - return ( - courseListStatus === SUCCESS_STATUS && ( -
-
{intl.formatMessage(messages.notificationDropdownlabel)}
- - - {selectedCourse?.name} - - - {coursesList.map((course) => ( - - {course.name} - - ))} - - - - {selectedCourse?.name === 'Account' - ? intl.formatMessage(messages.notificationDropdownApplies) - : intl.formatMessage(messages.notificationCourseDropdownApplies)} - -
- ) - ); -}; - -export default NotificationCoursesDropdown; diff --git a/src/notification-preferences/NotificationPreferenceColumn.jsx b/src/notification-preferences/NotificationPreferenceColumn.jsx index 2dbae1e..d6a0e32 100644 --- a/src/notification-preferences/NotificationPreferenceColumn.jsx +++ b/src/notification-preferences/NotificationPreferenceColumn.jsx @@ -15,7 +15,7 @@ import { LOADING_STATUS } from '../constants'; import { updatePreferenceToggle } from './data/thunks'; import { selectAppNonEditableChannels, selectAppPreferences, - selectSelectedCourseId, selectUpdatePreferencesStatus, + selectUpdatePreferencesStatus, } from './data/selectors'; import { notificationChannels, shouldHideAppPreferences } from './data/utils'; import { @@ -25,7 +25,6 @@ import { const NotificationPreferenceColumn = ({ appId, channel, appPreference }) => { const dispatch = useDispatch(); const intl = useIntl(); - const courseId = useSelector(selectSelectedCourseId()); const appPreferences = useSelector(selectAppPreferences(appId)); const updatePreferencesStatus = useSelector(selectUpdatePreferencesStatus()); const nonEditable = useSelector(selectAppNonEditableChannels(appId)); @@ -34,11 +33,11 @@ const NotificationPreferenceColumn = ({ appId, channel, appPreference }) => { const hideAppPreferences = shouldHideAppPreferences(appPreferences, appId) || false; const getValue = useCallback((notificationChannel, innerText, checked) => { - if (notificationChannel === EMAIL_CADENCE && courseId) { + if (notificationChannel === EMAIL_CADENCE) { return innerText; } return checked; - }, [courseId]); + }, []); const getEmailCadence = useCallback((notificationChannel, checked, innerText, emailCadence) => { if (notificationChannel === EMAIL_CADENCE) { @@ -63,14 +62,13 @@ const NotificationPreferenceColumn = ({ appId, channel, appPreference }) => { ); dispatch(updatePreferenceToggle( - courseId, appId, notificationType, notificationChannel, value, emailCadence !== MIXED ? emailCadence : undefined, )); - }, [appPreferences, getValue, getEmailCadence, dispatch, courseId, appId]); + }, [appPreferences, getValue, getEmailCadence, dispatch, appId]); const renderPreference = (preference) => ( (preference?.coreNotificationTypes?.length > 0 || preference.id !== 'core') && ( diff --git a/src/notification-preferences/NotificationPreferences.jsx b/src/notification-preferences/NotificationPreferences.jsx index 46cbc87..a510f72 100644 --- a/src/notification-preferences/NotificationPreferences.jsx +++ b/src/notification-preferences/NotificationPreferences.jsx @@ -9,23 +9,21 @@ import { Spinner, NavItem } from '@openedx/paragon'; import { useIsOnMobile } from '../hooks'; import messages from './messages'; import NotificationPreferenceApp from './NotificationPreferenceApp'; -import { fetchCourseNotificationPreferences } from './data/thunks'; +import { fetchNotificationPreferences } from './data/thunks'; import { LOADING_STATUS } from '../constants'; import { - selectCourseListStatus, selectNotificationPreferencesStatus, selectPreferenceAppsId, selectSelectedCourseId, + selectNotificationPreferencesStatus, selectPreferenceAppsId, } from './data/selectors'; import { notificationChannels } from './data/utils'; const NotificationPreferences = () => { const dispatch = useDispatch(); const intl = useIntl(); - const courseStatus = useSelector(selectCourseListStatus()); - const courseId = useSelector(selectSelectedCourseId()); const notificationStatus = useSelector(selectNotificationPreferencesStatus()); const preferenceAppsIds = useSelector(selectPreferenceAppsId()); const mobileView = useIsOnMobile(); const NOTIFICATION_CHANNELS = notificationChannels(); - const isLoading = notificationStatus === LOADING_STATUS || courseStatus === LOADING_STATUS; + const isLoading = notificationStatus === LOADING_STATUS; const preferencesList = useMemo(() => ( preferenceAppsIds.map(appId => ( @@ -34,8 +32,8 @@ const NotificationPreferences = () => { ), [preferenceAppsIds]); useEffect(() => { - dispatch(fetchCourseNotificationPreferences(courseId)); - }, [courseId, dispatch]); + dispatch(fetchNotificationPreferences()); + }, [dispatch]); if (preferenceAppsIds.length === 0) { return null; diff --git a/src/notification-preferences/NotificationPreferences.test.jsx b/src/notification-preferences/NotificationPreferences.test.jsx index dc237ac..dcf364d 100644 --- a/src/notification-preferences/NotificationPreferences.test.jsx +++ b/src/notification-preferences/NotificationPreferences.test.jsx @@ -12,7 +12,7 @@ import { defaultState } from './data/reducers'; import NotificationPreferences from './NotificationPreferences'; import { LOADING_STATUS, SUCCESS_STATUS } from '../constants'; import { - getCourseNotificationPreferences, + getNotificationPreferences, postPreferenceToggle, } from './data/service'; @@ -117,7 +117,6 @@ describe('Notification Preferences', () => { store = setupStore({ ...defaultPreferences, status: SUCCESS_STATUS, - selectedCourse: courseId, }); auth.getAuthenticatedHttpClient = jest.fn(() => ({ @@ -148,19 +147,10 @@ describe('Notification Preferences', () => { expect(screen.queryAllByTestId('notification-preference')).toHaveLength(4); }); - it('update preference on click', async () => { - const wrapper = await render(notificationPreferences(store)); - const element = wrapper.container.querySelector('#core-web'); - expect(element).not.toBeChecked(); - await fireEvent.click(element); - expect(mockDispatch).toHaveBeenCalled(); - }); - it('update account preference on click', async () => { store = setupStore({ ...defaultPreferences, status: SUCCESS_STATUS, - selectedCourse: '', }); await render(notificationPreferences(store)); const element = screen.getByTestId('toggle-core-web'); @@ -203,22 +193,11 @@ describe('Notification Preferences API v2 Logic', () => { setConfig({ LMS_BASE_URL }); }); - describe('getCourseNotificationPreferences', () => { - it('should call the v2 configurations URL when ENABLE_PREFERENCES_V2 is true', async () => { - setConfig({ LMS_BASE_URL, ENABLE_PREFERENCES_V2: 'true' }); + describe('getNotificationPreferences', () => { + it('should call the v2 configurations URL', async () => { const expectedUrl = `${LMS_BASE_URL}/api/notifications/v2/configurations/`; - await getCourseNotificationPreferences('any-course-id'); - - expect(mockHttpClient.get).toHaveBeenCalledWith(expectedUrl); - expect(mockHttpClient.get).toHaveBeenCalledTimes(1); - }); - - it('should call the original (v1) configurations URL when ENABLE_PREFERENCES_V2 is not true', async () => { - setConfig({ LMS_BASE_URL, ENABLE_PREFERENCES_V2: 'false' }); - const expectedUrl = `${LMS_BASE_URL}/api/notifications/configurations/${courseId}`; - - await getCourseNotificationPreferences(courseId); + await getNotificationPreferences(); expect(mockHttpClient.get).toHaveBeenCalledWith(expectedUrl); expect(mockHttpClient.get).toHaveBeenCalledTimes(1); @@ -226,8 +205,7 @@ describe('Notification Preferences API v2 Logic', () => { }); describe('postPreferenceToggle', () => { - it('should call the v2 configurations URL with PUT method when ENABLE_PREFERENCES_V2 is true', async () => { - setConfig({ LMS_BASE_URL, ENABLE_PREFERENCES_V2: 'true' }); + it('should call the v2 configurations URL with PUT method', async () => { const expectedUrl = `${LMS_BASE_URL}/api/notifications/v2/configurations/`; const testArgs = ['app_name', 'notification_type', 'web', true, 'daily']; @@ -237,17 +215,5 @@ describe('Notification Preferences API v2 Logic', () => { expect(mockHttpClient.put).toHaveBeenCalledTimes(1); expect(mockHttpClient.post).not.toHaveBeenCalled(); }); - - it('should call the original (v1) update-all URL with POST method when ENABLE_PREFERENCES_V2 is not true', async () => { - setConfig({ LMS_BASE_URL, ENABLE_PREFERENCES_V2: 'false' }); - const expectedUrl = `${LMS_BASE_URL}/api/notifications/preferences/update-all/`; - const testArgs = ['app_name', 'notification_type', 'web', true, 'daily']; - - await postPreferenceToggle(...testArgs); - - expect(mockHttpClient.post).toHaveBeenCalledWith(expectedUrl, expect.any(Object)); - expect(mockHttpClient.post).toHaveBeenCalledTimes(1); - expect(mockHttpClient.put).not.toHaveBeenCalled(); - }); }); }); diff --git a/src/notification-preferences/NotificationSettings.jsx b/src/notification-preferences/NotificationSettings.jsx index d437f89..1c30c7d 100644 --- a/src/notification-preferences/NotificationSettings.jsx +++ b/src/notification-preferences/NotificationSettings.jsx @@ -4,9 +4,8 @@ import { useSelector } from 'react-redux'; import { useIntl } from '@edx/frontend-platform/i18n'; import { Container, Hyperlink } from '@openedx/paragon'; -import { selectSelectedCourseId, selectShowPreferences } from './data/selectors'; +import { selectShowPreferences } from './data/selectors'; import messages from './messages'; -import NotificationCoursesDropdown from './NotificationCoursesDropdown'; import NotificationPreferences from './NotificationPreferences'; import { useFeedbackWrapper } from '../hooks'; @@ -14,7 +13,6 @@ const NotificationSettings = () => { useFeedbackWrapper(); const intl = useIntl(); const showPreferences = useSelector(selectShowPreferences()); - const courseId = useSelector(selectSelectedCourseId()); return ( showPreferences && ( @@ -42,8 +40,7 @@ const NotificationSettings = () => { {intl.formatMessage(messages.notificationPreferenceGuideLink)} - - +
) diff --git a/src/notification-preferences/data/actions.js b/src/notification-preferences/data/actions.js index e9f900d..e0f0a5d 100644 --- a/src/notification-preferences/data/actions.js +++ b/src/notification-preferences/data/actions.js @@ -2,19 +2,15 @@ export const Actions = { FETCHED_PREFERENCES: 'fetchedPreferences', FETCHING_PREFERENCES: 'fetchingPreferences', FAILED_PREFERENCES: 'failedPreferences', - FETCHING_COURSE_LIST: 'fetchingCourseList', - FETCHED_COURSE_LIST: 'fetchedCourseList', - FAILED_COURSE_LIST: 'failedCourseList', - UPDATE_SELECTED_COURSE: 'updateSelectedCourse', UPDATE_PREFERENCE: 'updatePreference', UPDATE_APP_PREFERENCE: 'updateAppValue', }; -export const fetchNotificationPreferenceSuccess = (courseId, payload, isAccountPreference) => dispatch => ( +export const fetchNotificationPreferenceSuccess = (payload, showPreferences, isPreferenceUpdate) => dispatch => { dispatch({ - type: Actions.FETCHED_PREFERENCES, courseId, payload, isAccountPreference, - }) -); + type: Actions.FETCHED_PREFERENCES, payload, showPreferences, isPreferenceUpdate, + }); +}; export const fetchNotificationPreferenceFetching = () => dispatch => ( dispatch({ type: Actions.FETCHING_PREFERENCES }) @@ -24,22 +20,6 @@ export const fetchNotificationPreferenceFailed = () => dispatch => ( dispatch({ type: Actions.FAILED_PREFERENCES }) ); -export const fetchCourseListSuccess = payload => dispatch => ( - dispatch({ type: Actions.FETCHED_COURSE_LIST, payload }) -); - -export const fetchCourseListFetching = () => dispatch => ( - dispatch({ type: Actions.FETCHING_COURSE_LIST }) -); - -export const fetchCourseListFailed = () => dispatch => ( - dispatch({ type: Actions.FAILED_COURSE_LIST }) -); - -export const updateSelectedCourse = courseId => dispatch => ( - dispatch({ type: Actions.UPDATE_SELECTED_COURSE, courseId }) -); - export const updatePreferenceValue = (appId, preferenceName, notificationChannel, value) => dispatch => ( dispatch({ type: Actions.UPDATE_PREFERENCE, diff --git a/src/notification-preferences/data/reducers.js b/src/notification-preferences/data/reducers.js index 1683b20..a9aacd2 100644 --- a/src/notification-preferences/data/reducers.js +++ b/src/notification-preferences/data/reducers.js @@ -9,15 +9,9 @@ import { normalizeAccountPreferences } from './thunks'; export const defaultState = { showPreferences: false, - courses: { - status: IDLE_STATUS, - courses: [{ id: '', name: 'Account' }], - pagination: {}, - }, preferences: { status: IDLE_STATUS, updatePreferenceStatus: IDLE_STATUS, - selectedCourse: '', preferences: [], apps: [], nonEditable: {}, @@ -26,35 +20,9 @@ export const defaultState = { const notificationPreferencesReducer = (state = defaultState, action = {}) => { const { - courseId, appId, notificationChannel, preferenceName, value, + appId, notificationChannel, preferenceName, value, } = action; switch (action.type) { - case Actions.FETCHING_COURSE_LIST: - return { - ...state, - courses: { - ...state.courses, - status: LOADING_STATUS, - }, - }; - case Actions.FETCHED_COURSE_LIST: - return { - ...state, - courses: { - status: SUCCESS_STATUS, - courses: [...state.courses.courses, ...action.payload.courseList], - pagination: action.payload.pagination, - }, - showPreferences: action.payload.showPreferences, - }; - case Actions.FAILED_COURSE_LIST: - return { - ...state, - courses: { - ...state.courses, - status: FAILURE_STATUS, - }, - }; case Actions.FETCHING_PREFERENCES: return { ...state, @@ -69,7 +37,7 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { case Actions.FETCHED_PREFERENCES: { const { preferences } = state; - if (action.isAccountPreference) { + if (action.isPreferenceUpdate) { normalizeAccountPreferences(preferences, action.payload); } @@ -81,6 +49,7 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { updatePreferenceStatus: SUCCESS_STATUS, ...action.payload, }, + showPreferences: action.showPreferences, }; } case Actions.FAILED_PREFERENCES: @@ -95,14 +64,6 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { nonEditable: {}, }, }; - case Actions.UPDATE_SELECTED_COURSE: - return { - ...state, - preferences: { - ...state.preferences, - selectedCourse: courseId, - }, - }; case Actions.UPDATE_PREFERENCE: return { ...state, diff --git a/src/notification-preferences/data/reducers.test.js b/src/notification-preferences/data/reducers.test.js index 697b7fe..79a5ee4 100644 --- a/src/notification-preferences/data/reducers.test.js +++ b/src/notification-preferences/data/reducers.test.js @@ -10,7 +10,6 @@ import { describe('notification-preferences reducer', () => { let state = null; - const selectedCourseId = 'selected-course-id'; const preferenceData = { apps: [{ id: 'discussion', enabled: true }], @@ -28,53 +27,6 @@ describe('notification-preferences reducer', () => { state = reducer(); }); - it('updates course list when api call is successful', () => { - const data = { - pagination: { - count: 1, - currentPage: 1, - hasMore: false, - totalPages: 1, - }, - courseList: [], - }; - const result = reducer( - state, - { type: Actions.FETCHED_COURSE_LIST, payload: data }, - ); - expect(result.courses).toEqual({ - status: SUCCESS_STATUS, - courses: [{ id: '', name: 'Account' }], - pagination: data.pagination, - }); - }); - - test.each([ - { action: Actions.FETCHING_COURSE_LIST, status: LOADING_STATUS }, - { action: Actions.FAILED_COURSE_LIST, status: FAILURE_STATUS }, - ])('course list is empty when api call is %s', ({ action, status }) => { - const result = reducer( - state, - { type: action }, - ); - expect(result.courses).toEqual({ - status, - courses: [{ - id: '', - name: 'Account', - }], - pagination: {}, - }); - }); - - it('updates selected course id', () => { - const result = reducer( - state, - { type: Actions.UPDATE_SELECTED_COURSE, courseId: selectedCourseId }, - ); - expect(result.preferences.selectedCourse).toEqual(selectedCourseId); - }); - it('updates preferences when api call is successful', () => { const result = reducer( state, @@ -83,7 +35,6 @@ describe('notification-preferences reducer', () => { expect(result.preferences).toEqual({ status: SUCCESS_STATUS, updatePreferenceStatus: SUCCESS_STATUS, - selectedCourse: '', ...preferenceData, }); }); @@ -98,7 +49,6 @@ describe('notification-preferences reducer', () => { ); expect(result.preferences).toEqual({ status, - selectedCourse: '', preferences: [], apps: [], nonEditable: {}, diff --git a/src/notification-preferences/data/selectors.js b/src/notification-preferences/data/selectors.js index 3dbe375..3850e72 100644 --- a/src/notification-preferences/data/selectors.js +++ b/src/notification-preferences/data/selectors.js @@ -13,20 +13,6 @@ export const selectPreferences = () => state => ( state.notificationPreferences.preferences?.preferences ); -export const selectCourseListStatus = () => state => ( - state.notificationPreferences.courses.status -); - -export const selectCourseList = () => state => ( - state.notificationPreferences.courses.courses -); - -export const selectCourse = courseId => state => ( - selectCourseList()(state).find( - course => course.id === courseId, - ) -); - export const selectPreferenceAppsId = () => state => ( state.notificationPreferences.preferences.apps.map(app => app.id) ); @@ -57,14 +43,6 @@ export const selectPreferenceNonEditableChannels = (appId, name) => state => ( state?.notificationPreferences.preferences.nonEditable[appId]?.[name] || [] ); -export const selectSelectedCourseId = () => state => ( - state.notificationPreferences.preferences.selectedCourse -); - -export const selectPagination = () => state => ( - state.notificationPreferences.courses.pagination -); - export const selectShowPreferences = () => state => ( state.notificationPreferences.showPreferences ); diff --git a/src/notification-preferences/data/service.js b/src/notification-preferences/data/service.js index d9dbc88..332f90d 100644 --- a/src/notification-preferences/data/service.js +++ b/src/notification-preferences/data/service.js @@ -2,40 +2,12 @@ import { getConfig, snakeCaseObject } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import snakeCase from 'lodash.snakecase'; -export const getCourseNotificationPreferences = async (courseId) => { - let url = `${getConfig().LMS_BASE_URL}/api/notifications/configurations/${courseId}`; - if (getConfig().ENABLE_PREFERENCES_V2 === 'true') { - url = `${getConfig().LMS_BASE_URL}/api/notifications/v2/configurations/`; - } +export const getNotificationPreferences = async () => { + const url = `${getConfig().LMS_BASE_URL}/api/notifications/v2/configurations/`; const { data } = await getAuthenticatedHttpClient().get(url); return data; }; -export const getCourseList = async (page, pageSize) => { - const params = snakeCaseObject({ page, pageSize }); - const url = `${getConfig().LMS_BASE_URL}/api/notifications/enrollments/`; - const { data } = await getAuthenticatedHttpClient().get(url, { params }); - 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; -}; - export const postPreferenceToggle = async ( notificationApp, notificationType, @@ -50,13 +22,7 @@ export const postPreferenceToggle = async ( value, emailCadence, }); - if (getConfig().ENABLE_PREFERENCES_V2 === 'true') { - const url = `${getConfig().LMS_BASE_URL}/api/notifications/v2/configurations/`; - const { data } = await getAuthenticatedHttpClient().put(url, patchData); - return data; - } - - const url = `${getConfig().LMS_BASE_URL}/api/notifications/preferences/update-all/`; - const { data } = await getAuthenticatedHttpClient().post(url, patchData); + const url = `${getConfig().LMS_BASE_URL}/api/notifications/v2/configurations/`; + const { data } = await getAuthenticatedHttpClient().put(url, patchData); return data; }; diff --git a/src/notification-preferences/data/thunk.test.js b/src/notification-preferences/data/thunk.test.js index 2b23e40..6aa4e66 100644 --- a/src/notification-preferences/data/thunk.test.js +++ b/src/notification-preferences/data/thunk.test.js @@ -4,7 +4,7 @@ import { fetchNotificationPreferenceSuccess, fetchNotificationPreferenceFailed, } from './actions'; -import { patchPreferenceToggle, postPreferenceToggle } from './service'; +import { postPreferenceToggle } from './service'; import { EMAIL } from './constants'; jest.mock('./service', () => ({ @@ -60,37 +60,9 @@ describe('updatePreferenceToggle', () => { jest.clearAllMocks(); }); - it('should update preference for a course-specific notification', async () => { - patchPreferenceToggle.mockResolvedValue({ data: mockData }); - await updatePreferenceToggle( - courseId, - notificationApp, - notificationType, - notificationChannel, - value, - emailCadence, - )(dispatch); - - expect(dispatch).toHaveBeenCalledWith(updatePreferenceValue( - notificationApp, - notificationType, - notificationChannel, - !value, - )); - expect(patchPreferenceToggle).toHaveBeenCalledWith( - courseId, - notificationApp, - notificationType, - notificationChannel, - value, - ); - expect(dispatch).toHaveBeenCalledWith(fetchNotificationPreferenceSuccess(courseId, { data: mockData }, false)); - }); - - it('should update preference globally when courseId is not provided', async () => { + it('should update preference globally', async () => { postPreferenceToggle.mockResolvedValue({ data: mockData }); await updatePreferenceToggle( - null, notificationApp, notificationType, notificationChannel, @@ -115,23 +87,22 @@ describe('updatePreferenceToggle', () => { }); it('should handle email preferences separately', async () => { - patchPreferenceToggle.mockResolvedValue({ data: mockData }); - await updatePreferenceToggle(courseId, notificationApp, notificationType, EMAIL, value, emailCadence)(dispatch); + postPreferenceToggle.mockResolvedValue({ data: mockData }); + await updatePreferenceToggle(notificationApp, notificationType, EMAIL, value, emailCadence)(dispatch); - expect(patchPreferenceToggle).toHaveBeenCalledWith( - courseId, + expect(postPreferenceToggle).toHaveBeenCalledWith( notificationApp, notificationType, EMAIL, true, + emailCadence, ); expect(dispatch).toHaveBeenCalledWith(fetchNotificationPreferenceSuccess(courseId, { data: mockData }, false)); }); it('should dispatch fetchNotificationPreferenceFailed on error', async () => { - patchPreferenceToggle.mockRejectedValue(new Error('Network Error')); + postPreferenceToggle.mockRejectedValue(new Error('Network Error')); await updatePreferenceToggle( - courseId, notificationApp, notificationType, notificationChannel, diff --git a/src/notification-preferences/data/thunks.js b/src/notification-preferences/data/thunks.js index 0041b84..7798de9 100644 --- a/src/notification-preferences/data/thunks.js +++ b/src/notification-preferences/data/thunks.js @@ -2,42 +2,16 @@ import { camelCaseObject } from '@edx/frontend-platform'; import camelCase from 'lodash.camelcase'; import { EMAIL, EMAIL_CADENCE, EMAIL_CADENCE_PREFERENCES } from './constants'; import { - fetchCourseListSuccess, - fetchCourseListFetching, - fetchCourseListFailed, fetchNotificationPreferenceFailed, fetchNotificationPreferenceFetching, fetchNotificationPreferenceSuccess, updatePreferenceValue, - updateSelectedCourse, } from './actions'; import { - getCourseList, - getCourseNotificationPreferences, - patchPreferenceToggle, + getNotificationPreferences, postPreferenceToggle, } from './service'; -const normalizeCourses = (responseData) => { - const courseList = responseData.results?.map((enrollment) => ({ - id: enrollment.course.id, - name: enrollment.course.displayName, - })) || []; - - const pagination = { - count: responseData.count, - currentPage: responseData.currentPage, - hasMore: Boolean(responseData.next), - totalPages: responseData.numPages, - }; - - return { - courseList, - pagination, - showPreferences: responseData.showPreferences, - }; -}; - export const normalizeAccountPreferences = (originalData, updateInfo) => { const { app, notificationType, channel, updatedValue, @@ -54,13 +28,8 @@ export const normalizeAccountPreferences = (originalData, updateInfo) => { return originalData; }; -const normalizePreferences = (responseData, courseId) => { - let preferences; - if (courseId) { - preferences = responseData.notificationPreferenceConfig; - } else { - preferences = responseData.data; - } +const normalizePreferences = (responseData) => { + const preferences = responseData.data; const appKeys = Object.keys(preferences); const apps = appKeys.map((appId) => ({ @@ -97,41 +66,20 @@ const normalizePreferences = (responseData, courseId) => { return normalizedPreferences; }; -export const fetchCourseList = (page, pageSize) => ( +export const fetchNotificationPreferences = () => ( async (dispatch) => { try { - dispatch(fetchCourseListFetching()); - const data = await getCourseList(page, pageSize); - const normalizedData = normalizeCourses(camelCaseObject(data)); - dispatch(fetchCourseListSuccess(normalizedData)); - } catch (errors) { - dispatch(fetchCourseListFailed()); - } - } -); - -export const fetchCourseNotificationPreferences = (courseId) => ( - async (dispatch) => { - try { - dispatch(updateSelectedCourse(courseId)); dispatch(fetchNotificationPreferenceFetching()); - const data = await getCourseNotificationPreferences(courseId); - const normalizedData = normalizePreferences(camelCaseObject(data), courseId); - dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData)); + const data = camelCaseObject(await getNotificationPreferences()); + const normalizedData = normalizePreferences(data); + dispatch(fetchNotificationPreferenceSuccess(normalizedData, data.showPreferences)); } catch (errors) { dispatch(fetchNotificationPreferenceFailed()); } } ); -export const setSelectedCourse = courseId => ( - async (dispatch) => { - dispatch(updateSelectedCourse(courseId)); - } -); - export const updatePreferenceToggle = ( - courseId, notificationApp, notificationType, notificationChannel, @@ -149,49 +97,35 @@ export const updatePreferenceToggle = ( )); // Function to handle data normalization and dispatching success - const handleSuccessResponse = (data, isGlobal = false) => { - const processedData = courseId - ? normalizePreferences(camelCaseObject(data), courseId) - : camelCaseObject(data); + const handleSuccessResponse = (data) => { + const processedData = camelCaseObject(data); - dispatch(fetchNotificationPreferenceSuccess(courseId, processedData, isGlobal)); + dispatch(fetchNotificationPreferenceSuccess(processedData, processedData.showPreferences, true)); return processedData; }; - // Function to toggle preference based on context (course-specific or global) - const togglePreference = async (channel, toggleValue, cadence) => { - if (courseId) { - return patchPreferenceToggle( - courseId, - notificationApp, - notificationType, - channel, - channel === EMAIL_CADENCE ? cadence : toggleValue, - ); - } - - return postPreferenceToggle( - notificationApp, - notificationType, - channel, - channel === EMAIL_CADENCE ? undefined : toggleValue, - cadence, - ); - }; + // Function to toggle preference based on context + const togglePreference = async (channel, toggleValue, cadence) => postPreferenceToggle( + notificationApp, + notificationType, + channel, + channel === EMAIL_CADENCE ? undefined : toggleValue, + cadence, + ); // Execute the main preference toggle const data = await togglePreference(notificationChannel, value, emailCadence); - handleSuccessResponse(data, !courseId); + handleSuccessResponse(data); // Handle special case for email notifications if (notificationChannel === EMAIL && value) { const emailCadenceData = await togglePreference( EMAIL_CADENCE, - courseId ? undefined : value, + value, EMAIL_CADENCE_PREFERENCES.DAILY, ); - handleSuccessResponse(emailCadenceData, !courseId); + handleSuccessResponse(emailCadenceData); } } catch (errors) { dispatch(updatePreferenceValue( diff --git a/src/notification-preferences/messages.js b/src/notification-preferences/messages.js index fea8cd3..00c598b 100644 --- a/src/notification-preferences/messages.js +++ b/src/notification-preferences/messages.js @@ -93,7 +93,7 @@ const messages = defineMessages({ }, accountNotificationDescription: { id: 'account.notification.description', - defaultMessage: 'Account-level settings apply to all courses. Notifications for individual courses can be changed within each course and will override account-level settings.', + defaultMessage: 'Account-level settings apply to all courses.', description: 'Account notification description', }, notificationCadenceDescription: {