aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/src/components/ArticleItem.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/ArticleItem.tsx')
-rw-r--r--frontend/src/components/ArticleItem.tsx38
1 files changed, 13 insertions, 25 deletions
diff --git a/frontend/src/components/ArticleItem.tsx b/frontend/src/components/ArticleItem.tsx
index faa86fe..4942518 100644
--- a/frontend/src/components/ArticleItem.tsx
+++ b/frontend/src/components/ArticleItem.tsx
@@ -1,6 +1,5 @@
import { faCheck, faCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useOptimistic } from "react";
import { useMutation } from "urql";
import type {
GetReadArticlesQuery,
@@ -18,27 +17,19 @@ type Article = NonNullable<
interface Props {
article: Article;
+ onReadChange?: (articleId: string, isRead: boolean) => void;
}
-export function ArticleItem({ article }: Props) {
+export function ArticleItem({ article, onReadChange }: Props) {
const [, markArticleRead] = useMutation(MarkArticleReadDocument);
const [, markArticleUnread] = useMutation(MarkArticleUnreadDocument);
- const [optimisticArticle, setOptimisticArticle] = useOptimistic(
- article,
- (currentArticle, newReadState: boolean) => ({
- ...currentArticle,
- isRead: newReadState,
- }),
- );
-
const handleToggleRead = async (
articleId: string,
isCurrentlyRead: boolean,
) => {
const newReadState = !isCurrentlyRead;
-
- setOptimisticArticle(newReadState);
+ onReadChange?.(articleId, newReadState);
if (isCurrentlyRead) {
await markArticleUnread({ id: articleId });
@@ -50,9 +41,8 @@ export function ArticleItem({ article }: Props) {
const handleArticleClick = async (article: Article) => {
// Open article in new tab and mark as read if it's unread
window.open(article.url, "_blank", "noreferrer");
- if (!optimisticArticle.isRead) {
- setOptimisticArticle(true);
-
+ if (!article.isRead) {
+ onReadChange?.(article.id, true);
await markArticleRead({ id: article.id });
}
};
@@ -60,38 +50,36 @@ export function ArticleItem({ article }: Props) {
return (
<div
className={`group flex items-center gap-3 rounded-lg border p-3 hover:bg-gray-50 ${
- optimisticArticle.isRead
+ article.isRead
? "border-gray-200 bg-white"
: "border-blue-200 bg-blue-50"
}`}
>
<button
type="button"
- onClick={() => handleToggleRead(article.id, optimisticArticle.isRead)}
+ onClick={() => handleToggleRead(article.id, article.isRead)}
className={`flex-shrink-0 rounded p-1 transition-colors ${
- optimisticArticle.isRead
+ article.isRead
? "text-gray-400 hover:text-gray-600"
: "text-blue-600 hover:text-blue-700"
}`}
- title={optimisticArticle.isRead ? "Mark as unread" : "Mark as read"}
+ title={article.isRead ? "Mark as unread" : "Mark as read"}
>
<FontAwesomeIcon
- icon={optimisticArticle.isRead ? faCheck : faCircle}
+ icon={article.isRead ? faCheck : faCircle}
className="w-4 h-4"
/>
</button>
<div className="flex-1 min-w-0">
<button
type="button"
- onClick={() => handleArticleClick(optimisticArticle)}
+ onClick={() => handleArticleClick(article)}
className={`text-left w-full group-hover:text-blue-600 transition-colors ${
- optimisticArticle.isRead
- ? "text-gray-700"
- : "text-gray-900 font-medium"
+ article.isRead ? "text-gray-700" : "text-gray-900 font-medium"
}`}
>
<div className="flex items-center gap-2 break-words">
- {optimisticArticle.title}
+ {article.title}
</div>
</button>
</div>