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>
);
}
|