From 5bfff22bab7bc100df39157435c1c526c872f644 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Thu, 9 Oct 2025 12:15:32 +0000 Subject: [PATCH] Improve media loading with infinite scrolling on upload and new post pages Integrates IntersectionObserver for infinite scrolling in MediaUpload and NewPost components, enhancing performance by loading media items in batches. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/lEm79pj --- client/src/components/media-upload.tsx | 37 +++++++- client/src/pages/new-post.tsx | 119 ++++++++++++++++--------- 2 files changed, 110 insertions(+), 46 deletions(-) diff --git a/client/src/components/media-upload.tsx b/client/src/components/media-upload.tsx index ea14abb..7ce35bb 100644 --- a/client/src/components/media-upload.tsx +++ b/client/src/components/media-upload.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback } from "react"; +import { useState, useCallback, useRef, useEffect } from "react"; import { useDropzone } from "react-dropzone"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent } from "@/components/ui/dialog"; @@ -11,6 +11,8 @@ import { SiFacebook, SiInstagram } from "react-icons/si"; export default function MediaUpload() { const { toast } = useToast(); const [selectedFile, setSelectedFile] = useState(null); + const [visibleCount, setVisibleCount] = useState(5); + const loadMoreRef = useRef(null); const { data: mediaList } = useQuery({ queryKey: ["/api/media"], @@ -69,6 +71,28 @@ export default function MediaUpload() { }, }); + // Scroll infini - IntersectionObserver + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && mediaList && visibleCount < (mediaList as any[]).length) { + setVisibleCount(prev => Math.min(prev + 5, (mediaList as any[]).length)); + } + }, + { threshold: 0.1 } + ); + + if (loadMoreRef.current) { + observer.observe(loadMoreRef.current); + } + + return () => { + if (loadMoreRef.current) { + observer.unobserve(loadMoreRef.current); + } + }; + }, [mediaList, visibleCount]); + return (
@@ -123,8 +147,8 @@ export default function MediaUpload() { -
- {(mediaList as any[])?.slice(0, 5).map((media: any) => ( +
+ {(mediaList as any[])?.slice(0, visibleCount).map((media: any) => (
setSelectedFile(media)} @@ -165,6 +189,13 @@ export default function MediaUpload() {
))} + + {/* Élément sentinelle pour le scroll infini */} + {mediaList && visibleCount < (mediaList as any[]).length && ( +
+ +
+ )}
diff --git a/client/src/pages/new-post.tsx b/client/src/pages/new-post.tsx index 3bd1486..9baacbc 100644 --- a/client/src/pages/new-post.tsx +++ b/client/src/pages/new-post.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useRef } from "react"; +import { useState, useCallback, useRef, useEffect } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useLocation } from "wouter"; import { Send, Sparkles, Image as ImageIcon, Calendar, Upload, Camera, GripVertical, Loader2 } from "lucide-react"; @@ -112,6 +112,8 @@ export default function NewPost() { const [postText, setPostText] = useState(''); const [postType, setPostType] = useState<'feed' | 'story' | 'both'>('feed'); const [previewModalOpen, setPreviewModalOpen] = useState(false); + const [visibleMediaCount, setVisibleMediaCount] = useState(12); + const loadMoreMediaRef = useRef(null); const { data: pages = [] } = useQuery({ queryKey: ['/api/pages'], @@ -310,6 +312,28 @@ export default function NewPost() { } }; + // Scroll infini - IntersectionObserver pour la grille de médias + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && mediaList && visibleMediaCount < mediaList.length) { + setVisibleMediaCount(prev => Math.min(prev + 12, mediaList.length)); + } + }, + { threshold: 0.1 } + ); + + if (loadMoreMediaRef.current) { + observer.observe(loadMoreMediaRef.current); + } + + return () => { + if (loadMoreMediaRef.current) { + observer.unobserve(loadMoreMediaRef.current); + } + }; + }, [mediaList, visibleMediaCount]); + return (
{sidebarOpen && ( @@ -451,48 +475,57 @@ export default function NewPost() {
Toutes les photos
-
- {mediaList.map((media) => { - const isSelected = selectedMedia.includes(media.id); - - return ( - - ); - })} +
+
+ {mediaList.slice(0, visibleMediaCount).map((media) => { + const isSelected = selectedMedia.includes(media.id); + + return ( + + ); + })} +
+ + {/* Élément sentinelle pour le scroll infini */} + {mediaList && visibleMediaCount < mediaList.length && ( +
+ +
+ )}
)}