Skip to content

Commit 3467534

Browse files
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 <[email protected]>
1 parent 5f43f94 commit 3467534

File tree

5 files changed

+69
-4
lines changed

5 files changed

+69
-4
lines changed

src/notification-preferences/NotificationPreferenceApp.jsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@ import React, { useCallback, useMemo } from 'react';
22
import PropTypes from 'prop-types';
33
import { useDispatch, useSelector } from 'react-redux';
44
import { useIntl } from '@edx/frontend-platform/i18n';
5-
import { Collapsible } from '@openedx/paragon';
5+
import { Collapsible, NavItem } from '@openedx/paragon';
6+
import classNames from 'classnames';
67
import messages from './messages';
78
import ToggleSwitch from './ToggleSwitch';
89
import {
910
selectPreferenceAppToggleValue,
11+
selectNonEditablePreferences,
1012
selectPreferencesOfApp,
1113
selectSelectedCourseId,
1214
selectUpdatePreferencesStatus,
1315
} from './data/selectors';
1416
import NotificationPreferenceRow from './NotificationPreferenceRow';
15-
import { updateAppPreferenceToggle } from './data/thunks';
17+
import { updateAppPreferenceToggle, updateChannelPreferenceToggle } from './data/thunks';
1618
import { LOADING_STATUS } from '../constants';
19+
import NOTIFICATION_CHANNELS from './data/constants';
1720

1821
const NotificationPreferenceApp = ({ appId }) => {
1922
const dispatch = useDispatch();
@@ -22,6 +25,18 @@ const NotificationPreferenceApp = ({ appId }) => {
2225
const appPreferences = useSelector(selectPreferencesOfApp(appId));
2326
const appToggle = useSelector(selectPreferenceAppToggleValue(appId));
2427
const updatePreferencesStatus = useSelector(selectUpdatePreferencesStatus());
28+
const nonEditable = useSelector(selectNonEditablePreferences(appId));
29+
30+
const onChannelToggle = useCallback((event) => {
31+
const { id: notificationChannel } = event.target;
32+
const isPreferenceNonEditable = (preference) => nonEditable?.[preference.id]?.includes(notificationChannel);
33+
34+
const hasActivePreferences = appPreferences.some(
35+
(preference) => preference[notificationChannel] && !isPreferenceNonEditable(preference),
36+
);
37+
38+
dispatch(updateChannelPreferenceToggle(courseId, appId, notificationChannel, !hasActivePreferences));
39+
}, [appId, appPreferences, courseId, dispatch, nonEditable]);
2540

2641
const preferences = useMemo(() => (
2742
appPreferences.map(preference => (
@@ -36,10 +51,10 @@ const NotificationPreferenceApp = ({ appId }) => {
3651
dispatch(updateAppPreferenceToggle(courseId, appId, event.target.checked));
3752
// eslint-disable-next-line react-hooks/exhaustive-deps
3853
}, [appId]);
39-
4054
if (!courseId) {
4155
return null;
4256
}
57+
4358
return (
4459
<Collapsible.Advanced open={appToggle} data-testid={`${appId}-app`} className="mb-5">
4560
<Collapsible.Trigger>
@@ -62,7 +77,22 @@ const NotificationPreferenceApp = ({ appId }) => {
6277
<div className="d-flex flex-row header-label">
6378
<span className="col-8 px-0">{intl.formatMessage(messages.typeLabel)}</span>
6479
<span className="d-flex col-4 px-0">
65-
<span className="ml-auto">{intl.formatMessage(messages.webLabel)}</span>
80+
{NOTIFICATION_CHANNELS.map((channel) => (
81+
<NavItem
82+
id={channel}
83+
key={channel}
84+
className={classNames(
85+
'd-flex',
86+
{ 'ml-auto': channel === 'web' },
87+
{ 'mx-auto': channel === 'email' },
88+
{ 'ml-auto mr-0': channel === 'push' },
89+
)}
90+
role="button"
91+
onClick={onChannelToggle}
92+
>
93+
{intl.formatMessage(messages.notificationChannel, { text: channel })}
94+
</NavItem>
95+
))}
6696
</span>
6797
</div>
6898
<div className="my-3">

src/notification-preferences/data/selectors.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ export const selectPreferenceNonEditableChannels = (appId, name) => state => (
5454
state?.notificationPreferences.preferences.nonEditable[appId]?.[name] || []
5555
);
5656

57+
export const selectNonEditablePreferences = appId => state => (
58+
state?.notificationPreferences.preferences.nonEditable[appId] || []
59+
);
60+
5761
export const selectSelectedCourseId = () => state => (
5862
state.notificationPreferences.preferences.selectedCourse
5963
);

src/notification-preferences/data/service.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,10 @@ export const patchPreferenceToggle = async (
4242
const { data } = await getAuthenticatedHttpClient().patch(url, patchData);
4343
return data;
4444
};
45+
46+
export const patchChannelPreferenceToggle = async (courseId, notificationApp, notificationChannel, value) => {
47+
const patchData = snakeCaseObject({ notificationApp, notificationChannel, value });
48+
const url = `${getConfig().LMS_BASE_URL}/api/notifications/channel/configurations/${courseId}`;
49+
const { data } = await getAuthenticatedHttpClient().patch(url, patchData);
50+
return data;
51+
};

src/notification-preferences/data/thunks.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getCourseList,
1515
getCourseNotificationPreferences,
1616
patchAppPreferenceToggle,
17+
patchChannelPreferenceToggle,
1718
patchPreferenceToggle,
1819
} from './service';
1920

@@ -148,3 +149,15 @@ export const updatePreferenceToggle = (
148149
}
149150
}
150151
);
152+
153+
export const updateChannelPreferenceToggle = (courseId, notificationApp, notificationChannel, value) => (
154+
async (dispatch) => {
155+
try {
156+
const data = await patchChannelPreferenceToggle(courseId, notificationApp, notificationChannel, value);
157+
const normalizedData = normalizePreferences(camelCaseObject(data));
158+
dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData));
159+
} catch (errors) {
160+
dispatch(fetchNotificationPreferenceFailed());
161+
}
162+
}
163+
);

src/notification-preferences/messages.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ const messages = defineMessages({
2828
}`,
2929
description: 'Display text for Notification Types',
3030
},
31+
notificationChannel: {
32+
id: 'notification.preference.channel',
33+
defaultMessage: `{
34+
text, select,
35+
web {Web}
36+
email {Email}
37+
push {Push}
38+
other {{text}}
39+
}`,
40+
description: 'Display text for Notification Channel',
41+
},
3142
typeLabel: {
3243
id: 'notification.preference.type.label',
3344
defaultMessage: 'Type',

0 commit comments

Comments
 (0)