| 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) => { |