feat: added post navigation buttons
9a2101c9
1 file(s) · +57 −1
| 15 | 15 | SidebarTrigger, |
|
| 16 | 16 | } from "@/components/ui/sidebar"; |
|
| 17 | 17 | import { Button } from "@/components/ui/button"; |
|
| 18 | - | import { Circle, CircleCheckBig } from "lucide-react"; |
|
| 18 | + | import { Circle, CircleCheckBig, ChevronUp, ChevronDown } from "lucide-react"; |
|
| 19 | 19 | import { useQuery } from "@evolu/react"; |
|
| 20 | 20 | import { |
|
| 21 | 21 | allFeedsQuery, |
|
| 74 | 74 | if (!selectedPost?.feedId) return null; |
|
| 75 | 75 | return allFeeds.find((f) => f.id === selectedPost.feedId); |
|
| 76 | 76 | }, [selectedPost?.feedId, allFeeds]); |
|
| 77 | + | ||
| 78 | + | // Get sorted posts for navigation |
|
| 79 | + | const sortedPosts = React.useMemo(() => { |
|
| 80 | + | return [...allPosts].sort((a, b) => { |
|
| 81 | + | if (!a.publishedDate) return 1; |
|
| 82 | + | if (!b.publishedDate) return -1; |
|
| 83 | + | return b.publishedDate.localeCompare(a.publishedDate); |
|
| 84 | + | }); |
|
| 85 | + | }, [allPosts]); |
|
| 86 | + | ||
| 87 | + | // Get current post index and navigation info |
|
| 88 | + | const currentPostIndex = React.useMemo(() => { |
|
| 89 | + | if (!selectedPostId) return -1; |
|
| 90 | + | return sortedPosts.findIndex((p) => p.id === selectedPostId); |
|
| 91 | + | }, [selectedPostId, sortedPosts]); |
|
| 92 | + | ||
| 93 | + | const hasPreviousPost = currentPostIndex > 0; |
|
| 94 | + | const hasNextPost = |
|
| 95 | + | currentPostIndex >= 0 && currentPostIndex < sortedPosts.length - 1; |
|
| 96 | + | ||
| 97 | + | // Navigation handlers |
|
| 98 | + | const goToPreviousPost = React.useCallback(() => { |
|
| 99 | + | if (hasPreviousPost) { |
|
| 100 | + | setSelectedPostId(sortedPosts[currentPostIndex - 1].id); |
|
| 101 | + | } |
|
| 102 | + | }, [hasPreviousPost, sortedPosts, currentPostIndex]); |
|
| 103 | + | ||
| 104 | + | const goToNextPost = React.useCallback(() => { |
|
| 105 | + | if (hasNextPost) { |
|
| 106 | + | setSelectedPostId(sortedPosts[currentPostIndex + 1].id); |
|
| 107 | + | } |
|
| 108 | + | }, [hasNextPost, sortedPosts, currentPostIndex]); |
|
| 77 | 109 | ||
| 78 | 110 | // Check if current post is read |
|
| 79 | 111 | const isCurrentPostRead = React.useMemo(() => { |
|
| 192 | 224 | )} |
|
| 193 | 225 | </BreadcrumbList> |
|
| 194 | 226 | </Breadcrumb> |
|
| 227 | + | {selectedPost && ( |
|
| 228 | + | <div className="ml-auto flex items-center gap-1"> |
|
| 229 | + | <Button |
|
| 230 | + | variant="ghost" |
|
| 231 | + | size="icon" |
|
| 232 | + | onClick={goToPreviousPost} |
|
| 233 | + | disabled={!hasPreviousPost} |
|
| 234 | + | className="h-8 w-8" |
|
| 235 | + | title="Previous post" |
|
| 236 | + | > |
|
| 237 | + | <ChevronUp className="h-4 w-4" /> |
|
| 238 | + | </Button> |
|
| 239 | + | <Button |
|
| 240 | + | variant="ghost" |
|
| 241 | + | size="icon" |
|
| 242 | + | onClick={goToNextPost} |
|
| 243 | + | disabled={!hasNextPost} |
|
| 244 | + | className="h-8 w-8" |
|
| 245 | + | title="Next post" |
|
| 246 | + | > |
|
| 247 | + | <ChevronDown className="h-4 w-4" /> |
|
| 248 | + | </Button> |
|
| 249 | + | </div> |
|
| 250 | + | )} |
|
| 195 | 251 | </header> |
|
| 196 | 252 | <div |
|
| 197 | 253 | ref={mainContentRef} |
|