Skip to content

Commit

Permalink
Merge pull request #20 from SimerusM/chat-rerendering-issue
Browse files Browse the repository at this point in the history
Chat rerendering issue
  • Loading branch information
SimerusM authored Aug 29, 2024
2 parents bb0ff89 + 09c5cab commit 16b92ef
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 86 deletions.
20 changes: 0 additions & 20 deletions api-server/test.js

This file was deleted.

75 changes: 75 additions & 0 deletions client/src/components/Chat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// src/components/Chat.js
import React, { useState, useRef, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import toast from 'react-hot-toast';
import Button from './Button';
import ChatHandler from '../services/chatHandler';
import ChatInput from './ChatInput';


const Chat = ({ meeting_id, username, socket }) => {
const [chatHistory, setChatHistory] = useState([]);
const chatHandler = useRef(null);

const initializeChat = () => {
if (!username) {
toast.error('Please enter a username before joining a meeting.');
return;
}

const errorHandler = (error) => {
console.error(error);
toast.error(error.message);
}

// Assuming ChatHandler is a utility to handle the chat logic
chatHandler.current = new ChatHandler(meeting_id, username, socket, setChatHistory, errorHandler);
chatHandler.current.initialize();
};

useEffect(() => {
initializeChat();
}, [meeting_id, username, socket]);

const handleSendMessage = useCallback((message) => {
if (!message.trim()) {
toast.error('Please enter a message before sending.');
return;
}
chatHandler.current.sendMessage(message);
setChatHistory(prevHistory => [...prevHistory, { sender: username, text: message }]);
}, [username]);

const getProfilePicture = (name) => {
return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random`;
};

return (
<div className="w-1/4 bg-white p-4 flex flex-col">
<h2 className="text-xl mb-4">Chat</h2>
<div className="flex-1 overflow-y-auto mb-4">
{chatHistory.map((msg, index) => (
<div key={index} className="mb-2 flex items-center">
<img
src={getProfilePicture(msg.sender)}
alt={`${msg.sender}'s avatar`}
className="w-8 h-8 rounded-full mr-2"
/>
<div>
<strong>{msg.sender}:</strong> {msg.text}
</div>
</div>
))}
</div>
<ChatInput onSend={handleSendMessage} />
</div>
);
};

Chat.propTypes = {
meeting_id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
socket: PropTypes.object.isRequired,
};

export default Chat;
34 changes: 34 additions & 0 deletions client/src/components/ChatInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { useRef } from 'react';
import Button from '../components/Button';
import PropTypes from 'prop-types';

const ChatInputSection = ({ onSend }) => {
const inputRef = useRef();

const handleSendMessage = (e) => {
e.preventDefault(); // Prevent the form from causing a page reload
const message = inputRef.current.value.trim();
if (message) {
onSend(message);
inputRef.current.value = ''; // Clear the input after sending
}
};

return (
<form onSubmit={handleSendMessage} className="flex">
<input
ref={inputRef}
type="text"
className="flex-1 px-2 py-1 border rounded-l"
placeholder="Type a message..."
/>
<Button type="submit" className="rounded-l-none">Send</Button>
</form>
);
};

ChatInputSection.propTypes = {
onSend: PropTypes.func.isRequired,
};

export default ChatInputSection;
89 changes: 23 additions & 66 deletions client/src/pages/MeetingPage.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router-dom';
import io from 'socket.io-client';
import ChatHandler from '../services/chatHandler';
import RTCHandler from '../services/rtcHandler';
import Chat from '../components/Chat';

