@@ -8,9 +8,16 @@ import Post from "./PostCard";
88interface FeedProp {
99 isAdmin : boolean ;
1010 searchQuery ?: string ;
11+ sortField : "title" | "date" | "upvotes" ;
12+ sortOrder : "asc" | "desc" ;
1113}
1214
13- export default function Feed ( { isAdmin, searchQuery } : FeedProp ) {
15+ export default function Feed ( {
16+ isAdmin,
17+ searchQuery,
18+ sortField,
19+ sortOrder,
20+ } : FeedProp ) {
1421 const posts = useFeedsStore ( ( state ) => state . posts ) ;
1522 const fetchPosts = useFeedsStore ( ( state ) => state . fetchPosts ) ;
1623
@@ -21,12 +28,10 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
2128 fetchPosts ( ) ;
2229 } , [ fetchPosts ] ) ;
2330
24- // Filter posts by title or author's first/last name (case-insensitive)
31+ // Filter posts
2532 const filteredPosts = useMemo ( ( ) => {
2633 if ( ! searchQuery || searchQuery . trim ( ) === "" ) return posts ;
27-
2834 const query = searchQuery . toLowerCase ( ) ;
29-
3035 return posts . filter ( ( post ) => {
3136 const titleMatch = post . title ?. toLowerCase ( ) . includes ( query ) ;
3237 const firstNameMatch = post . author_id ?. first_name
@@ -35,43 +40,63 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
3540 const lastNameMatch = post . author_id ?. last_name
3641 ?. toLowerCase ( )
3742 . includes ( query ) ;
38-
3943 return titleMatch || firstNameMatch || lastNameMatch ;
4044 } ) ;
4145 } , [ posts , searchQuery ] ) ;
4246
43- const visiblePosts = filteredPosts . slice ( 0 , visibleCount ) ;
47+ // Sort posts
48+ const sortedPosts = useMemo ( ( ) => {
49+ const sorted = [ ...filteredPosts ] ;
50+ sorted . sort ( ( a , b ) => {
51+ let aVal : string | number = "" ;
52+ let bVal : string | number = "" ;
53+
54+ switch ( sortField ) {
55+ case "title" :
56+ aVal = a . title ?. toLowerCase ( ) || "" ;
57+ bVal = b . title ?. toLowerCase ( ) || "" ;
58+ break ;
59+ case "date" :
60+ aVal = new Date ( a . created_at ) . getTime ( ) ;
61+ bVal = new Date ( b . created_at ) . getTime ( ) ;
62+ break ;
63+ case "upvotes" :
64+ aVal = a . upvote_count ?? 0 ;
65+ bVal = b . upvote_count ?? 0 ;
66+ break ;
67+ }
68+
69+ if ( aVal < bVal ) return sortOrder === "asc" ? - 1 : 1 ;
70+ if ( aVal > bVal ) return sortOrder === "asc" ? 1 : - 1 ;
71+ return 0 ;
72+ } ) ;
73+ return sorted ;
74+ } , [ filteredPosts , sortField , sortOrder ] ) ;
75+
76+ const visiblePosts = sortedPosts . slice ( 0 , visibleCount ) ;
4477
45- // Reset visible count when search changes
4678 useEffect ( ( ) => {
4779 setVisibleCount ( 6 ) ;
4880 } , [ searchQuery ] ) ;
4981
50- // Infinite scroll logic
82+ // Infinite scroll
5183 useEffect ( ( ) => {
5284 const observer = new IntersectionObserver (
5385 ( entries ) => {
5486 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- } ) ;
87+ if ( entry ?. isIntersecting ) {
88+ setVisibleCount ( ( prev ) => Math . min ( prev + 6 , sortedPosts . length ) ) ;
6289 }
6390 } ,
6491 { threshold : 1 } ,
6592 ) ;
6693
6794 const currentLoader = loaderRef . current ;
6895 if ( currentLoader ) observer . observe ( currentLoader ) ;
69-
7096 return ( ) => {
7197 if ( currentLoader ) observer . unobserve ( currentLoader ) ;
7298 } ;
73- } , [ filteredPosts . length ] ) ;
74-
99+ } , [ sortedPosts . length ] ) ;
75100 const handleDeletePost = ( deletedPostId : string | number ) => {
76101 useFeedsStore . setState ( ( state ) => ( {
77102 posts : state . posts . filter ( ( post ) => post . id !== deletedPostId ) ,
@@ -80,6 +105,7 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
80105
81106 return (
82107 < >
108+ { /* Posts grid */ }
83109 < div className = "grid grid-cols-1 gap-4 md:grid-cols-3" >
84110 { visiblePosts . map ( ( post ) => (
85111 < Post
@@ -91,8 +117,8 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
91117 ) ) }
92118 </ div >
93119
94- { /* Loader / sentinel div for infinite scroll */ }
95- { visibleCount < filteredPosts . length && (
120+ { /* Loader */ }
121+ { visibleCount < sortedPosts . length && (
96122 < div
97123 ref = { loaderRef }
98124 className = "mt-6 flex justify-center text-sm text-gray-500"
@@ -105,7 +131,7 @@ export default function Feed({ isAdmin, searchQuery }: FeedProp) {
105131 ) }
106132
107133 { /* Empty state */ }
108- { filteredPosts . length === 0 && (
134+ { sortedPosts . length === 0 && (
109135 < div className = "mt-8 text-center text-gray-500" > No posts found.</ div >
110136 ) }
111137 </ >
0 commit comments