7 Commity 7aabf6d36b ... 7339d2f695

Autor SHA1 Wiadomość Data
  gpt-engineer-app[bot] 7339d2f695 Ajouter pagination accueil 3 dni temu
  gpt-engineer-app[bot] bf4878c582 Changes 3 dni temu
  MrDuhaz 60dfdf20ad Fix formatting of project info section in README 3 dni temu
  gpt-engineer-app[bot] 4a9ace157a Remove read receipts toasts 3 dni temu
  gpt-engineer-app[bot] cab016b937 Changes 3 dni temu
  gpt-engineer-app[bot] 6e57f6d277 Mets à jour changelog 3 dni temu
  gpt-engineer-app[bot] 80952f4239 Changes 3 dni temu

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-## Project info ##
+## Project info
 
 **URL**: (https://feeds.duhaz.fr)
 ## How can I edit this code?

+ 4 - 4
src/components/ui/pagination.tsx

@@ -64,13 +64,13 @@ const PaginationPrevious = ({
   ...props
 }: React.ComponentProps<typeof PaginationLink>) => (
   <PaginationLink
-    aria-label="Go to previous page"
+    aria-label="Page précédente"
     size="default"
     className={cn("gap-1 pl-2.5", className)}
     {...props}
   >
     <ChevronLeft className="h-4 w-4" />
-    <span>Previous</span>
+    <span>Précédent</span>
   </PaginationLink>
 )
 PaginationPrevious.displayName = "PaginationPrevious"
@@ -80,12 +80,12 @@ const PaginationNext = ({
   ...props
 }: React.ComponentProps<typeof PaginationLink>) => (
   <PaginationLink
-    aria-label="Go to next page"
+    aria-label="Page suivante"
     size="default"
     className={cn("gap-1 pr-2.5", className)}
     {...props}
   >
-    <span>Next</span>
+    <span>Suivant</span>
     <ChevronRight className="h-4 w-4" />
   </PaginationLink>
 )

+ 27 - 0
src/data/changelog.ts

@@ -8,6 +8,33 @@ export interface ChangelogEntry {
 }
 
 export const changelogData: ChangelogEntry[] = [
+  {
+    version: "1.6.0",
+    date: "2025-12-03",
+    category: "improvement",
+    title: "Optimisations de performance",
+    description: "Amélioration significative des performances et de la qualité du code.",
+    details: [
+      "Memoization du décodage HTML avec cache LRU",
+      "Lazy loading des images pour un chargement plus rapide",
+      "Suppression des logs de debug en production",
+      "Amélioration du typage TypeScript",
+      "Optimisation des calculs de filtres avec useMemo"
+    ]
+  },
+  {
+    version: "1.5.0",
+    date: "2025-12-02",
+    category: "security",
+    title: "Renforcement de la sécurité RLS",
+    description: "Correction et amélioration des politiques de sécurité Row Level Security.",
+    details: [
+      "Restriction des modifications de flux aux super-utilisateurs",
+      "Protection de la table super_users contre les accès non autorisés",
+      "Correction des accès null-safe sur les données utilisateur",
+      "Gestion des erreurs localStorage améliorée"
+    ]
+  },
   {
     version: "1.4.0",
     date: "2025-01-14",

+ 0 - 1
src/hooks/useArticles.tsx

@@ -69,7 +69,6 @@ export function useArticles() {
     setArticles(prev => prev.map(item => 
       item.id === id ? { ...item, isRead: true } : item
     ));
-    toast.success("Article marqué comme lu");
   };
 
   const deleteArticle = (id: string) => {

+ 0 - 1
src/hooks/useFeedArticles.tsx

@@ -169,7 +169,6 @@ export function useFeedArticles(feedId: string, page: number = 1) {
       setArticles(prev => prev.map(item => 
         item.id === articleId ? { ...item, isRead: true } : item
       ));
-      toast.success("Article marqué comme lu");
     } catch (error) {
       console.error('Error marking as read:', error);
     }

+ 0 - 2
src/hooks/useRealArticles.tsx

@@ -353,10 +353,8 @@ export function useRealArticles(dateFilter?: 'today' | 'yesterday' | null, showF
 
       if (!showReadArticles) {
         setArticles(prev => prev.filter(item => item.id !== articleId));
-        toast.success("Article marqué comme lu");
       } else {
         setArticles(prev => prev.map(item => item.id === articleId ? { ...item, isRead: true } : item));
-        toast.success("Article marqué comme lu");
       }
     } catch (error) {
       if (isDev) console.error('Error marking as read:', error);

+ 68 - 3
src/pages/Index.tsx

@@ -11,11 +11,13 @@ import AddFeedModal from '@/components/AddFeedModal';
 import ArticleModal from '@/components/ArticleModal';
 import { Badge } from '@/components/ui/badge';
 import { Button } from '@/components/ui/button';
-import { Card, CardContent } from '@/components/ui/card';
 import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer';
-import { RefreshCw, Filter, User, Rss, Plus } from 'lucide-react';
+import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PaginationEllipsis } from '@/components/ui/pagination';
+import { RefreshCw, Filter, Rss, Plus } from 'lucide-react';
 import { toast } from 'sonner';
 import { Link, useNavigate } from 'react-router-dom';
+
+const ARTICLES_PER_PAGE = 20;
 const Index = () => {
   const {
     user
@@ -60,6 +62,7 @@ const Index = () => {
   const [isAddFeedModalOpen, setIsAddFeedModalOpen] = useState(false);
   const [selectedArticle, setSelectedArticle] = useState<NewsItem | null>(null);
   const [isArticleModalOpen, setIsArticleModalOpen] = useState(false);
+  const [currentPage, setCurrentPage] = useState(1);
   console.log('🏠 Index page - Articles count:', articles.length, 'Loading:', loading, 'User:', !!user);
   const filteredNews = useMemo(() => {
     let filtered = articles;
@@ -87,6 +90,19 @@ const Index = () => {
   const regularArticles = useMemo(() => {
     return filteredNews.filter(article => !article.isPinned);
   }, [filteredNews]);
+
+  // Pagination logic
+  const totalPages = Math.ceil(regularArticles.length / ARTICLES_PER_PAGE);
+  const paginatedArticles = useMemo(() => {
+    const startIndex = (currentPage - 1) * ARTICLES_PER_PAGE;
+    return regularArticles.slice(startIndex, startIndex + ARTICLES_PER_PAGE);
+  }, [regularArticles, currentPage]);
+
+  // Reset page when filters change
+  useEffect(() => {
+    setCurrentPage(1);
+  }, [selectedCategory, searchQuery, dateFilter, showFollowedOnly, showDiscoveryMode, showReadArticles]);
+
   const pinnedCount = articles.filter(item => item.isPinned).length;
   const unreadCount = articles.filter(item => !item.isRead).length;
 
@@ -313,7 +329,56 @@ const Index = () => {
                     </Button>
                   </div>}
               </div> : <div className="space-y-4">
-                {regularArticles.map(item => <NewsCard key={item.id} news={item} onTogglePin={togglePin} onMarkAsRead={markAsRead} onDelete={deleteArticle} onOpenArticle={handleOpenArticle} onSourceClick={handleSourceClick} isDiscoveryMode={showDiscoveryMode} />)}
+                {paginatedArticles.map(item => <NewsCard key={item.id} news={item} onTogglePin={togglePin} onMarkAsRead={markAsRead} onDelete={deleteArticle} onOpenArticle={handleOpenArticle} onSourceClick={handleSourceClick} isDiscoveryMode={showDiscoveryMode} />)}
+                
+                {/* Pagination */}
+                {totalPages > 1 && (
+                  <Pagination className="mt-8">
+                    <PaginationContent>
+                      <PaginationItem>
+                        <PaginationPrevious 
+                          onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
+                          className={currentPage === 1 ? 'pointer-events-none opacity-50' : 'cursor-pointer'}
+                        />
+                      </PaginationItem>
+                      
+                      {Array.from({ length: totalPages }, (_, i) => i + 1)
+                        .filter(page => page === 1 || page === totalPages || Math.abs(page - currentPage) <= 1)
+                        .map((page, index, array) => (
+                          <>
+                            {index > 0 && array[index - 1] !== page - 1 && (
+                              <PaginationItem key={`ellipsis-${page}`}>
+                                <PaginationEllipsis />
+                              </PaginationItem>
+                            )}
+                            <PaginationItem key={page}>
+                              <PaginationLink
+                                onClick={() => setCurrentPage(page)}
+                                isActive={currentPage === page}
+                                className="cursor-pointer"
+                              >
+                                {page}
+                              </PaginationLink>
+                            </PaginationItem>
+                          </>
+                        ))}
+                      
+                      <PaginationItem>
+                        <PaginationNext 
+                          onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
+                          className={currentPage === totalPages ? 'pointer-events-none opacity-50' : 'cursor-pointer'}
+                        />
+                      </PaginationItem>
+                    </PaginationContent>
+                  </Pagination>
+                )}
+                
+                {/* Info pagination */}
+                {totalPages > 1 && (
+                  <p className="text-center text-sm text-muted-foreground">
+                    Page {currentPage} sur {totalPages} ({regularArticles.length} articles)
+                  </p>
+                )}
               </div>}
           </div>
         </div>