Merge pull request #6 from stevedylandev/chore/update-navigation e5cc239b
chore: updated navigation
Steve Simkins · 2025-11-26 16:07 1 file(s) · +70 −2
src/components/dashboard.tsx +70 −2
20 20
import {
21 21
	allFeedsQuery,
22 22
	allPostsQuery,
23 +
	postsByFeedQuery,
23 24
	allReadStatusesQuery,
24 25
	allReadStatusesWithUnreadQuery,
25 26
	useEvolu,
40 41
	const evolu = useEvolu();
41 42
	const allFeeds = useQuery(allFeedsQuery);
42 43
	const allPosts = useQuery(allPostsQuery);
44 +
	const feedPostsQuery = useQuery(postsByFeedQuery(selectedFeedId || ""));
43 45
	const allReadStatuses = useQuery(allReadStatusesQuery);
44 46
	const allReadStatusesWithUnread = useQuery(allReadStatusesWithUnreadQuery);
45 47
	console.log(allPosts);
46 48
49 +
	// Check if a post is read
50 +
	const isPostRead = React.useCallback(
51 +
		(postId: string) => {
52 +
			return allReadStatuses.some((status) => status.postId === postId);
53 +
		},
54 +
		[allReadStatuses],
55 +
	);
56 +
47 57
	// Get the first post (most recent) to use as default
48 58
	const firstPostId = React.useMemo(() => {
49 59
		if (allPosts.length === 0) return null;
77 87
78 88
	// Get sorted posts for navigation
79 89
	const sortedPosts = React.useMemo(() => {
80 -
		return [...allPosts].sort((a, b) => {
90 +
		// Filter posts based on selected feed
91 +
		let postsToSort = allPosts;
92 +
		if (selectedFeedId === "unread") {
93 +
			// Show only unread posts from all feeds
94 +
			postsToSort = allPosts.filter((post) => !isPostRead(post.id));
95 +
		} else if (selectedFeedId) {
96 +
			// Show posts from specific feed
97 +
			postsToSort = feedPostsQuery;
98 +
		}
99 +
		// Sort by published date (most recent first)
100 +
		return [...postsToSort].sort((a, b) => {
81 101
			if (!a.publishedDate) return 1;
82 102
			if (!b.publishedDate) return -1;
83 103
			return b.publishedDate.localeCompare(a.publishedDate);
84 104
		});
85 -
	}, [allPosts]);
105 +
	}, [allPosts, selectedFeedId, feedPostsQuery, isPostRead]);
86 106
87 107
	// Get current post index and navigation info
88 108
	const currentPostIndex = React.useMemo(() => {
146 166
			mainContentRef.current.scrollTo({ top: 0, behavior: "smooth" });
147 167
		}
148 168
	}, [selectedPostId]);
169 +
170 +
	// Mark post as read when selected
171 +
	React.useEffect(() => {
172 +
		if (!selectedPostId || !selectedPost) return;
173 +
174 +
		const existingStatus = allReadStatusesWithUnread.find(
175 +
			(status) => status.postId === selectedPostId,
176 +
		);
177 +
178 +
		if (existingStatus && existingStatus.isRead === 0) {
179 +
			// Update existing status to read
180 +
			evolu.update("readStatus", {
181 +
				id: existingStatus.id as any,
182 +
				isRead: 1,
183 +
			});
184 +
		} else if (!existingStatus && selectedPost.feedId) {
185 +
			// Create new read status
186 +
			evolu.insert("readStatus", {
187 +
				postId: selectedPostId,
188 +
				feedId: selectedPost.feedId,
189 +
				isRead: 1,
190 +
			});
191 +
		}
192 +
	}, [selectedPostId, selectedPost, allReadStatusesWithUnread, evolu]);
193 +
194 +
	// Keyboard navigation for posts
195 +
	React.useEffect(() => {
196 +
		const handleKeyDown = (e: KeyboardEvent) => {
197 +
			// Only handle arrow keys if not typing in an input/textarea
198 +
			if (
199 +
				e.target instanceof HTMLInputElement ||
200 +
				e.target instanceof HTMLTextAreaElement
201 +
			) {
202 +
				return;
203 +
			}
204 +
205 +
			if (e.key === "ArrowUp") {
206 +
				e.preventDefault();
207 +
				goToPreviousPost();
208 +
			} else if (e.key === "ArrowDown") {
209 +
				e.preventDefault();
210 +
				goToNextPost();
211 +
			}
212 +
		};
213 +
214 +
		window.addEventListener("keydown", handleKeyDown);
215 +
		return () => window.removeEventListener("keydown", handleKeyDown);
216 +
	}, [goToPreviousPost, goToNextPost]);
149 217
150 218
	// Get base URL from the post link to fix relative image paths
151 219
	const getBaseUrl = React.useCallback((link: string | null) => {