Skip to content

Commit

Permalink
feat: added notification preference ui
Browse files Browse the repository at this point in the history
  • Loading branch information
adeel.tajamul committed May 4, 2023
1 parent 7cf9294 commit ed8256a
Show file tree
Hide file tree
Showing 17 changed files with 737 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/account-settings/AccountSettingsPage.messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,11 @@ const messages = defineMessages({
defaultMessage: 'No value set.',
description: 'The placeholder for an empty but uneditable field when there is no administrator',
},
'notification.preferences.notifications.text': {
id: 'notification.preferences.notifications.text',
defaultMessage: 'Notifications',
description: 'Notifications Text',
},
});

export default messages;
15 changes: 14 additions & 1 deletion src/account-settings/JumpNav.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { breakpoints, useWindowSize } from '@edx/paragon';
import { breakpoints, useWindowSize, Icon } from '@edx/paragon';
import { OpenInNew } from '@edx/paragon/icons';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { NavHashLink } from 'react-router-hash-link';
import Scrollspy from 'react-scrollspy';
import { Link } from 'react-router-dom';
import messages from './AccountSettingsPage.messages';

const JumpNav = ({
Expand Down Expand Up @@ -67,6 +69,17 @@ const JumpNav = ({
</NavHashLink>
</li>
</Scrollspy>
<hr className="ml-0" style={{ width: '180px' }} />
<Scrollspy
className="list-unstyled"
>
<li>
<Link to="/notifications" target="_blank" rel="noopener noreferrer">
<span>{intl.formatMessage(messages['notification.preferences.notifications.text'])}</span>
<Icon className="d-inline-block align-bottom ml-1" src={OpenInNew} />
</Link>
</li>
</Scrollspy>
</div>
);
};
Expand Down
92 changes: 92 additions & 0 deletions src/account-settings/test/__snapshots__/JumpNav.test.jsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,52 @@ exports[`JumpNav should not render Optional Information link 1`] = `
</a>
</li>
</ul>
<hr
className="ml-0"
style={
Object {
"width": "180px",
}
}
/>
<ul
className="list-unstyled"
style={Object {}}
>
<li
className=""
>
<a
href="/notifications"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
<span>
Notifications
</span>
<span
className="pgn__icon d-inline-block align-bottom ml-1"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</a>
</li>
</ul>
</div>
`;

Expand Down Expand Up @@ -164,5 +210,51 @@ exports[`JumpNav should render Optional Information link 1`] = `
</a>
</li>
</ul>
<hr
className="ml-0"
style={
Object {
"width": "180px",
}
}
/>
<ul
className="list-unstyled"
style={Object {}}
>
<li
className=""
>
<a
href="/notifications"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
<span>
Notifications
</span>
<span
className="pgn__icon d-inline-block align-bottom ml-1"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</a>
</li>
</ul>
</div>
`;
5 changes: 5 additions & 0 deletions src/data/reducers.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
/* eslint-disable import/no-named-default */
import { combineReducers } from 'redux';

import {
reducer as accountSettingsReducer,
storeName as accountSettingsStoreName,
} from '../account-settings';
import {
default as notificationPreferencesReducer,
} from '../notification-preferences/data/reducers';

const createRootReducer = () => combineReducers({
[accountSettingsStoreName]: accountSettingsReducer,
notificationPreferences: notificationPreferencesReducer,
});
export default createRootReducer;
4 changes: 4 additions & 0 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import messages from './i18n';

import './index.scss';
import Head from './head/Head';
import NotificationCourses from './notification-preferences/NotificationCourses';
import NotificationPreferences from './notification-preferences/NotificationPreferences';

subscribe(APP_READY, () => {
ReactDOM.render(
Expand All @@ -32,6 +34,8 @@ subscribe(APP_READY, () => {
<Header />
<main className="flex-grow-1">
<Switch>
<Route path="/notifications/:courseId" component={NotificationPreferences} />
<Route path="/notifications" component={NotificationCourses} />
<Route path="/id-verification" component={IdVerificationPage} />
<Route exact path="/" component={AccountSettingsPage} />
<Route path="/notfound" component={NotFoundPage} />
Expand Down
28 changes: 28 additions & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,31 @@ $fa-font-path: "~font-awesome/fonts";
font-weight: normal;
}
}

.notification-heading {
line-height: 36px;
font-weight: 700;
font-size: 32px;
}

.notification-course-title {
line-height: 28px;
font-weight: 700;
font-size: 18px;
}

.w-66 {
width: 66%;
}

.w-34 {
width: 34%;
}

.notification-help-text {
font-size: 14px;
font-weight: 400;
line-height: 28px;
height: 28px;
color: #707070;
}
46 changes: 46 additions & 0 deletions src/notification-preferences/NotificationCourses.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { fetchCourseList } from './data/thunks';
import { courseListStatus, getCourseList } from './data/selectors';
import { IDLE_STATUS } from '../constants';
import { messages } from './messages';

const NotificationCourses = ({ intl }) => {
const dispatch = useDispatch();
const courseStatus = useSelector(courseListStatus());
const coursesList = useSelector(getCourseList());
useEffect(() => {
if (courseStatus === IDLE_STATUS) {
dispatch(fetchCourseList());
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="ml-auto mr-auto" style={{ width: '828px' }}>
<h2 className="notification-heading mt-6 mb-5.5">
{intl.formatMessage(messages.notificationHeading)}
</h2>
<div>
{
coursesList.map(course => (
<Link
to={`/notifications/${course.id}`}
>
<div className="mb-4">
{course.name}
</div>
</Link>
))
}
</div>
</div>
);
};

NotificationCourses.propTypes = {
intl: intlShape.isRequired,
};

export default injectIntl(NotificationCourses);
70 changes: 70 additions & 0 deletions src/notification-preferences/NotificationPreferenceGroup.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Collapsible } from '@edx/paragon';
import { messages } from './messages';
import ToggleSwitch from './ToggleSwitch';
import {
getCoursePreferenceGroup,
getSelectedCourse,
} from './data/selectors';
import NotificationPreferenceRow from './NotificationPreferenceRow';
import { updateGroupValue } from './data/actions';

const NotificationPreferenceGroup = ({ intl, groupKey }) => {
const dispatch = useDispatch();
const courseId = useSelector(getSelectedCourse());
const preferenceGroup = useSelector(getCoursePreferenceGroup(groupKey));
const [groupToggle, setGroupToggle] = useState(true);
useEffect(() => {}, [courseId]);
const onChangeGroupSettings = (checked) => {
setGroupToggle(checked);
dispatch(updateGroupValue(courseId, groupKey, checked));
};
if (!courseId) {
return null;
}
return (
<Collapsible.Advanced open={groupToggle}>
<Collapsible.Trigger>
<div className="d-flex">
<span className="ml-0 mr-auto">
{intl.formatMessage(messages.notificationGroupTitle, { key: groupKey })}
</span>
<span className="ml-auto mr-0">
<ToggleSwitch value={groupToggle} onChange={onChangeGroupSettings} />
</span>
</div>
<hr />
</Collapsible.Trigger>
<Collapsible.Body>
<div className="d-flex flex-row notification-help-text">
<span className="w-66 ">{intl.formatMessage(messages.notificationHelpType)}</span>
<span className="d-flex w-34">
<span className="ml-0 mr-auto">{intl.formatMessage(messages.notificationHelpWeb)}</span>
<span className="mx-auto">{intl.formatMessage(messages.notificationHelpEmail)}</span>
<span className="ml-auto mr-0 pr-2.5">{intl.formatMessage(messages.notificationHelpPush)}</span>
</span>
</div>
<div className="mt-3 pb-5.5">
{
Object.keys(preferenceGroup).map(preferenceName => (
<NotificationPreferenceRow
groupKey={groupKey}
preferenceName={preferenceName}
/>
))
}
</div>
</Collapsible.Body>
</Collapsible.Advanced>
);
};

NotificationPreferenceGroup.propTypes = {
intl: intlShape.isRequired,
groupKey: PropTypes.string.isRequired,
};

export default injectIntl(NotificationPreferenceGroup);
51 changes: 51 additions & 0 deletions src/notification-preferences/NotificationPreferenceRow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { messages } from './messages';
import ToggleSwitch from './ToggleSwitch';
import { getPreferenceAttribute } from './data/selectors';
import { updatePreferenceValue } from './data/actions';

const NotificationPreferenceRow = ({ intl, groupKey, preferenceName }) => {
const dispatch = useDispatch();
const preference = useSelector(getPreferenceAttribute(groupKey, preferenceName));
const onToggle = (checked, notificationFor) => {
dispatch(updatePreferenceValue(groupKey, preferenceName, notificationFor, checked));
};
return (
<div className="d-flex flex-row mb-3">
<span className="w-66">
{intl.formatMessage(messages.notificationTitle, { text: preferenceName })}
</span>
<span className="d-flex w-34">
<span className="ml-0 mr-auto">
<ToggleSwitch
value={preference.web}
onChange={(checked) => onToggle(checked, 'web')}
/>
</span>
<span className="mx-auto">
<ToggleSwitch
value={preference.email}
onChange={(checked) => onToggle(checked, 'email')}
/>
</span>
<span className="ml-auto mr-0">
<ToggleSwitch
value={preference.push}
onChange={(checked) => onToggle(checked, 'push')}
/>
</span>
</span>
</div>
);
};

NotificationPreferenceRow.propTypes = {
intl: intlShape.isRequired,
groupKey: PropTypes.string.isRequired,
preferenceName: PropTypes.string.isRequired,
};

export default injectIntl(NotificationPreferenceRow);
Loading

0 comments on commit ed8256a

Please sign in to comment.