浏览代码

Feat: Show followed feeds on index page

Show only followed feeds on the index page for logged-in users.
Show the latest articles for non-logged-in users.
gpt-engineer-app[bot] 6 月之前
父节点
当前提交
4a75837668
共有 2 个文件被更改,包括 174 次插入45 次删除
  1. 92 0
      src/hooks/useArticles.tsx
  2. 82 45
      src/pages/Index.tsx

+ 92 - 0
src/hooks/useArticles.tsx

@@ -0,0 +1,92 @@
+
+import { useState, useEffect } from 'react';
+import { supabase } from '@/integrations/supabase/client';
+import { useAuth } from './useAuth';
+import { NewsItem } from '@/types/news';
+import { toast } from 'sonner';
+
+export function useArticles() {
+  const [articles, setArticles] = useState<NewsItem[]>([]);
+  const [loading, setLoading] = useState(true);
+  const { user } = useAuth();
+
+  const fetchArticles = async () => {
+    try {
+      setLoading(true);
+      
+      if (user) {
+        // For authenticated users, fetch articles from their followed feeds
+        const { data: userFeeds, error: userFeedsError } = await supabase
+          .from('user_feeds')
+          .select(`
+            feed_id,
+            feeds!inner(*)
+          `)
+          .eq('user_id', user.id)
+          .eq('is_followed', true);
+
+        if (userFeedsError) {
+          console.error('Error fetching user feeds:', userFeedsError);
+          toast.error('Erreur lors du chargement de vos flux');
+          return;
+        }
+
+        // For now, we'll use mock data but filter by followed feeds
+        // In a real app, you'd have an articles table linked to feeds
+        const followedFeedNames = userFeeds?.map(uf => uf.feeds.name) || [];
+        
+        // Import mock data and filter by followed feeds
+        const { newsItems } = await import('@/data/mockNews');
+        const filteredArticles = newsItems.filter(article => 
+          followedFeedNames.some(feedName => 
+            article.source.toLowerCase().includes(feedName.toLowerCase()) ||
+            feedName.toLowerCase().includes(article.source.toLowerCase())
+          )
+        );
+        
+        setArticles(filteredArticles);
+      } else {
+        // For visitors, show all recent articles
+        const { newsItems } = await import('@/data/mockNews');
+        setArticles(newsItems);
+      }
+    } catch (error) {
+      console.error('Error in fetchArticles:', error);
+      toast.error('Erreur lors du chargement des articles');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const togglePin = (id: string) => {
+    setArticles(prev => prev.map(item => 
+      item.id === id ? { ...item, isPinned: !item.isPinned } : item
+    ));
+    toast.success("Article épinglé mis à jour");
+  };
+
+  const markAsRead = (id: string) => {
+    setArticles(prev => prev.map(item => 
+      item.id === id ? { ...item, isRead: true } : item
+    ));
+    toast.success("Article marqué comme lu");
+  };
+
+  const deleteArticle = (id: string) => {
+    setArticles(prev => prev.filter(item => item.id !== id));
+    toast.success("Article supprimé");
+  };
+
+  useEffect(() => {
+    fetchArticles();
+  }, [user]);
+
+  return {
+    articles,
+    loading,
+    togglePin,
+    markAsRead,
+    deleteArticle,
+    refetch: fetchArticles
+  };
+}

+ 82 - 45
src/pages/Index.tsx

@@ -1,25 +1,29 @@
 
 import { useState, useMemo } from 'react';
-import { newsItems, categories } from '@/data/mockNews';
-import { NewsItem } from '@/types/news';
+import { categories } from '@/data/mockNews';
+import { useArticles } from '@/hooks/useArticles';
+import { useAuth } from '@/hooks/useAuth';
 import Header from '@/components/Header';
 import CategoryFilter from '@/components/CategoryFilter';
 import NewsCard from '@/components/NewsCard';
 import AddFeedModal from '@/components/AddFeedModal';
 import { Badge } from '@/components/ui/badge';
 import { Button } from '@/components/ui/button';
-import { RefreshCw, Filter } from 'lucide-react';
+import { Card, CardContent } from '@/components/ui/card';
+import { RefreshCw, Filter, User, Rss } from 'lucide-react';
 import { toast } from 'sonner';
+import { Link } from 'react-router-dom';
 
 const Index = () => {
-  const [news, setNews] = useState<NewsItem[]>(newsItems);
+  const { user } = useAuth();
+  const { articles, loading, togglePin, markAsRead, deleteArticle, refetch } = useArticles();
   const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
   const [searchQuery, setSearchQuery] = useState('');
   const [showFilters, setShowFilters] = useState(true);
   const [isAddFeedModalOpen, setIsAddFeedModalOpen] = useState(false);
 
   const filteredNews = useMemo(() => {
-    let filtered = news;
+    let filtered = articles;
     
     if (selectedCategory) {
       const category = categories.find(c => c.id === selectedCategory);
@@ -41,42 +45,32 @@ const Index = () => {
       if (!a.isPinned && b.isPinned) return 1;
       return new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime();
     });
-  }, [news, selectedCategory, searchQuery]);
+  }, [articles, selectedCategory, searchQuery]);
 
-  const pinnedCount = news.filter(item => item.isPinned).length;
-  const unreadCount = news.filter(item => !item.isRead).length;
-
-  const handleTogglePin = (id: string) => {
-    setNews(prev => prev.map(item => 
-      item.id === id ? { ...item, isPinned: !item.isPinned } : item
-    ));
-    toast.success("Article épinglé mis à jour");
-  };
-
-  const handleMarkAsRead = (id: string) => {
-    setNews(prev => prev.map(item => 
-      item.id === id ? { ...item, isRead: true } : item
-    ));
-    toast.success("Article marqué comme lu");
-  };
-
-  const handleDelete = (id: string) => {
-    setNews(prev => prev.filter(item => item.id !== id));
-    toast.success("Article supprimé");
-  };
+  const pinnedCount = articles.filter(item => item.isPinned).length;
+  const unreadCount = articles.filter(item => !item.isRead).length;
 
   const handleRefresh = () => {
+    refetch();
     toast.success("Flux actualisés");
   };
 
   const handleAddFeed = (feedData: any) => {
     console.log('Nouveau flux ajouté:', feedData);
     toast.success(`Flux "${feedData.name}" ajouté avec succès!`);
-    
-    // Ici vous pourrez ajouter la logique pour envoyer les données à votre backend MySQL
-    // Par exemple: await api.addFeed(feedData);
   };
 
+  if (loading) {
+    return (
+      <div className="min-h-screen bg-background flex items-center justify-center">
+        <div className="flex items-center gap-2">
+          <Rss className="h-6 w-6 animate-spin text-primary" />
+          <p>Chargement des articles...</p>
+        </div>
+      </div>
+    );
+  }
+
   return (
     <div className="min-h-screen bg-background">
       <Header 
@@ -87,6 +81,50 @@ const Index = () => {
       />
       
       <main className="container mx-auto px-4 py-6">
+        {/* Message pour les utilisateurs non connectés */}
+        {!user && (
+          <Card className="mb-6 border-blue-200 bg-blue-50">
+            <CardContent className="pt-6">
+              <div className="flex items-center gap-3">
+                <User className="h-5 w-5 text-blue-600" />
+                <div className="flex-1">
+                  <p className="font-medium text-blue-900">Vous consultez les derniers articles publics</p>
+                  <p className="text-sm text-blue-700">
+                    Connectez-vous pour voir uniquement les articles de vos flux suivis et personnaliser votre expérience.
+                  </p>
+                </div>
+                <Link to="/auth">
+                  <Button size="sm">
+                    Se connecter
+                  </Button>
+                </Link>
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
+        {/* Message pour les utilisateurs connectés sans articles */}
+        {user && articles.length === 0 && (
+          <Card className="mb-6 border-yellow-200 bg-yellow-50">
+            <CardContent className="pt-6">
+              <div className="flex items-center gap-3">
+                <Rss className="h-5 w-5 text-yellow-600" />
+                <div className="flex-1">
+                  <p className="font-medium text-yellow-900">Aucun article trouvé</p>
+                  <p className="text-sm text-yellow-700">
+                    Vous ne suivez aucun flux pour le moment. Visitez la page de gestion des flux pour commencer à suivre des sources d'actualités.
+                  </p>
+                </div>
+                <Link to="/feeds">
+                  <Button size="sm">
+                    Gérer les flux
+                  </Button>
+                </Link>
+              </div>
+            </CardContent>
+          </Card>
+        )}
+
         <div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
           {/* Sidebar */}
           <div className={`lg:col-span-1 space-y-6 ${!showFilters && 'hidden lg:block'}`}>
@@ -94,7 +132,7 @@ const Index = () => {
               categories={categories}
               selectedCategory={selectedCategory}
               onCategoryChange={setSelectedCategory}
-              newsCount={news.length}
+              newsCount={articles.length}
             />
             
             <div className="bg-card border rounded-lg p-4 space-y-3">
@@ -102,7 +140,7 @@ const Index = () => {
               <div className="space-y-2 text-sm">
                 <div className="flex justify-between">
                   <span className="text-muted-foreground">Total articles</span>
-                  <Badge variant="outline">{news.length}</Badge>
+                  <Badge variant="outline">{articles.length}</Badge>
                 </div>
                 <div className="flex justify-between">
                   <span className="text-muted-foreground">Non lus</span>
@@ -121,10 +159,7 @@ const Index = () => {
             <div className="flex items-center justify-between">
               <div className="flex items-center gap-4">
                 <h2 className="text-2xl font-bold">
-                  {selectedCategory ? 
-                    categories.find(c => c.id === selectedCategory)?.name : 
-                    'Tous les flux'
-                  }
+                  {user ? 'Vos flux suivis' : 'Derniers articles'}
                 </h2>
                 <Badge variant="outline">
                   {filteredNews.length} article{filteredNews.length !== 1 ? 's' : ''}
@@ -167,9 +202,9 @@ const Index = () => {
                   <NewsCard
                     key={item.id}
                     news={item}
-                    onTogglePin={handleTogglePin}
-                    onMarkAsRead={handleMarkAsRead}
-                    onDelete={handleDelete}
+                    onTogglePin={togglePin}
+                    onMarkAsRead={markAsRead}
+                    onDelete={deleteArticle}
                   />
                 ))}
               </div>
@@ -178,12 +213,14 @@ const Index = () => {
         </div>
       </main>
 
-      <AddFeedModal 
-        isOpen={isAddFeedModalOpen}
-        onClose={() => setIsAddFeedModalOpen(false)}
-        onAddFeed={handleAddFeed}
-        categories={categories}
-      />
+      {user && (
+        <AddFeedModal 
+          isOpen={isAddFeedModalOpen}
+          onClose={() => setIsAddFeedModalOpen(false)}
+          onAddFeed={handleAddFeed}
+          categories={categories}
+        />
+      )}
     </div>
   );
 };