From e3c379d069ffa9661561d25cdbf2f5894a2f8ee8 Mon Sep 17 00:00:00 2001
From: Adam Mathes <adam@adammathes.com>
Date: Sat, 14 Feb 2026 08:58:38 -0800
Subject: Refactor: project structure, implement dependency injection, and
 align v2 UI with v1

---
 frontend/src/components/FeedItem.tsx | 146 +++++++++++++++++------------------
 1 file changed, 72 insertions(+), 74 deletions(-)

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

diff --git a/frontend/src/components/FeedItem.tsx b/frontend/src/components/FeedItem.tsx
index b86e60c..9b40114 100644
--- a/frontend/src/components/FeedItem.tsx
+++ b/frontend/src/components/FeedItem.tsx
@@ -3,86 +3,84 @@ import type { Item } from '../types';
 import './FeedItem.css';
 
 interface FeedItemProps {
-    item: Item;
+  item: Item;
 }
 
 export default function FeedItem({ item: initialItem }: FeedItemProps) {
-    const [item, setItem] = useState(initialItem);
-    const [loading, setLoading] = useState(false);
+  const [item, setItem] = useState(initialItem);
+  const [loading, setLoading] = useState(false);
 
-    useEffect(() => {
-        setItem(initialItem);
-    }, [initialItem]);
+  useEffect(() => {
+    setItem(initialItem);
+  }, [initialItem]);
 
+  const toggleStar = () => {
+    updateItem({ ...item, starred: !item.starred });
+  };
 
-    const toggleStar = () => {
-        updateItem({ ...item, starred: !item.starred });
-    };
+  const updateItem = (newItem: Item) => {
+    setLoading(true);
+    // Optimistic update
+    const previousItem = item;
+    setItem(newItem);
 
-    const updateItem = (newItem: Item) => {
-        setLoading(true);
-        // Optimistic update
-        const previousItem = item;
-        setItem(newItem);
+    fetch(`/api/item/${newItem._id}`, {
+      method: 'PUT',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        _id: newItem._id,
+        read: newItem.read,
+        starred: newItem.starred,
+      }),
+    })
+      .then((res) => {
+        if (!res.ok) {
+          throw new Error('Failed to update item');
+        }
+        return res.json();
+      })
+      .then(() => {
+        // Confirm with server response if needed, but for now we trust the optimistic update
+        // or we could setItem(updated) if the server returns the full object
+        setLoading(false);
+      })
+      .catch((err) => {
+        console.error('Error updating item:', err);
+        // Revert on error
+        setItem(previousItem);
+        setLoading(false);
+      });
+  };
 
-        fetch(`/api/item/${newItem._id}`, {
-            method: 'PUT',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                _id: newItem._id,
-                read: newItem.read,
-                starred: newItem.starred,
-            }),
-        })
-            .then((res) => {
-                if (!res.ok) {
-                    throw new Error('Failed to update item');
-                }
-                return res.json();
-            })
-            .then(() => {
-                // Confirm with server response if needed, but for now we trust the optimistic update
-                // or we could setItem(updated) if the server returns the full object
-                setLoading(false);
-            })
-            .catch((err) => {
-                console.error('Error updating item:', err);
-                // Revert on error
-                setItem(previousItem);
-                setLoading(false);
-            });
-    };
-
-    return (
-        <li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}>
-            <div className="item-header">
-                <a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title">
-                    {item.title || '(No Title)'}
-                </a>
-                <button
-                    onClick={(e) => {
-                        e.stopPropagation();
-                        toggleStar();
-                    }}
-                    className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
-                    title={item.starred ? "Unstar" : "Star"}
-                >
-                    ★
-                </button>
-            </div>
-            <div className="dateline">
-                <a href={item.url} target="_blank" rel="noopener noreferrer">
-                    {new Date(item.publish_date).toLocaleDateString()}
-                    {item.feed_title && ` - ${item.feed_title}`}
-                </a>
-                <div className="item-actions" style={{ display: 'inline-block', float: 'right' }}>
-                </div>
-            </div>
-            {item.description && (
-                <div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} />
-            )}
-        </li>
-    );
+  return (
+    <li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}>
+      <div className="item-header">
+        <a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title">
+          {item.title || '(No Title)'}
+        </a>
+        <button
+          onClick={(e) => {
+            e.stopPropagation();
+            toggleStar();
+          }}
+          className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
+          title={item.starred ? 'Unstar' : 'Star'}
+        >
+          ★
+        </button>
+      </div>
+      <div className="dateline">
+        <a href={item.url} target="_blank" rel="noopener noreferrer">
+          {new Date(item.publish_date).toLocaleDateString()}
+          {item.feed_title && ` - ${item.feed_title}`}
+        </a>
+        <div className="item-actions" style={{ display: 'inline-block', float: 'right' }}></div>
+      </div>
+      {item.description && (
+        <div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} />
+      )}
+    </li>
+  );
 }
-- 
cgit v1.2.3

