{article.title}
{article.ai_summary ? ({article.ai_summary}
{article.summary.replace(/<[^>]*>?/gm, '').substring(0, 160)}...
)}import { useState } from 'react'; import { useSwipeable } from 'react-swipeable'; import { type Article, articlesApi } from '../api/articles'; interface ArticleCardProps { article: Article; onClick: (article: Article) => void; onToggleRead: (id: string, is_read: boolean) => void; onToggleFavorite: (id: string) => void; } export function ArticleCard({ article, onClick, onToggleRead, onToggleFavorite }: ArticleCardProps) { const [swipeOffset, setSwipeOffset] = useState(0); const [isSwiping, setIsSwiping] = useState(false); const [isSummarizing, setIsSummarizing] = useState(false); const handlers = useSwipeable({ onSwiping: (event) => { const offset = event.deltaX; // Limit swipe to 80px if (Math.abs(offset) < 100) { setSwipeOffset(offset); setIsSwiping(true); } }, onSwipedRight: () => { if (swipeOffset > 50) { onToggleRead(article.id, !article.is_read); } resetSwipe(); }, onSwipedLeft: () => { if (swipeOffset < -50) { onToggleFavorite(article.id); } resetSwipe(); }, onSwiped: () => resetSwipe(), trackMouse: true, preventScrollOnSwipe: true, delta: 10, }); const resetSwipe = () => { setSwipeOffset(0); setIsSwiping(false); }; const handleSummarize = async (e: React.MouseEvent) => { e.stopPropagation(); if (article.ai_summary || isSummarizing) return; setIsSummarizing(true); try { await articlesApi.summarize(article.id); // The article will be updated via WebSocket broadcast } catch (error) { console.error('Failed to summarize:', error); alert('Erreur lors de la génération du résumé. Vérifiez votre clé API OpenRouter.'); } finally { setIsSummarizing(false); } }; return (
{article.ai_summary}
{article.summary.replace(/<[^>]*>?/gm, '').substring(0, 160)}...
)}