import Button from '../components/Button';
import toast, { Toaster } from 'react-hot-toast';
Expand All @@ -21,11 +21,7 @@ const MeetingPage = () => {
const [isMuted, setIsMuted] = useState(false);
const [isVideoOff, setIsVideoOff] = useState(false);

const chatHandler = useRef(null);
const [chatHistory, setChatHistory] = useState([]);
const [message, setMessage] = useState('');

useEffect(() => {
const initializeMeeting = () => {
if (!username) {
toast.error('Please enter a username before joining a meeting.');
navigate('/');
Expand All @@ -47,25 +43,26 @@ const MeetingPage = () => {
console.error(error);
navigate('/');
toast.error(error.message);
}
};

chatHandler.current = new ChatHandler(meeting_id, username, socketRef.current, setChatHistory, errorHandler);
chatHandler.current.initialize();
rtcHandler.current = new RTCHandler(meeting_id, username, socketRef.current, setPeers, errorHandler);
rtcHandler.current.initialize();
};

useEffect(() => {
initializeMeeting();

return () => {
rtcHandler.current.cleanup();
socketRef.current.disconnect();
}
};
}, []);

const toggleMute = () => {
setIsMuted(prevState => {
const newMutedState = !prevState;
// Mute or unmute the audio track in the local stream
if (rtcHandler.current && rtcHandler.current.localStream) {
rtcHandler.current.localStream.getAudioTracks().forEach(track => {
console.log(track);
track.enabled = !newMutedState;
});
}
Expand All @@ -76,35 +73,15 @@ const MeetingPage = () => {
const toggleVideo = () => {
setIsVideoOff(prevState => {
const newVideoState = !prevState;
// Turn on or off the video track in the local stream
if (rtcHandler.current && rtcHandler.current.localStream) {
rtcHandler.current.localStream.getVideoTracks().forEach(track => {
console.log(track);
track.enabled = !newVideoState;
});
}
return newVideoState;
});
};

const sendMessage = (e) => {
e.preventDefault();
if (!message.trim()) {
toast.error('Please enter a message before sending.');
return;
}
chatHandler.current.sendMessage(message);
setMessage('');
};

const getProfilePicture = (name) => {
return `https://ui-avatars.com/api/?name=${encodeURIComponent(name)}&background=random`;
};

if (!username || !rtcHandler.current) {
return null;
}

const VideoElement = React.memo(({ stream, muted, peerName }) => {
const videoRef = useRef();

Expand All @@ -116,12 +93,16 @@ const MeetingPage = () => {

return (
<div className="video-container">
<video ref={videoRef} autoPlay playsInline muted={muted} className="video-element"/>
<video ref={videoRef} autoPlay playsInline muted={muted} className="video-element" />
<p className="video-username">{peerName}</p>
</div>
);
});

if (!username || !rtcHandler.current) {
return null;
}

return (
<div className="flex flex-col h-screen bg-gray-100">
<Toaster position="top-center" reverseOrder={false} />
Expand All @@ -131,45 +112,21 @@ const MeetingPage = () => {
</header>
<main className="flex flex-1 overflow-hidden">
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 p-4">
<VideoElement stream={rtcHandler.current.localStream} muted={true} peerName="You" />
{rtcHandler.current.localStream && (
<VideoElement stream={rtcHandler.current.localStream} muted={true} peerName="You" />
)}
{Object.entries(peers).map(([peerUsername, peer]) => (
peerUsername !== username && (
<VideoElement
key={peerUsername}
stream={peer.stream}
muted={false}
<VideoElement
key={peerUsername}
stream={peer.stream}
muted={false}
peerName={peerUsername}
/>
)
))}
</div>
<div className="w-1/4 bg-white p-4 flex flex-col">
<h2 className="text-xl mb-4">Chat</h2>
<div className="flex-1 overflow-y-auto mb-4">
{chatHistory.map((msg, index) => (
<div key={index} className="mb-2 flex items-center">
<img
src={getProfilePicture(msg.sender)}
alt={`${msg.sender}'s avatar`}
className="w-8 h-8 rounded-full mr-2"
/>
<div>
<strong>{msg.sender}:</strong> {msg.text}
</div>
</div>
))}
</div>
<form onSubmit={sendMessage} className="flex">
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="flex-1 px-2 py-1 border rounded-l"
placeholder="Type a message..."
/>
<Button type="submit" className="rounded-l-none">Send</Button>
</form>
</div>
<Chat meeting_id={meeting_id} username={username} socket={socketRef.current} />
</main>
<footer className="bg-gray-200 p-4 flex justify-center space-x-4">
<Button onClick={toggleMute}>{isMuted ? 'Unmute' : 'Mute'}</Button>
Expand Down
1 change: 1 addition & 0 deletions client/src/services/chatHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ChatHandler {

setupSocketListeners() {
this.socket.on('chat_message', (data) => {
console.log("WebSocket event received:", data);
this.chatHistory.push(data);
if (this.setChatHistory) {
this.setChatHistory([...this.chatHistory]); // update UI
Expand Down

0 comments on commit 16b92ef

Please sign in to comment.