diff --git a/src/index.jsx b/src/index.jsx index 3874c72..e67d731 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -35,12 +35,8 @@ subscribe(APP_READY, () => {
- {allowNotificationRoutes && ( - <> - - - - )} + {allowNotificationRoutes && ()} + {allowNotificationRoutes && ()} diff --git a/src/notification-preferences/NotificationCourses.jsx b/src/notification-preferences/NotificationCourses.jsx index ef22cc2..6ab3018 100644 --- a/src/notification-preferences/NotificationCourses.jsx +++ b/src/notification-preferences/NotificationCourses.jsx @@ -1,11 +1,13 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Container, Icon, Spinner } from '@edx/paragon'; +import { + Container, Icon, Spinner, Button, +} from '@edx/paragon'; import { ArrowForwardIos } from '@edx/paragon/icons'; import { fetchCourseList } from './data/thunks'; -import { selectCourseListStatus, selectCourseList } from './data/selectors'; +import { selectCourseListStatus, selectCourseList, selectPagination } from './data/selectors'; import { IDLE_STATUS, LOADING_STATUS, @@ -18,11 +20,14 @@ const NotificationCourses = ({ intl }) => { const dispatch = useDispatch(); const coursesList = useSelector(selectCourseList()); const courseListStatus = useSelector(selectCourseListStatus()); + const { hasMore, currentPage } = useSelector(selectPagination()); + + const loadMore = useCallback((page = 1, pageSize = 10) => { + dispatch(fetchCourseList(page, pageSize)); + }, [dispatch]); useEffect(() => { - if (courseListStatus === IDLE_STATUS) { - dispatch(fetchCourseList()); - } + if (courseListStatus === IDLE_STATUS) { loadMore(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -30,43 +35,43 @@ const NotificationCourses = ({ intl }) => { return ; } - if (courseListStatus === LOADING_STATUS) { - return ( -
- -
- ); - } return (

{intl.formatMessage(messages.notificationHeading)}

- { - coursesList.map(course => ( - -
- - {course.name} - - - - -
- - )) - } + {coursesList.map(course => ( + +
+ + {course.name} + + + + +
+ + ))}
+ {courseListStatus === LOADING_STATUS ? ( +
+ +
+ ) : hasMore && ( + + )}
); }; diff --git a/src/notification-preferences/NotificationPreferences.jsx b/src/notification-preferences/NotificationPreferences.jsx index bb8e514..b388de8 100644 --- a/src/notification-preferences/NotificationPreferences.jsx +++ b/src/notification-preferences/NotificationPreferences.jsx @@ -32,6 +32,7 @@ const NotificationPreferences = () => { const course = useSelector(selectCourse(courseId)); const notificationStatus = useSelector(selectNotificationPreferencesStatus()); const preferenceAppsIds = useSelector(selectPreferenceAppsId()); + const isLoading = notificationStatus === LOADING_STATUS || courseStatus === LOADING_STATUS; const preferencesList = useMemo(() => ( preferenceAppsIds.map(appId => ( @@ -47,20 +48,6 @@ const NotificationPreferences = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [courseId]); - if (notificationStatus === LOADING_STATUS) { - return ( -
- -
- ); - } - if ( (courseStatus === SUCCESS_STATUS && coursesList.length === 0) || (notificationStatus === FAILURE_STATUS && coursesList.length !== 0) @@ -73,7 +60,7 @@ const NotificationPreferences = () => {

{intl.formatMessage(messages.notificationHeading)}

-
+
@@ -82,7 +69,18 @@ const NotificationPreferences = () => { {course?.name}
- { preferencesList } + {preferencesList} + {isLoading && ( +
+ +
+ )}
); diff --git a/src/notification-preferences/data/reducers.js b/src/notification-preferences/data/reducers.js index 189b832..4a1ee11 100644 --- a/src/notification-preferences/data/reducers.js +++ b/src/notification-preferences/data/reducers.js @@ -10,6 +10,7 @@ export const defaultState = { courses: { status: IDLE_STATUS, courses: [], + pagination: {}, }, preferences: { status: IDLE_STATUS, @@ -29,8 +30,8 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { return { ...state, courses: { + ...state.courses, status: LOADING_STATUS, - courses: [], }, }; case Actions.FETCHED_COURSE_LIST: @@ -38,15 +39,16 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => { ...state, courses: { status: SUCCESS_STATUS, - courses: action.payload, + courses: [...state.courses.courses, ...action.payload.courseList], + pagination: action.payload.pagination, }, }; case Actions.FAILED_COURSE_LIST: return { ...state, courses: { + ...state.courses, status: FAILURE_STATUS, - courses: [], }, }; case Actions.FETCHING_PREFERENCES: diff --git a/src/notification-preferences/data/selectors.js b/src/notification-preferences/data/selectors.js index 728f1aa..ee41410 100644 --- a/src/notification-preferences/data/selectors.js +++ b/src/notification-preferences/data/selectors.js @@ -53,3 +53,7 @@ export const selectPreferenceNonEditableChannels = (appId, name) => state => ( export const selectSelectedCourseId = () => state => ( state.notificationPreferences.preferences.selectedCourse ); + +export const selectPagination = () => state => ( + state.notificationPreferences.courses.pagination +); diff --git a/src/notification-preferences/data/service.js b/src/notification-preferences/data/service.js index 4b6e939..c3deb56 100644 --- a/src/notification-preferences/data/service.js +++ b/src/notification-preferences/data/service.js @@ -8,9 +8,10 @@ export const getCourseNotificationPreferences = async (courseId) => { return data; }; -export const getCourseList = async () => { +export const getCourseList = async (page, pageSize) => { + const params = snakeCaseObject({ page, pageSize }); const url = `${getConfig().LMS_BASE_URL}/api/notifications/enrollments/`; - const { data } = await getAuthenticatedHttpClient().get(url); + const { data } = await getAuthenticatedHttpClient().get(url, { params }); return data; }; diff --git a/src/notification-preferences/data/thunks.js b/src/notification-preferences/data/thunks.js index e980283..b3b6306 100644 --- a/src/notification-preferences/data/thunks.js +++ b/src/notification-preferences/data/thunks.js @@ -17,12 +17,24 @@ import { patchPreferenceToggle, } from './service'; -const normalizeCourses = (responseData) => ( - responseData.map((enrollment) => ({ +const normalizeCourses = (responseData) => { + const courseList = responseData.results?.map((enrollment) => ({ id: enrollment.course.id, name: enrollment.course.displayName, - })) -); + })) || []; + + const pagination = { + count: responseData.count, + currentPage: responseData.currentPage, + hasMore: Boolean(responseData.next), + totalPages: responseData.numPages, + }; + + return { + courseList, + pagination, + }; +}; const normalizePreferences = (responseData) => { const preferences = responseData.notificationPreferenceConfig; @@ -60,11 +72,11 @@ const normalizePreferences = (responseData) => { return normalizedPreferences; }; -export const fetchCourseList = () => ( +export const fetchCourseList = (page, pageSize) => ( async (dispatch) => { try { dispatch(fetchCourseListFetching()); - const data = await getCourseList(); + const data = await getCourseList(page, pageSize); const normalizedData = normalizeCourses(camelCaseObject(data)); dispatch(fetchCourseListSuccess(normalizedData)); } catch (errors) { diff --git a/src/notification-preferences/messages.js b/src/notification-preferences/messages.js index fdd8346..b47fd1e 100644 --- a/src/notification-preferences/messages.js +++ b/src/notification-preferences/messages.js @@ -53,4 +53,9 @@ export const messages = defineMessages({ defaultMessage: 'Push', description: 'Display text for push', }, + loadMoreCourses: { + id: 'notification.preference.load.more.courses', + defaultMessage: 'Load more courses', + description: 'Load more button to load more courses', + }, });