From ca3acd2df4efadc91b62c501ac2064710f13fb22 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Thu, 9 Oct 2025 12:04:54 +0000 Subject: [PATCH 1/3] Transitioned from Plan to Build mode Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/AfZNdRv 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 2/3] 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 && ( +
+ +
+ )}
)} From 91b15c11c6686ae1b8f1fe52d2eb93fe29b4d548 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Thu, 9 Oct 2025 12:16:03 +0000 Subject: [PATCH 3/3] Add infinite scroll to media library and new post pages Implement infinite scroll for media library and new post pages using IntersectionObserver, loading more content as the user scrolls. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/lEm79pj --- replit.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/replit.md b/replit.md index f37ed4c..ad54cda 100644 --- a/replit.md +++ b/replit.md @@ -43,6 +43,12 @@ Authentication uses **Passport.js** with local strategy and `bcrypt` for passwor The UI is built with **Radix UI** and **shadcn/ui**, leveraging **Tailwind CSS** for styling. The default theme is dark mode. Key design decisions include a component-based architecture for reusability, custom path aliases, and a responsive, mobile-first approach. The platform supports multi-photo carousel posts with drag-and-drop reordering, and a **PreviewModal** offers realistic rendering of posts across Facebook and Instagram formats before publishing. The calendar view is responsive, with a grid layout for desktop and an expandable list view for mobile, allowing editing and deletion of scheduled posts directly from the calendar. +**Infinite Scroll**: Both the media library and new post pages implement infinite scroll using IntersectionObserver for performance optimization: +- **Media Library**: Loads 5 media items initially, then 5 more when scrolling to bottom (max height: 500px) +- **New Post Page**: Loads 12 media items initially in a 3-column grid, then 12 more when scrolling (max height: 500px) +- Visual loading indicator ("Chargement..." with spinner) appears when more content is available +- Proper cleanup to prevent memory leaks + ## External Dependencies ### AI Content Generation