Merge pull request #25 from open-craft/felipetrz/bb-4993-add-post-pagination

feat: add pagination for posts [BD-38] [TNL-8810] [BB-4993]
This commit is contained in:
Kshitij Sobti
2021-10-21 12:49:08 +05:30
committed by GitHub
5 changed files with 101 additions and 10 deletions

View File

@@ -0,0 +1,41 @@
import React, {
useEffect,
useRef,
} from 'react';
import PropTypes from 'prop-types';
function ScrollThreshold({ onScroll }) {
const elementRef = useRef(null);
useEffect(() => {
if (!elementRef.current) {
return undefined;
}
// create the observer
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
onScroll();
}
},
);
observer.observe(elementRef.current);
// cleanup callback
return () => {
observer.disconnect();
};
}, [elementRef]);
return (
<div ref={elementRef} />
);
}
ScrollThreshold.propTypes = {
onScroll: PropTypes.func.isRequired,
};
export default ScrollThreshold;

View File

@@ -7,10 +7,12 @@ import { useParams } from 'react-router';
import { AppContext } from '@edx/frontend-platform/react';
import { Spinner } from '@edx/paragon';
import ScrollThreshold from '../../components/ScrollThreshold';
import { RequestStatus } from '../../data/constants';
import {
selectAllThreads,
selectThreadFilters,
selectThreadNextPage,
selectThreadSorting,
selectTopicThreads,
selectUserThreads,
@@ -30,6 +32,7 @@ function PostsView({ showOwnPosts }) {
const { authenticatedUser } = useContext(AppContext);
const orderBy = useSelector(selectThreadSorting());
const filters = useSelector(selectThreadFilters());
const nextPage = useSelector(selectThreadNextPage());
const loadingStatus = useSelector(threadsLoadingStatus());
let posts = [];
@@ -48,19 +51,33 @@ function PostsView({ showOwnPosts }) {
}));
}, [courseId, orderBy, filters]);
const loadMorePosts = async () => {
if (nextPage) {
dispatch(fetchThreads(courseId, {
orderBy,
filters,
page: nextPage,
}));
}
};
return (
<div className="discussion-posts d-flex flex-column">
<PostFilterBar filterSelfPosts={showOwnPosts} />
{posts && posts.length > 0 && (
<div className="list-group list-group-flush">
{posts.map(post => (<PostLink post={post} key={post.id} />))}
</div>
)}
{loadingStatus === RequestStatus.IN_PROGRESS && (
<div className="d-flex justify-content-center p-4">
<Spinner animation="border" variant="primary" size="lg" />
</div>
)}
<div className="list-group list-group-flush">
{posts && posts.map(post => (
<PostLink post={post} key={post.id} />
))}
{loadingStatus === RequestStatus.IN_PROGRESS ? (
<div className="d-flex justify-content-center p-4">
<Spinner animation="border" variant="primary" size="lg" />
</div>
) : (
nextPage && (
<ScrollThreshold onScroll={loadMorePosts} />
)
)}
</div>
</div>
);
}

View File

@@ -58,6 +58,35 @@ describe('Threads/Posts data layer tests', () => {
.toEqual('test-topic');
});
test('successfully processes threads pagination', async () => {
const mockPage = page => axiosMock
.onGet(threadsApiUrl)
.reply(200, Factory.build('threadsResult', null, {
page,
count: 5,
pageSize: 3,
}));
mockPage(1);
await executeThunk(fetchThreads(courseId), store.dispatch, store.getState);
expect(store.getState().threads.pages)
.toEqual([
['thread-1', 'thread-2', 'thread-3'],
]);
expect(store.getState().threads.nextPage)
.toEqual(2);
mockPage(2);
await executeThunk(fetchThreads(courseId, { page: 2 }), store.dispatch, store.getState);
expect(store.getState().threads.pages)
.toEqual([
['thread-1', 'thread-2', 'thread-3'],
['thread-4', 'thread-5'],
]);
expect(store.getState().threads.nextPage)
.toBeNull();
});
test('successfully processes single thread', async () => {
const threadId = 'thread-1';
axiosMock.onGet(`${threadsApiUrl}${threadId}/`)

View File

@@ -47,6 +47,8 @@ export const selectThreadSorting = () => state => state.threads.sortedBy;
export const selectThreadFilters = () => state => state.threads.filters;
export const selectThreadNextPage = () => state => state.threads.nextPage;
export const selectAuthorAvatars = author => state => (
state.threads.avatars?.[author]?.profile.image
);

View File

@@ -24,6 +24,7 @@ const threadsSlice = createSlice({
},
pages: [],
threadDraft: null,
nextPage: null,
totalPages: null,
totalThreads: null,
postStatus: RequestStatus.SUCCESSFUL,
@@ -47,6 +48,7 @@ const threadsSlice = createSlice({
state.threadsById = { ...state.threadsById, ...payload.threadsById };
state.threadsInTopic = { ...state.threadsInTopic, ...payload.threadsInTopic };
state.avatars = { ...state.avatars, ...payload.avatars };
state.nextPage = (payload.page < payload.pagination.numPages) ? payload.page + 1 : null;
state.totalPages = payload.pagination.numPages;
state.totalThreads = payload.pagination.count;
},