Skip to content

Commit 0e0cb68

Browse files
committed
change paganation to infinite scroll
1 parent 4b20978 commit 0e0cb68

1 file changed

Lines changed: 48 additions & 29 deletions

File tree

  • apps/codebility/app/home/feeds/_components

‎apps/codebility/app/home/feeds/_components/Feed.tsx‎

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use client";
22

3-
import { useEffect, useMemo, useState } from "react";
4-
import DefaultPagination from "@/components/ui/pagination";
3+
import { useEffect, useMemo, useRef, useState } from "react";
54
import { useFeedsStore } from "@/store/feeds-store";
65

76
import Post from "./PostCard";
@@ -15,7 +14,8 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
1514
const posts = useFeedsStore((state) => state.posts);
1615
const fetchPosts = useFeedsStore((state) => state.fetchPosts);
1716

18-
const [currentPage, setCurrentPage] = useState(1);
17+
const [visibleCount, setVisibleCount] = useState(6);
18+
const loaderRef = useRef<HTMLDivElement | null>(null);
1919

2020
useEffect(() => {
2121
fetchPosts();
@@ -40,37 +40,48 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
4040
});
4141
}, [posts, searchQuery]);
4242

43-
const postsPerPage = 6;
44-
const totalPages = Math.ceil(filteredPosts.length / postsPerPage);
45-
const startIndex = (currentPage - 1) * postsPerPage;
46-
const currentPosts = filteredPosts.slice(
47-
startIndex,
48-
startIndex + postsPerPage,
49-
);
43+
const visiblePosts = filteredPosts.slice(0, visibleCount);
5044

51-
// Reset to first page when search changes
45+
// Reset visible count when search changes
5246
useEffect(() => {
53-
setCurrentPage(1);
47+
setVisibleCount(6);
5448
}, [searchQuery]);
5549

50+
// Infinite scroll logic
51+
useEffect(() => {
52+
const observer = new IntersectionObserver(
53+
(entries) => {
54+
const entry = entries[0];
55+
if (entry && entry.isIntersecting) {
56+
setVisibleCount((prev) => {
57+
if (prev < filteredPosts.length) {
58+
return Math.min(prev + 6, filteredPosts.length);
59+
}
60+
return prev;
61+
});
62+
}
63+
},
64+
{ threshold: 1 },
65+
);
66+
67+
const currentLoader = loaderRef.current;
68+
if (currentLoader) observer.observe(currentLoader);
69+
70+
return () => {
71+
if (currentLoader) observer.unobserve(currentLoader);
72+
};
73+
}, [filteredPosts.length]);
74+
5675
const handleDeletePost = (deletedPostId: string | number) => {
5776
useFeedsStore.setState((state) => ({
5877
posts: state.posts.filter((post) => post.id !== deletedPostId),
5978
}));
6079
};
6180

62-
const handleNextPage = () => {
63-
if (currentPage < totalPages) setCurrentPage((p) => p + 1);
64-
};
65-
66-
const handlePreviousPage = () => {
67-
if (currentPage > 1) setCurrentPage((p) => p - 1);
68-
};
69-
7081
return (
7182
<>
7283
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
73-
{currentPosts.map((post) => (
84+
{visiblePosts.map((post) => (
7485
<Post
7586
key={post.id}
7687
post={post}
@@ -80,14 +91,22 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
8091
))}
8192
</div>
8293

83-
{filteredPosts.length > 0 && (
84-
<DefaultPagination
85-
totalPages={totalPages}
86-
currentPage={currentPage}
87-
handleNextPage={handleNextPage}
88-
handlePreviousPage={handlePreviousPage}
89-
setCurrentPage={setCurrentPage}
90-
/>
94+
{/* Loader / sentinel div for infinite scroll */}
95+
{visibleCount < filteredPosts.length && (
96+
<div
97+
ref={loaderRef}
98+
className="mt-6 flex justify-center text-sm text-gray-500"
99+
>
100+
<div className="flex items-center gap-2">
101+
<div className="h-4 w-4 animate-spin rounded-full border-2 border-gray-400 border-t-transparent" />
102+
Loading more posts...
103+
</div>
104+
</div>
105+
)}
106+
107+
{/* Empty state */}
108+
{filteredPosts.length === 0 && (
109+
<div className="mt-8 text-center text-gray-500">No posts found.</div>
91110
)}
92111
</>
93112
);

0 commit comments

Comments
 (0)