From 3467534bc7b838b85a54514fdec08b9dfdeee526 Mon Sep 17 00:00:00 2001 From: ayesha waris <73840786+ayesha-waris@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:56:27 +0500 Subject: [PATCH] feat: make notification channel headings clickable in notification (#983) * feat: make notification channel headings clickable in notification preferences * chore: refactoring the code for readability according to ESLint * refactor: onChannelToggle updated for readability * refactor: onChannelToggle updated * refactor: further simplified onChannelToggle * perf: updated onChannelToggle to improve performance * fix: fixed lint error --------- Co-authored-by: eemaanamir --- .../NotificationPreferenceApp.jsx | 38 +++++++++++++++++-- .../data/selectors.js | 4 ++ src/notification-preferences/data/service.js | 7 ++++ src/notification-preferences/data/thunks.js | 13 +++++++ src/notification-preferences/messages.js | 11 ++++++ 5 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/notification-preferences/NotificationPreferenceApp.jsx b/src/notification-preferences/NotificationPreferenceApp.jsx index ed57e66d9..1ab67b030 100644 --- a/src/notification-preferences/NotificationPreferenceApp.jsx +++ b/src/notification-preferences/NotificationPreferenceApp.jsx @@ -2,18 +2,21 @@ import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useIntl } from '@edx/frontend-platform/i18n'; -import { Collapsible } from '@openedx/paragon'; +import { Collapsible, NavItem } from '@openedx/paragon'; +import classNames from 'classnames'; import messages from './messages'; import ToggleSwitch from './ToggleSwitch'; import { selectPreferenceAppToggleValue, + selectNonEditablePreferences, selectPreferencesOfApp, selectSelectedCourseId, selectUpdatePreferencesStatus, } from './data/selectors'; import NotificationPreferenceRow from './NotificationPreferenceRow'; -import { updateAppPreferenceToggle } from './data/thunks'; +import { updateAppPreferenceToggle, updateChannelPreferenceToggle } from './data/thunks'; import { LOADING_STATUS } from '../constants'; +import NOTIFICATION_CHANNELS from './data/constants'; const NotificationPreferenceApp = ({ appId }) => { const dispatch = useDispatch(); @@ -22,6 +25,18 @@ const NotificationPreferenceApp = ({ appId }) => { const appPreferences = useSelector(selectPreferencesOfApp(appId)); const appToggle = useSelector(selectPreferenceAppToggleValue(appId)); const updatePreferencesStatus = useSelector(selectUpdatePreferencesStatus()); + const nonEditable = useSelector(selectNonEditablePreferences(appId)); + + const onChannelToggle = useCallback((event) => { + const { id: notificationChannel } = event.target; + const isPreferenceNonEditable = (preference) => nonEditable?.[preference.id]?.includes(notificationChannel); + + const hasActivePreferences = appPreferences.some( + (preference) => preference[notificationChannel] && !isPreferenceNonEditable(preference), + ); + + dispatch(updateChannelPreferenceToggle(courseId, appId, notificationChannel, !hasActivePreferences)); + }, [appId, appPreferences, courseId, dispatch, nonEditable]); const preferences = useMemo(() => ( appPreferences.map(preference => ( @@ -36,10 +51,10 @@ const NotificationPreferenceApp = ({ appId }) => { dispatch(updateAppPreferenceToggle(courseId, appId, event.target.checked)); // eslint-disable-next-line react-hooks/exhaustive-deps }, [appId]); - if (!courseId) { return null; } + return ( @@ -62,7 +77,22 @@ const NotificationPreferenceApp = ({ appId }) => {
{intl.formatMessage(messages.typeLabel)} - {intl.formatMessage(messages.webLabel)} + {NOTIFICATION_CHANNELS.map((channel) => ( + + {intl.formatMessage(messages.notificationChannel, { text: channel })} + + ))}
diff --git a/src/notification-preferences/data/selectors.js b/src/notification-preferences/data/selectors.js index d918705c5..ffbc5d91f 100644 --- a/src/notification-preferences/data/selectors.js +++ b/src/notification-preferences/data/selectors.js @@ -54,6 +54,10 @@ export const selectPreferenceNonEditableChannels = (appId, name) => state => ( state?.notificationPreferences.preferences.nonEditable[appId]?.[name] || [] ); +export const selectNonEditablePreferences = appId => state => ( + state?.notificationPreferences.preferences.nonEditable[appId] || [] +); + 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 c3deb5616..50a1e04c0 100644 --- a/src/notification-preferences/data/service.js +++ b/src/notification-preferences/data/service.js @@ -42,3 +42,10 @@ export const patchPreferenceToggle = async ( const { data } = await getAuthenticatedHttpClient().patch(url, patchData); return data; }; + +export const patchChannelPreferenceToggle = async (courseId, notificationApp, notificationChannel, value) => { + const patchData = snakeCaseObject({ notificationApp, notificationChannel, value }); + const url = `${getConfig().LMS_BASE_URL}/api/notifications/channel/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 86ff5c0f8..222201b52 100644 --- a/src/notification-preferences/data/thunks.js +++ b/src/notification-preferences/data/thunks.js @@ -14,6 +14,7 @@ import { getCourseList, getCourseNotificationPreferences, patchAppPreferenceToggle, + patchChannelPreferenceToggle, patchPreferenceToggle, } from './service'; @@ -148,3 +149,15 @@ export const updatePreferenceToggle = ( } } ); + +export const updateChannelPreferenceToggle = (courseId, notificationApp, notificationChannel, value) => ( + async (dispatch) => { + try { + const data = await patchChannelPreferenceToggle(courseId, notificationApp, notificationChannel, value); + const normalizedData = normalizePreferences(camelCaseObject(data)); + dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData)); + } catch (errors) { + dispatch(fetchNotificationPreferenceFailed()); + } + } +); diff --git a/src/notification-preferences/messages.js b/src/notification-preferences/messages.js index bd7f6ca25..b48349291 100644 --- a/src/notification-preferences/messages.js +++ b/src/notification-preferences/messages.js @@ -28,6 +28,17 @@ const messages = defineMessages({ }`, description: 'Display text for Notification Types', }, + notificationChannel: { + id: 'notification.preference.channel', + defaultMessage: `{ + text, select, + web {Web} + email {Email} + push {Push} + other {{text}} + }`, + description: 'Display text for Notification Channel', + }, typeLabel: { id: 'notification.preference.type.label', defaultMessage: 'Type',