From 15656ac816d3e726d7c53f976cdb682249e8cd9f Mon Sep 17 00:00:00 2001 From: charleswrayjr Date: Sat, 27 Sep 2025 00:20:12 -0500 Subject: [PATCH] Adding proper file upload management to chat #PHO-2. --- src/app/{components => views/Chat}/Chat.jsx | 116 ++++++++++++++++++-- 1 file changed, 108 insertions(+), 8 deletions(-) rename src/app/{components => views/Chat}/Chat.jsx (77%) diff --git a/src/app/components/Chat.jsx b/src/app/views/Chat/Chat.jsx similarity index 77% rename from src/app/components/Chat.jsx rename to src/app/views/Chat/Chat.jsx index 1919425..43a3cf3 100755 --- a/src/app/components/Chat.jsx +++ b/src/app/views/Chat/Chat.jsx @@ -5,7 +5,7 @@ import React, { useEffect, useRef, useState } from 'react'; import axios from 'axios'; -import { API_BASE_URL } from '../../App'; +import { API_BASE_URL } from '../../../App'; import AddIcon from '@mui/icons-material/Add'; import { Button, @@ -16,9 +16,13 @@ import { Grid, IconButton, InputAdornment, - TextField + TextField, + Box, + CardMedia, + Chip, + LinearProgress } from '@mui/material'; -import ChatBubble from './ChatBubble'; +import ChatBubble from '../../components/ChatBubble'; /** * @typedef {Object} User @@ -35,7 +39,7 @@ import ChatBubble from './ChatBubble'; * @param {Object} props.socket - Socket.io client instance * @returns {JSX.Element} Rendered component */ -const Chat = ( { user, socket } ) => { +const Chat = ( { user, socket, maxSizeMB = 10 } ) => { const [conversations, setConversations] = useState( [] ); const [selectedConversation, setSelectedConversation] = useState( null ); const [messages, setMessages] = useState( [] ); @@ -48,6 +52,19 @@ const Chat = ( { user, socket } ) => { const messagesEndRef = useRef( null ); const fileRef = useRef( null ); + /** + * @type {[MediaFile[], React.Dispatch>]} + */ + const [selectedMedia, setSelectedMedia] = useState([]); + /** + * @type {[string[], React.Dispatch>]} + */ + const [previews, setPreviews] = useState([]); + /** + * @type {[number, React.Dispatch>]} + */ + const [progress, setProgress] = useState(0); + useEffect( () => { if (user) { const fetchData = async () => { @@ -245,9 +262,58 @@ const Chat = ( { user, socket } ) => { ) ) ); + /** + * Handles file selection and validation + * @param {React.ChangeEvent} event + */ + const handleFileChange = (event) => { + const files = Array.from(event.target.files); + const validMedia = files + .map((file) => ({ + file, + type: file.type.startsWith('image/') ? 'image' : file.type.startsWith('video/') ? 'video' : null, + })) + .filter((media) => { + if (!media.type) { + alert('Please select image or video files only.'); + return false; + } + if (media.file.size > maxSizeMB * 1024 * 1024) { + alert(`File size exceeds ${maxSizeMB}MB.`); + return false; + } + return true; + }); + + setSelectedMedia(validMedia); + + // Generate previews + const newPreviews = validMedia.map((media) => URL.createObjectURL(media.file)); + setPreviews(newPreviews); + + // Trigger upload if provided + // if (onUpload && validMedia.length > 0) { + // uploadFiles(validMedia.map((media) => media.file)); + // } + }; + + /** + * Removes a file from the selection + * @param {number} index + */ + const removeFile = (index) => { + setSelectedMedia((prev) => prev.filter((_, i) => i !== index)); + setPreviews((prev) => { + const newPreviews = [...prev]; + URL.revokeObjectURL(newPreviews[index]); // Free memory + newPreviews.splice(index, 1); + return newPreviews; + }); + }; + return ( { isSidebarOpen && { getConvos() } @@ -282,14 +348,44 @@ const Chat = ( { user, socket } ) => { ) }
+ + {/* Progress Bar */} + {progress > 0 && ( + + )} + { selectedConversation && previews.length > 0 && ( + + {previews.map((preview, index) => ( + + {selectedMedia[index].type === 'image' ? ( + + ) : ( + + )} + removeFile(index)} + sx={{ position: 'absolute', top: 4, right: 4 }} + /> + + ))} + + ) } setAttachedFiles( Array.from( e.target.files ) ) } + onChange={ e => handleFileChange(e) } className="mb-2"/> { selectedConversation ? ( { position="start"> , @@ -320,6 +419,7 @@ const Chat = ( { user, socket } ) => { } } }/> + /*