-
Notifications
You must be signed in to change notification settings - Fork 124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added notification preference ui #784
Conversation
1fe528c
to
ed8256a
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## master #784 +/- ##
==========================================
- Coverage 38.63% 36.04% -2.60%
==========================================
Files 110 121 +11
Lines 2288 2458 +170
Branches 624 652 +28
==========================================
+ Hits 884 886 +2
- Misses 1319 1483 +164
- Partials 85 89 +4
☔ View full report in Codecov by Sentry. |
ed8256a
to
5a32386
Compare
5a32386
to
b67fbb7
Compare
|
||
// eslint-disable-next-line import/prefer-default-export | ||
export const messages = defineMessages({ | ||
notificationHeading: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add "Notifications" instead of "Notification" as defaultMessage: 'Notification',
<Link | ||
to={`/notifications/${course.id}`} | ||
> | ||
<div className="mb-4"> | ||
{course.name} | ||
</div> | ||
</Link> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forward arrow as per figma is missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
if (notificationStatus === IDLE_STATUS) { | ||
dispatch(fetchCourseNotificationPreferences(courseId)); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove this lint disable next line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependencies it requires will cause few extra re-renders. So disabling lint for this line is better.
return null; | ||
} | ||
return ( | ||
<div className="ml-auto mr-auto" style={{ width: '828px' }}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can add css class for width 828px because same width is using in NotificationCourses.jsx also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added class
<span className="ml-auto mr-0 pr-2.5">{intl.formatMessage(messages.notificationHelpPush)}</span> | ||
</span> | ||
</div> | ||
<div className="mt-3 pb-5.5"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
padding bottom is 48px in figma currently its showing 64px
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
// import { getConfig } from '@edx/frontend-platform'; | ||
// import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove commented code if they are of no use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are required imports that will be added when integrating API's.
// const url = `${getConfig().LMS_BASE_URL}/api/notifications/${courseId}`; | ||
// const { data } = await getAuthenticatedHttpClient().get(url); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove commented code if they are of no use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is API call for it that will bring data whose structure will be same as dummy data. It will be removed when integrating API's
// 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' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
title issue replace with "The Foundations of Happiness at Work"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a dummy data... it will be removed when integrating api's.
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' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
title issue kindly replace with "Empathy and Emotional Intelligence at Work"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same. This is also dummy data...
b67fbb7
to
6eb8974
Compare
@@ -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': { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
notification.preferences.notifications.label
src/account-settings/JumpNav.jsx
Outdated
{showNotificationMenu | ||
&& ( | ||
<> | ||
<hr className="ml-0" style={{ width: '180px' }} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not add specific width for the divider. It should inherit the width from a parent. Adjust the parent width according to Figma.
src/data/reducers.js
Outdated
@@ -1,11 +1,16 @@ | |||
/* eslint-disable import/no-named-default */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
default export the notificationPreferencesReducer from the component.
src/data/reducers.js
Outdated
import { | ||
default as notificationPreferencesReducer, | ||
} from '../notification-preferences/data/reducers'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import notificationPreferencesReducer from '../notification-preferences/data/reducers';
src/index.scss
Outdated
.w-66 { | ||
width: 66%; | ||
} | ||
|
||
.w-34 { | ||
width: 34%; | ||
} | ||
|
||
.w-828px { | ||
width: 828px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using responsive class from paragon.
preferenceName: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default injectIntl(NotificationPreferenceRow); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memoize this component
const onToggle = (checked, notificationFor) => { | ||
dispatch(updatePreferenceValue(groupKey, preferenceName, notificationFor, checked)); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memoize this callback
}; | ||
return ( | ||
<div className="d-flex flex-row mb-3"> | ||
<span className="w-66"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use col class from paragon
onChange: () => null, | ||
}; | ||
|
||
export default ToggleSwitch; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memoize this component
export const getCourse = courseId => state => ( | ||
state.notificationPreferences.courses.courses.find( | ||
element => element.id === courseId, | ||
) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change element to course.
call getCourseList selector instead of state.notificationPreferences.courses.courses
6eb8974
to
1282815
Compare
if (courseStatus === LOADING_STATUS) { | ||
return ( | ||
<div className="d-flex h-100"> | ||
<Spinner | ||
variant="primary" | ||
animation="border" | ||
className="mx-auto my-auto" | ||
style={{ width: '4rem', height: '4rem' }} | ||
/> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can memo this like
const SpinnerComp = useMemo(() => {
if (courseStatus === LOADING_STATUS) {
return (
<Spinner
variant="primary"
animation="border"
className="mx-auto my-auto"
style={{ width: '4rem', height: '4rem' }}
/>
);
}
return null;
}, [courseStatus]);
if (SpinnerComp) { return SpinnerComp; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this component is not being rendered multiple times, hence memoization is not required. Excessive memoization should be avoided in code
intl: intlShape.isRequired, | ||
}; | ||
|
||
export default injectIntl(NotificationCourses); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memo this
export default injectIntl(React.memo(NotificationCourses));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NotificationCourses component is not taking any props (except intl). I don't think that React.memo will be helpful
const preference = useSelector(getPreferenceAttribute(groupId, preferenceName)); | ||
const onToggle = useCallback((checked, notificationChannel) => { | ||
dispatch(updatePreferenceValue(groupId, preferenceName, notificationChannel, checked)); | ||
}, [dispatch, groupId, preferenceName]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to pass dispatch here, instead pass updatePreferenceValue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updatePreferenceValue is a function. I don't think we need to pass function in dependency list. Added dispatch to avoid disabling lint for this line
if (notificationStatus === LOADING_STATUS) { | ||
return ( | ||
<div className="d-flex h-100"> | ||
<Spinner | ||
variant="primary" | ||
animation="border" | ||
className="mx-auto my-auto" | ||
style={{ width: '4rem', height: '4rem' }} | ||
/> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can memo this like
const SpinnerComp = useMemo(() => {
if (notificationStatus === LOADING_STATUS) {
return (
<Spinner
variant="primary"
animation="border"
className="mx-auto my-auto"
style={{ width: '4rem', height: '4rem' }}
/>
);
}
return null;
}, [notificationStatus]);
if (SpinnerComp) { return SpinnerComp; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memoization not required here
); | ||
}; | ||
|
||
export default NotificationPreferences; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
memo this component
export default React.memo(NotificationPreferences);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NotificationPreferences component has no props. React.memo will not be of any use
import PropTypes from 'prop-types'; | ||
|
||
const ToggleSwitch = ({ value, onChange }) => ( | ||
<Form.Switch checked={value} onChange={(event) => onChange(event.target.checked)} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add callback function for onChange
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onChange is being passed as prop from NotificationPreferenceGroup and from NotificationPreferenceRow. These calls have been wrapped in useCallback there
1282815
to
44a712b
Compare
const preferenceGroups = useSelector(getPreferenceGroupIds()); | ||
const preferencesList = useMemo(() => ( | ||
preferenceGroups.map(key => ( | ||
<NotificationPreferenceGroup groupId={key} key={key} /> | ||
)) | ||
), [preferenceGroups]); | ||
useEffect(() => { | ||
dispatch(updateSelectedCourse(courseId)); | ||
if (courseStatus !== SUCCESS_STATUS) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add newline between logics
44a712b
to
01f24b4
Compare
Added Notification Preferences UI components.
Tests will be added after api integration