diff --git a/src/Notifications/NotificationSections.jsx b/src/Notifications/NotificationSections.jsx
index 35ba370..2048ce5 100644
--- a/src/Notifications/NotificationSections.jsx
+++ b/src/Notifications/NotificationSections.jsx
@@ -3,7 +3,7 @@ import { Button } from '@edx/paragon';
import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import isEmpty from 'lodash/isEmpty';
-import { messages } from './messages';
+import messages from './messages';
import NotificationRowItem from './NotificationRowItem';
import { markAllNotificationsAsRead } from './data/thunks';
import { selectNotificationsByIds, selectPaginationData, selectSelectedAppName } from './data/selectors';
@@ -14,8 +14,8 @@ const NotificationSections = () => {
const intl = useIntl();
const dispatch = useDispatch();
const selectedAppName = useSelector(selectSelectedAppName());
- const notifications = useSelector(selectNotificationsByIds);
- const paginationData = useSelector(selectPaginationData());
+ const notifications = useSelector(selectNotificationsByIds(selectedAppName));
+ const { currentPage, numPages } = useSelector(selectPaginationData());
const { today = [], earlier = [] } = useMemo(
() => splitNotificationsByTime(notifications),
[notifications],
@@ -69,7 +69,7 @@ const NotificationSections = () => {
{renderNotificationSection('today', today)}
{renderNotificationSection('earlier', earlier)}
- {paginationData.currentPage < paginationData.numPages && (
+ {currentPage < numPages && (
diff --git a/src/Notifications/NotificationTabs.jsx b/src/Notifications/NotificationTabs.jsx
index 78e704d..490ec06 100644
--- a/src/Notifications/NotificationTabs.jsx
+++ b/src/Notifications/NotificationTabs.jsx
@@ -1,3 +1,4 @@
+/* eslint-disable react-hooks/exhaustive-deps */
import React, { useCallback, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Tab, Tabs } from '@edx/paragon';
@@ -13,16 +14,16 @@ const NotificationTabs = () => {
const selectedAppName = useSelector(selectSelectedAppName());
const notificationUnseenCounts = useSelector(selectNotificationTabsCount());
const notificationTabs = useSelector(selectNotificationTabs());
- const paginationData = useSelector(selectPaginationData());
+ const { currentPage } = useSelector(selectPaginationData());
useEffect(() => {
- dispatch(fetchNotificationList({ appName: selectedAppName, page: paginationData.currentPage, pageSize: 10 }));
+ dispatch(fetchNotificationList({ appName: selectedAppName, page: currentPage, pageSize: 10 }));
if (selectedAppName) { dispatch(markNotificationsAsSeen(selectedAppName)); }
- }, [dispatch, paginationData.currentPage, selectedAppName]);
+ }, [currentPage, selectedAppName]);
const handleActiveTab = useCallback((appName) => {
dispatch(updateAppNameRequest({ appName }));
- }, [dispatch]);
+ }, []);
const tabArray = useMemo(() => notificationTabs?.map((appName) => (
state => state.noti
export const selectShowNotificationTray = () => state => state.notifications.showNotificationTray;
-export const selectNotifications = () => state => state.notifications.notification;
+export const selectNotifications = () => state => state.notifications.notifications;
-export const selectNotificationsByIds = createSelector(
- state => state.notifications.notifications,
- state => state.notifications.apps[state.notifications.appName] || [],
- (notifications, notificationIds) => notificationIds.map(notificationId => notifications[notificationId]),
+export const selectNotificationsByIds = (appName) => createSelector(
+ selectNotifications(),
+ selectSelectedAppNotificationIds(appName),
+ (notifications, notificationIds) => notificationIds.map((notificationId) => notifications[notificationId]) || [],
);
export const selectSelectedAppName = () => state => state.notifications.appName;
diff --git a/src/Notifications/data/slice.js b/src/Notifications/data/slice.js
index c3fd4f5..8751475 100644
--- a/src/Notifications/data/slice.js
+++ b/src/Notifications/data/slice.js
@@ -1,15 +1,17 @@
/* eslint-disable no-param-reassign */
import { createSlice } from '@reduxjs/toolkit';
-export const IDLE = 'idle';
-export const LOADING = 'loading';
-export const LOADED = 'loaded';
-export const FAILED = 'failed';
-export const DENIED = 'denied';
+export const RequestStatus = {
+ IDLE: 'idle',
+ LOADING: 'in-progress',
+ LOADED: 'successful',
+ FAILED: 'failed',
+ DENIED: 'denied',
+};
const initialState = {
notificationStatus: 'idle',
- appName: 'reminders',
+ appName: 'discussions',
appsId: [],
apps: {},
notifications: {},
@@ -26,65 +28,62 @@ const slice = createSlice({
name: 'notifications',
initialState,
reducers: {
- fetchNotificationDenied: (state, { payload }) => {
- state.appName = payload.appName;
- state.notificationStatus = DENIED;
+ fetchNotificationDenied: (state) => {
+ state.notificationStatus = RequestStatus.DENIED;
},
- fetchNotificationFailure: (state, { payload }) => {
- state.appName = payload.appName;
- state.notificationStatus = FAILED;
+ fetchNotificationFailure: (state) => {
+ state.notificationStatus = RequestStatus.FAILED;
},
- fetchNotificationRequest: (state, { payload }) => {
- if (state.appName !== payload.appName) { state.apps[payload.appName] = []; }
- state.appName = payload.appName;
- state.notificationStatus = LOADING;
+ fetchNotificationRequest: (state) => {
+ state.notificationStatus = RequestStatus.LOADING;
},
fetchNotificationSuccess: (state, { payload }) => {
- const { notifications, numPages, currentPage } = payload;
- const newNotificationIds = notifications.map(notification => notification.id.toString());
+ const {
+ newNotificationIds, notificationsKeyValuePair, numPages, currentPage,
+ } = payload;
const existingNotificationIds = state.apps[state.appName];
- const notificationsKeyValuePair = notifications.reduce((acc, obj) => { acc[obj.id] = obj; return acc; }, {});
- const currentAppCount = state.tabsCount[state.appName];
state.apps[state.appName] = Array.from(new Set([...existingNotificationIds, ...newNotificationIds]));
state.notifications = { ...state.notifications, ...notificationsKeyValuePair };
- state.tabsCount.count -= currentAppCount;
+ state.tabsCount.count -= state.tabsCount[state.appName];
state.tabsCount[state.appName] = 0;
- state.notificationStatus = LOADED;
+ state.notificationStatus = RequestStatus.LOADED;
state.pagination.numPages = numPages;
state.pagination.currentPage = currentPage;
},
fetchNotificationsCountDenied: (state) => {
- state.notificationStatus = DENIED;
+ state.notificationStatus = RequestStatus.DENIED;
},
fetchNotificationsCountFailure: (state) => {
- state.notificationStatus = FAILED;
+ state.notificationStatus = RequestStatus.FAILED;
},
fetchNotificationsCountRequest: (state) => {
- state.notificationStatus = LOADING;
+ state.notificationStatus = RequestStatus.LOADING;
},
fetchNotificationsCountSuccess: (state, { payload }) => {
- const { countByAppName, count, showNotificationTray } = payload;
+ const {
+ countByAppName, appIds, apps, count, showNotificationTray,
+ } = payload;
state.tabsCount = { count, ...countByAppName };
- state.appsId = Object.keys(countByAppName);
- state.apps = Object.fromEntries(Object.keys(countByAppName).map(key => [key, []]));
+ state.appsId = appIds;
+ state.apps = apps;
state.showNotificationTray = showNotificationTray;
- state.notificationStatus = LOADED;
+ state.notificationStatus = RequestStatus.LOADED;
},
markNotificationsAsSeenRequest: (state) => {
- state.notificationStatus = LOADING;
+ state.notificationStatus = RequestStatus.LOADING;
},
markNotificationsAsSeenSuccess: (state) => {
- state.notificationStatus = LOADED;
+ state.notificationStatus = RequestStatus.LOADED;
},
markNotificationsAsSeenDenied: (state) => {
- state.notificationStatus = DENIED;
+ state.notificationStatus = RequestStatus.DENIED;
},
markNotificationsAsSeenFailure: (state) => {
- state.notificationStatus = FAILED;
+ state.notificationStatus = RequestStatus.FAILED;
},
markAllNotificationsAsReadRequest: (state) => {
- state.notificationStatus = LOADING;
+ state.notificationStatus = RequestStatus.LOADING;
},
markAllNotificationsAsReadSuccess: (state) => {
const updatedNotifications = Object.fromEntries(
@@ -93,27 +92,27 @@ const slice = createSlice({
]),
);
state.notifications = updatedNotifications;
- state.notificationStatus = LOADED;
+ state.notificationStatus = RequestStatus.LOADED;
},
markAllNotificationsAsReadDenied: (state) => {
- state.notificationStatus = DENIED;
+ state.notificationStatus = RequestStatus.DENIED;
},
markAllNotificationsAsReadFailure: (state) => {
- state.notificationStatus = FAILED;
+ state.notificationStatus = RequestStatus.FAILED;
},
markNotificationsAsReadRequest: (state) => {
- state.notificationStatus = LOADING;
+ state.notificationStatus = RequestStatus.LOADING;
},
markNotificationsAsReadSuccess: (state, { payload }) => {
const date = new Date().toISOString();
state.notifications[payload.id] = { ...state.notifications[payload.id], lastRead: date };
- state.notificationStatus = LOADED;
+ state.notificationStatus = RequestStatus.LOADED;
},
markNotificationsAsReadDenied: (state) => {
- state.notificationStatus = DENIED;
+ state.notificationStatus = RequestStatus.DENIED;
},
markNotificationsAsReadFailure: (state) => {
- state.notificationStatus = FAILED;
+ state.notificationStatus = RequestStatus.FAILED;
},
resetNotificationStateRequest: () => initialState,
updateAppNameRequest: (state, { payload }) => {
diff --git a/src/Notifications/data/thunks.js b/src/Notifications/data/thunks.js
index a4ff8ac..1e702b2 100644
--- a/src/Notifications/data/thunks.js
+++ b/src/Notifications/data/thunks.js
@@ -1,4 +1,3 @@
-import { camelCaseObject } from '@edx/frontend-platform';
import {
fetchNotificationSuccess,
fetchNotificationRequest,
@@ -27,14 +26,29 @@ import {
} from './api';
import { getHttpErrorStatus } from '../utils';
-export const fetchNotificationList = ({
- appName, page, pageSize,
-}) => (
+const normalizeNotificationCounts = ({ countByAppName, count, showNotificationTray }) => {
+ const appIds = Object.keys(countByAppName);
+ const apps = appIds.reduce((acc, appId) => { acc[appId] = []; return acc; }, {});
+ return {
+ countByAppName, appIds, apps, count, showNotificationTray,
+ };
+};
+
+const normalizeNotifications = ({ notifications }) => {
+ const newNotificationIds = notifications.map(notification => notification.id.toString());
+ const notificationsKeyValuePair = notifications.reduce((acc, obj) => { acc[obj.id] = obj; return acc; }, {});
+ return {
+ newNotificationIds, notificationsKeyValuePair,
+ };
+};
+
+export const fetchNotificationList = ({ appName, page, pageSize }) => (
async (dispatch) => {
try {
dispatch(fetchNotificationRequest({ appName }));
const data = await getNotifications(appName, page, pageSize);
- dispatch(fetchNotificationSuccess(data));
+ const normalisedData = normalizeNotifications((data));
+ dispatch(fetchNotificationSuccess({ ...normalisedData, numPages: data.numPages, currentPage: data.currentPage }));
} catch (error) {
if (getHttpErrorStatus(error) === 403) {
dispatch(fetchNotificationDenied(appName));
@@ -50,7 +64,13 @@ export const fetchAppsNotificationCount = () => (
try {
dispatch(fetchNotificationsCountRequest());
const data = await getNotificationCounts();
- dispatch(fetchNotificationsCountSuccess(camelCaseObject(data)));
+ const normalisedData = normalizeNotificationCounts((data));
+ dispatch(fetchNotificationsCountSuccess({
+ ...normalisedData,
+ countByAppName: data.countByAppName,
+ count: data.count,
+ showNotificationTray: data.showNotificationTray,
+ }));
} catch (error) {
if (getHttpErrorStatus(error) === 403) {
dispatch(fetchNotificationsCountDenied());
diff --git a/src/Notifications/index.jsx b/src/Notifications/index.jsx
index 419cc4b..30d28b9 100644
--- a/src/Notifications/index.jsx
+++ b/src/Notifications/index.jsx
@@ -13,33 +13,33 @@ import { selectNotificationTabsCount } from './data/selectors';
import { resetNotificationState } from './data/thunks';
import { useIsOnLargeScreen, useIsOnMediumScreen } from './data/hook';
import NotificationTabs from './NotificationTabs';
-import { messages } from './messages';
+import messages from './messages';
const Notifications = () => {
const intl = useIntl();
const dispatch = useDispatch();
const popoverRef = useRef(null);
const buttonRef = useRef(null);
- const [showNotificationTray, setShowNotificationTray] = useState(false);
+ const [enableNotificationTray, setEnableNotificationTray] = useState(false);
const notificationCounts = useSelector(selectNotificationTabsCount());
const isOnMediumScreen = useIsOnMediumScreen();
const isOnLargeScreen = useIsOnLargeScreen();
const hideNotificationTray = useCallback(() => {
- setShowNotificationTray(prevState => !prevState);
+ setEnableNotificationTray(prevState => !prevState);
}, []);
- const handleClickOutside = useCallback((event) => {
+ const handleClickOutsideNotificationTray = useCallback((event) => {
if (!popoverRef.current?.contains(event.target) && !buttonRef.current?.contains(event.target)) {
- setShowNotificationTray(false);
+ setEnableNotificationTray(false);
}
}, []);
useEffect(() => {
- document.addEventListener('mousedown', handleClickOutside);
+ document.addEventListener('mousedown', handleClickOutsideNotificationTray);
return () => {
- document.removeEventListener('mousedown', handleClickOutside);
+ document.removeEventListener('mousedown', handleClickOutsideNotificationTray);
dispatch(resetNotificationState());
};
}, []);
@@ -50,7 +50,7 @@ const Notifications = () => {
key="bottom"
placement="bottom"
id="notificationTray"
- show={showNotificationTray}
+ show={enableNotificationTray}
overlay={(
{
>
{
commentLiked: { icon: ThumbUpOutline, class: 'text-primary-500' },
edited: { icon: EditOutline, class: 'text-primary-500' },
};
- return iconMap[type] || null;
+ return iconMap[type] || { icon: PostOutline, class: 'text-primary-500' };
};
diff --git a/src/common/time-locale.js b/src/common/time-locale.js
index 19ae993..4a618dd 100644
--- a/src/common/time-locale.js
+++ b/src/common/time-locale.js
@@ -1,5 +1,4 @@
-// eslint-disable-next-line no-unused-vars
-export default function timeLocale(number, index, totalSec) {
+export default function timeLocale(number, index) {
return [
['just now', 'right now'],
['%ss', 'in %s seconds'],
diff --git a/src/index.scss b/src/index.scss
index 75abe8b..e30b11d 100644
--- a/src/index.scss
+++ b/src/index.scss
@@ -122,60 +122,67 @@ $white: #fff;
}
}
-.content{
- b{
- color: #00262B;
- font-weight: 500;
+.content {
+
+ b {
+ color: #00262B !important;
+ font-weight: 500 !important;
}
+
}
-.font-size-18{
+.font-size-18 {
font-size: 18px !important;
}
-.font-size-12{
+.font-size-12 {
font-size: 12px;
}
-.font-size-14{
+.font-size-14 {
font-size: 14px;
}
-.py-10px{
+.py-10px {
padding-top: 10px;
padding-bottom: 10px;
}
-.pb-10px{
+.pb-10px {
padding-bottom: 10px;
}
-.line-height-24{
+.line-height-24 {
line-height: 24px;
}
-.line-height-20{
+.line-height-20 {
line-height: 20px;
}
-.line-height-10{
+.line-height-10 {
line-height: 10px !important;
}
-.icon-size-20{
+.icon-size-20 {
width: 20px !important;
height: 20px !important;
}
-.cursor-pointer{
+.cursor-pointer {
cursor: pointer;
}
-.notification-button{
+.notification-button {
width: 36px;
height: 36px;
}
-.notification-badge{
+.notification-icon{
+ height: 23.33px !important;
+ width: 23.33px !important;
+}
+
+.notification-badge {
position: absolute;
margin-top: 18px;
margin-left: -21px;
@@ -183,7 +190,7 @@ $white: #fff;
font-size: 9px !important;
}
-.popover{
+.popover {
max-height: calc(100% - 68px);
min-height: 1220px;
filter: none;
@@ -201,7 +208,7 @@ $white: #fff;
display: none;
}
- .expandable{
+ .expandable {
position: relative !important;
margin-left: 4px;
padding: 2px 5px;
@@ -209,36 +216,42 @@ $white: #fff;
font-size: 9px;
}
- .dropdown-toggle{
+ .dropdown-toggle {
font-size: 14px;
padding-top: 0px !important;
padding-bottom: 12px !important;
- div{
+
+ div {
min-height: 6px !important;
min-width: 6px !important;
}
+
}
- .dropdown-item{
+ .dropdown-item {
font-size: 14px;
font-weight: 500;
}
- .notification-content{
- .notification-item-content{
- p{
+ .notification-content {
+
+ .notification-item-content {
+
+ p {
margin-bottom: 0px;
}
- b{
+
+ b {
color: #00262B;
}
+
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
}
- .unread{
+ .unread {
height: 10px;
width: 10px;
}
diff --git a/src/learning-header/AuthenticatedUserDropdown.jsx b/src/learning-header/AuthenticatedUserDropdown.jsx
index 680b437..2ac7beb 100644
--- a/src/learning-header/AuthenticatedUserDropdown.jsx
+++ b/src/learning-header/AuthenticatedUserDropdown.jsx
@@ -10,7 +10,7 @@ import { useSelector, useDispatch } from 'react-redux';
import Notifications from '../Notifications';
import { selectShowNotificationTray, selectNotificationStatus } from '../Notifications/data/selectors';
import { fetchAppsNotificationCount } from '../Notifications/data/thunks';
-import { IDLE } from '../Notifications/data/slice';
+import { RequestStatus } from '../Notifications/data/slice';
import messages from './messages';
@@ -20,7 +20,7 @@ const AuthenticatedUserDropdown = ({ intl, username }) => {
const dispatch = useDispatch();
useEffect(() => {
- if (notificationStatus === IDLE) {
+ if (notificationStatus === RequestStatus.IDLE) {
dispatch(fetchAppsNotificationCount());
}
// eslint-disable-next-line react-hooks/exhaustive-deps