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 && ( +
+ +
+ )}
)}