From 43e7807034db67652bada00eee17a425d332b3f3 Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Sun, 13 Jul 2025 15:58:47 +0900
Subject: refactor(frontend): extract ArticleItem from ArticleList

---
 frontend/src/components/ArticleList.tsx | 82 ++-------------------------------
 1 file changed, 5 insertions(+), 77 deletions(-)

(limited to 'frontend/src/components/ArticleList.tsx')

diff --git a/frontend/src/components/ArticleList.tsx b/frontend/src/components/ArticleList.tsx
index ee7b187..5d508c5 100644
--- a/frontend/src/components/ArticleList.tsx
+++ b/frontend/src/components/ArticleList.tsx
@@ -1,18 +1,8 @@
-import {
-	faCheck,
-	faCircle,
-	faExternalLinkAlt,
-} from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useMutation } from "urql";
 import type {
 	GetReadArticlesQuery,
 	GetUnreadArticlesQuery,
 } from "../graphql/generated/graphql";
-import {
-	MarkArticleReadDocument,
-	MarkArticleUnreadDocument,
-} from "../graphql/generated/graphql";
+import { ArticleItem } from "./ArticleItem";
 
 interface Props {
 	articles: NonNullable<
@@ -23,28 +13,6 @@ interface Props {
 }
 
 export function ArticleList({ articles, showReadStatus = true }: Props) {
-	const [, markArticleRead] = useMutation(MarkArticleReadDocument);
-	const [, markArticleUnread] = useMutation(MarkArticleUnreadDocument);
-
-	const handleToggleRead = async (
-		articleId: string,
-		isCurrentlyRead: boolean,
-	) => {
-		if (isCurrentlyRead) {
-			await markArticleUnread({ id: articleId });
-		} else {
-			await markArticleRead({ id: articleId });
-		}
-	};
-
-	const handleArticleClick = async (article: (typeof articles)[0]) => {
-		// Open article in new tab and mark as read if it's unread
-		window.open(article.url, "_blank");
-		if (!article.isRead) {
-			await markArticleRead({ id: article.id });
-		}
-	};
-
 	if (articles.length === 0) {
 		return (
 			<div className="p-4 text-center text-gray-500">No articles found.</div>
@@ -83,51 +51,11 @@ export function ArticleList({ articles, showReadStatus = true }: Props) {
 					</h3>
 					<div className="space-y-1">
 						{feedArticles.map((article) => (
-							<div
+							<ArticleItem
 								key={article.id}
-								className={`group flex items-center gap-3 rounded-lg border p-3 hover:bg-gray-50 ${
-									article.isRead
-										? "border-gray-200 bg-white"
-										: "border-blue-200 bg-blue-50"
-								}`}
-							>
-								{showReadStatus && (
-									<button
-										type="button"
-										onClick={() => handleToggleRead(article.id, article.isRead)}
-										className={`flex-shrink-0 rounded p-1 transition-colors ${
-											article.isRead
-												? "text-gray-400 hover:text-gray-600"
-												: "text-blue-600 hover:text-blue-700"
-										}`}
-										title={article.isRead ? "Mark as unread" : "Mark as read"}
-									>
-										<FontAwesomeIcon
-											icon={article.isRead ? faCheck : faCircle}
-											className="w-4 h-4"
-										/>
-									</button>
-								)}
-								<div className="flex-1 min-w-0">
-									<button
-										type="button"
-										onClick={() => handleArticleClick(article)}
-										className={`text-left w-full group-hover:text-blue-600 transition-colors ${
-											article.isRead
-												? "text-gray-700"
-												: "text-gray-900 font-medium"
-										}`}
-									>
-										<div className="flex items-center gap-2">
-											<span className="truncate">{article.title}</span>
-											<FontAwesomeIcon
-												icon={faExternalLinkAlt}
-												className="w-3 h-3 opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0"
-											/>
-										</div>
-									</button>
-								</div>
-							</div>
+								article={article}
+								showReadStatus={showReadStatus}
+							/>
 						))}
 					</div>
 				</div>
-- 
cgit v1.2.3-70-g09d2

