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" ;
54import { useFeedsStore } from "@/store/feeds-store" ;
65
76import 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