refactor: fixed code refactor and added new slices and selector

This commit is contained in:
SundasNoreen
2023-06-05 19:55:46 +05:00
parent 78a40d47c1
commit cabf4e3f27
9 changed files with 108 additions and 77 deletions

View File

@@ -1,14 +1,14 @@
import React, { useCallback } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Icon } from '@edx/paragon';
import * as timeago from 'timeago.js';
import PropTypes from 'prop-types';
import { useDispatch } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import { Icon } from '@edx/paragon';
import { Link } from 'react-router-dom';
import * as timeago from 'timeago.js';
import { getIconByType } from './utils';
import { markNotificationsAsRead } from './data/thunks';
import { messages } from './messages';
import timeLocale from '../common/time-locale';
import { markNotificationsAsRead } from './data/thunks';
import { getIconByType } from './utils';
const NotificationRowItem = ({
id, type, contentUrl, content, courseName, createdAt, lastRead,
@@ -38,7 +38,7 @@ const NotificationRowItem = ({
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: content }}
/>
<div className="py-0 d-flex flex-row align-items-center">
<div className="py-0 d-flex align-items-center">
<span className="font-size-12 text-gray-500 line-height-20">
<span>{courseName}</span>
<span className="text-light-700 px-1.5">{intl.formatMessage(messages.fullStop)}</span>

View File

@@ -1,26 +1,21 @@
import React, { useCallback, useMemo } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector, useDispatch } from 'react-redux';
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 NotificationRowItem from './NotificationRowItem';
import {
getSelectedAppNotificationIds,
getSelectedAppName,
getNotificationsByIds,
getPaginationData,
} from './data/selectors';
import { splitNotificationsByTime } from './utils';
import { markAllNotificationsAsRead } from './data/thunks';
import { selectNotificationsByIds, selectPaginationData, selectSelectedAppName } from './data/selectors';
import { splitNotificationsByTime } from './utils';
import { updatePaginationRequest } from './data/slice';
const NotificationSections = () => {
const intl = useIntl();
const dispatch = useDispatch();
const selectedAppName = useSelector(getSelectedAppName());
const notificationIds = useSelector(getSelectedAppNotificationIds(selectedAppName));
const notifications = useSelector(getNotificationsByIds(notificationIds));
const paginationData = useSelector(getPaginationData());
const selectedAppName = useSelector(selectSelectedAppName());
const notifications = useSelector(selectNotificationsByIds);
const paginationData = useSelector(selectPaginationData());
const { today = [], earlier = [] } = useMemo(
() => splitNotificationsByTime(notifications),
[notifications],
@@ -30,20 +25,28 @@ const NotificationSections = () => {
dispatch(markAllNotificationsAsRead(selectedAppName));
}, [dispatch, selectedAppName]);
const updatePagination = useCallback(() => {
dispatch(updatePaginationRequest());
}, [dispatch]);
const renderNotificationSection = (section, items) => {
if (isEmpty(items)) { return null; }
return (
<div className="pb-2">
<div className="d-flex flex-row justify-content-between align-items-center">
<div className="d-flex justify-content-between align-items-center">
<span className="text-gray-500">
{section === 'today' && intl.formatMessage(messages.notificationTodayHeading)}
{section === 'earlier' && intl.formatMessage(messages.notificationEarlierHeading)}
</span>
{notifications?.length > 0 && (section === 'earlier' ? today.length === 0 : true) && (
<span className="text-info-500 line-height-24 cursor-pointer" onClick={handleMarkAllAsRead}>
<Button
variant="link"
className="text-info-500 line-height-24 font-size-14 text-decoration-none"
onClick={handleMarkAllAsRead}
>
{intl.formatMessage(messages.notificationMarkAsRead)}
</span>
</Button>
)}
</div>
{items.map((notification) => (
@@ -67,7 +70,7 @@ const NotificationSections = () => {
{renderNotificationSection('today', today)}
{renderNotificationSection('earlier', earlier)}
{paginationData.currentPage < paginationData.numPages && (
<Button variant="primary" className="w-100 bg-primary-500" onClick={() => {}}>
<Button variant="primary" className="w-100 bg-primary-500" onClick={updatePagination}>
{intl.formatMessage(messages.loadMoreNotifications)}
</Button>
)}

View File

@@ -1,27 +1,28 @@
import React, {
useState, useCallback, useMemo, useEffect,
} from 'react';
import { Tabs, Tab } from '@edx/paragon';
import { useSelector, useDispatch } from 'react-redux';
import React, { useCallback, useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Tab, Tabs } from '@edx/paragon';
import NotificationSections from './NotificationSections';
import { getNotificationTabsCount, getSelectedAppName, getNotificationTabs } from './data/selectors';
import { fetchNotificationList, markNotificationsAsSeen } from './data/thunks';
import {
selectNotificationTabs, selectNotificationTabsCount, selectPaginationData, selectSelectedAppName,
} from './data/selectors';
import { updateAppNameRequest } from './data/slice';
const NotificationTabs = () => {
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const selectedAppName = useSelector(getSelectedAppName());
const notificationUnseenCounts = useSelector(getNotificationTabsCount());
const notificationTabs = useSelector(getNotificationTabs());
const selectedAppName = useSelector(selectSelectedAppName());
const notificationUnseenCounts = useSelector(selectNotificationTabsCount());
const notificationTabs = useSelector(selectNotificationTabs());
const paginationData = useSelector(selectPaginationData());
useEffect(() => {
dispatch(fetchNotificationList({ appName: selectedAppName, page, pageSize: 10 }));
dispatch(fetchNotificationList({ appName: selectedAppName, page: paginationData.currentPage, pageSize: 10 }));
if (selectedAppName) { dispatch(markNotificationsAsSeen(selectedAppName)); }
}, [dispatch, page, selectedAppName]);
}, [dispatch, paginationData.currentPage, selectedAppName]);
const handleActiveTab = useCallback((appName) => {
// dispatch(setSelectedAppName(appName));
}, []);
dispatch(updateAppNameRequest({ appName }));
}, [dispatch]);
const tabArray = useMemo(() => notificationTabs?.map((appName) => (
<Tab
@@ -47,4 +48,4 @@ const NotificationTabs = () => {
);
};
export default NotificationTabs;
export default React.memo(NotificationTabs);

View File

@@ -1,9 +1,23 @@
export const getNotificationStatus = () => state => state.notifications.notificationStatus;
export const getNotificationTabsCount = () => state => state.notifications.tabsCount;
export const getNotificationTabs = () => state => state.notifications.appsId;
export const getSelectedAppNotificationIds = (appName) => state => state.notifications.apps[appName] ?? [];
export const getNotificationTrayStatus = () => state => state.notifications.showNotificationTray;
export const getNotificationsByIds = (notificationIds) => state => Object.entries(state.notifications.notifications)
.filter(([key]) => notificationIds.includes(key)).map(([, value]) => value);
export const getSelectedAppName = () => state => state.notifications.appName;
export const getPaginationData = () => state => state.notifications.pagination;
import { createSelector } from '@reduxjs/toolkit';
export const selectNotificationStatus = () => state => state.notifications.notificationStatus;
export const selectNotificationTabsCount = () => state => state.notifications.tabsCount;
export const selectNotificationTabs = () => state => state.notifications.appsId;
export const selectSelectedAppNotificationIds = (appName) => state => state.notifications.apps[appName] ?? [];
export const selectShowNotificationTray = () => state => state.notifications.showNotificationTray;
export const selectNotifications = () => state => state.notifications.notification;
export const selectNotificationsByIds = createSelector(
state => state.notifications.notifications,
state => state.notifications.apps[state.notifications.appName] || [],
(notifications, notificationIds) => notificationIds.map(notificationId => notifications[notificationId]),
);
export const selectSelectedAppName = () => state => state.notifications.appName;
export const selectPaginationData = () => state => state.notifications.pagination;

View File

@@ -19,6 +19,7 @@ const initialState = {
count: 10,
numPages: 1,
currentPage: 1,
nextPage: null,
},
};
const slice = createSlice({
@@ -115,6 +116,15 @@ const slice = createSlice({
state.notificationStatus = FAILED;
},
resetNotificationStateRequest: () => initialState,
updateAppNameRequest: (state, { payload }) => {
state.appName = payload.appName;
state.pagination.currentPage = 1;
},
updatePaginationRequest: (state) => {
state.pagination.currentPage += 1;
},
},
});
@@ -140,6 +150,8 @@ export const {
markNotificationsAsReadSuccess,
markNotificationsAsReadFailure,
resetNotificationStateRequest,
updateAppNameRequest,
updatePaginationRequest,
} = slice.actions;
export const notificationsReducer = slice.reducer;

View File

@@ -1,19 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, {
useState, useCallback, useEffect, useRef,
useCallback, useEffect, useRef, useState,
} from 'react';
import { NotificationsNone, Settings } from '@edx/paragon/icons';
import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import classNames from 'classnames';
import {
Badge, Icon, IconButton, OverlayTrigger, Popover,
} from '@edx/paragon';
import { useSelector, useDispatch } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import classNames from 'classnames';
import NotificationTabs from './NotificationTabs';
import { getNotificationTabsCount } from './data/selectors';
import { NotificationsNone, Settings } from '@edx/paragon/icons';
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 { useIsOnMediumScreen, useIsOnLargeScreen } from './data/hook';
const Notifications = () => {
const intl = useIntl();
@@ -21,7 +21,7 @@ const Notifications = () => {
const popoverRef = useRef(null);
const buttonRef = useRef(null);
const [showNotificationTray, setShowNotificationTray] = useState(false);
const notificationCounts = useSelector(getNotificationTabsCount());
const notificationCounts = useSelector(selectNotificationTabsCount());
const isOnMediumScreen = useIsOnMediumScreen();
const isOnLargeScreen = useIsOnLargeScreen();
@@ -55,7 +55,7 @@ const Notifications = () => {
<Popover
id="notificationTray"
data-testid="notificationTray"
className={classNames('notification-tray-container overflow-auto rounded-0 border-0', {
className={classNames('overflow-auto rounded-0 border-0', {
'w-100': !isOnMediumScreen && !isOnLargeScreen,
'medium-screen': isOnMediumScreen,
'large-screen': isOnLargeScreen,
@@ -88,7 +88,7 @@ const Notifications = () => {
<Badge
pill
variant="danger"
className="font-weight-normal px-1 font-size-9 notification-badge"
className="font-weight-normal px-1 notification-badge"
>
{notificationCounts.count}
</Badge>

View File

@@ -17,11 +17,13 @@ export const splitNotificationsByTime = (notificationList) => {
splittedData = notificationList.reduce(
(result, notification) => {
const objectTime = new Date(notification.createdAt).getTime();
if (objectTime >= twentyFourHoursAgo && objectTime <= currentTime) {
result.today.push(notification);
} else {
result.earlier.push(notification);
if (notification) {
const objectTime = new Date(notification.createdAt).getTime();
if (objectTime >= twentyFourHoursAgo && objectTime <= currentTime) {
result.today.push(notification);
} else {
result.earlier.push(notification);
}
}
return result;
},

View File

@@ -130,8 +130,8 @@ $white: #fff;
font-size: 12px;
}
.font-size-9{
font-size: 9px;
.font-size-14{
font-size: 14px;
}
.line-height-24{
@@ -161,15 +161,14 @@ $white: #fff;
margin-top: 18px;
margin-left: -21px;
border: 2px solid #FFFFFF;
font-size: 9px !important;
}
.notification-tray-container {
.popover{
max-height: calc(100% - 68px);
min-height: 1220px;
.popover {
}
filter: none;
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15), 0px 2px 8px rgba(0, 0, 0, 0.15);
&.medium-screen {
min-width: 24.313rem;

View File

@@ -3,24 +3,24 @@ import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Dropdown } from '@edx/paragon';
import { useSelector, useDispatch } from 'react-redux';
import Notifications from '../Notifications/Notifications';
import { getNotificationTrayStatus, getNotificationStatus } from '../Notifications/data/selectors';
import Notifications from '../Notifications';
import { selectShowNotificationTray, selectNotificationStatus } from '../Notifications/data/selectors';
import { fetchAppsNotificationCount } from '../Notifications/data/thunks';
import { IDLE } from '../Notifications/data/slice';
import messages from './messages';
const AuthenticatedUserDropdown = ({ intl, username }) => {
const showNotificationTray = useSelector(getNotificationTrayStatus());
const notificationStatus = useSelector(getNotificationStatus());
const showNotificationTray = useSelector(selectShowNotificationTray());
const notificationStatus = useSelector(selectNotificationStatus());
const dispatch = useDispatch();
useEffect(() => {
if (notificationStatus === 'idle') {
if (notificationStatus === IDLE) {
dispatch(fetchAppsNotificationCount());
}
// eslint-disable-next-line react-hooks/exhaustive-deps