From bd2508211760edbc1bad1d515587d08fd2ec99c9 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Fri, 13 Feb 2026 06:58:30 -0800 Subject: Implement Item Interactions (read/star) with tests --- frontend/coverage/src/components/FeedItem.tsx.html | 352 +++++++++++++++++++++ 1 file changed, 352 insertions(+) create mode 100644 frontend/coverage/src/components/FeedItem.tsx.html (limited to 'frontend/coverage/src/components/FeedItem.tsx.html') diff --git a/frontend/coverage/src/components/FeedItem.tsx.html b/frontend/coverage/src/components/FeedItem.tsx.html new file mode 100644 index 0000000..418ab70 --- /dev/null +++ b/frontend/coverage/src/components/FeedItem.tsx.html @@ -0,0 +1,352 @@ + + + + + + Code coverage report for src/components/FeedItem.tsx + + + + + + + + + +
+
+

All files / src/components FeedItem.tsx

+
+ +
+ 94.73% + Statements + 18/19 +
+ + +
+ 91.66% + Branches + 22/24 +
+ + +
+ 100% + Functions + 7/7 +
+ + +
+ 94.73% + Lines + 18/19 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90  +  +  +  +  +  +  +  +  +12x +12x +  +12x +2x +  +  +12x +1x +  +  +12x +3x +  +3x +3x +  +3x +  +  +  +  +  +  +  +  +  +  +  +2x +  +  +2x +  +  +  +  +2x +  +  +1x +  +1x +1x +  +  +  +12x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import { useState } from 'react';
+import type { Item } from '../types';
+import './FeedItem.css';
+ 
+interface FeedItemProps {
+    item: Item;
+}
+ 
+export default function FeedItem({ item: initialItem }: FeedItemProps) {
+    const [item, setItem] = useState(initialItem);
+    const [loading, setLoading] = useState(false);
+ 
+    const toggleRead = () => {
+        updateItem({ ...item, read: !item.read });
+    };
+ 
+    const toggleStar = () => {
+        updateItem({ ...item, starred: !item.starred });
+    };
+ 
+    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) => {
+                Iif (!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>
+                <div className="item-actions">
+                    <button
+                        onClick={toggleRead}
+                        className={`action-btn ${item.read ? 'is-read' : 'is-unread'}`}
+                        title={item.read ? "Mark as unread" : "Mark as read"}
+                    >
+                        {item.read ? '📖' : 'uo'}
+                    </button>
+                    <button
+                        onClick={toggleStar}
+                        className={`action-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
+                        title={item.starred ? "Unstar" : "Star"}
+                    >
+                        {item.starred ? '★' : '☆'}
+                    </button>
+                </div>
+            </div>
+            <div className="item-meta">
+                <span className="item-date">{new Date(item.publish_date).toLocaleDateString()}</span>
+                {item.feed_title && <span className="item-feed"> - {item.feed_title}</span>}
+            </div>
+            {item.description && (
+                <div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} />
+            )}
+        </li>
+    );
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file -- cgit v1.2.3