Skip to content

Commit

Permalink
Merge pull request #246 from shubhagarwal1/chatbox
Browse files Browse the repository at this point in the history
Revamp Chatbox | ADD Appealing and Interactive user Features
  • Loading branch information
Anuj3553 authored Oct 25, 2024
2 parents efef0cb + 9c6751b commit 18f22f5
Showing 1 changed file with 213 additions and 134 deletions.
347 changes: 213 additions & 134 deletions client/src/component/Discussion.jsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,234 @@
import { useEffect, useState } from "react";
import PropTypes from "prop-types";
import "../css/Discussion.css";
import { io } from "socket.io-client";
// AUDIO
import recieveMsg from "../assets/music/recieveMsg.mp3";
import userJoin from "../assets/music/userJoin.mp3";
import userLeft from "../assets/music/userLeft.mp3";
import InputModal from "./InputModal";
import React, { useEffect, useState, useRef } from 'react';
import { MessageCircle, Users, Send, Smile, Image, Paperclip, Moon, Sun, ChevronDown } from 'lucide-react';

// Create a Socket
const socket = io("https://bitbox-uxbo.onrender.com", { transports: ["websocket"] });
const Discussion = ({ mode }) => {
const [messages, setMessages] = useState([]);
const [messageInput, setMessageInput] = useState('');
const [userName, setUserName] = useState('');
const [showModal, setShowModal] = useState(true);
const [onlineUsers, setOnlineUsers] = useState([
{ id: 1, name: 'Alice', status: 'active' },
{ id: 2, name: 'Bob', status: 'idle' },
{ id: 3, name: 'Charlie', status: 'active' }
]);
const [showEmoji, setShowEmoji] = useState(false);
const [isDarkMode, setIsDarkMode] = useState(false);
const [showUserList, setShowUserList] = useState(false);
const messagesEndRef = useRef(null);

const Discussion = (props) => {
const [messages, setMessages] = useState([]); // State to store chat messages
const [messageInput, setMessageInput] = useState(""); // State to store user input message
const [userName, setUserName] = useState(""); // State to store user's name
const [isModalOpen, setIsModalOpen] = useState(false);
const emojis = ['😊', '😂', '❤️', '👍', '🎉', '🔥', '✨', '🌟'];

useEffect(() => {
setIsModalOpen(true);

socket.on("user-joined", (name) => {
// Update messages state with the new user's joining message
setMessages((prevMessages) => [
...prevMessages,
{ content: `${name} joined the chat`, position: "center1" },
]);
// Play audio when a user joins
playAudio(userJoin);
});

// Event listener for receiving a message from the server
socket.on("receive", (data) => {
// Update messages state with the received message
setMessages((prevMessages) => [
...prevMessages,
{ content: `${data.name}: ${data.message}`, position: "left" },
]);
// Play audio when receiving a message
playAudio(recieveMsg);
});

// Event listener for a user leaving the chat
socket.on("left", (name) => {
// Update messages state with the user's leaving message
setMessages((prevMessages) => [
...prevMessages,
{ content: `${name} left the chat`, position: "center2" },
]);
// Play audio when a user leaves
playAudio(userLeft);
});

// Clean up socket listeners when component unmounts
return () => {
socket.off("user-joined");
socket.off("receive");
socket.off("left");
};
}, []);

// Function to handle audio playback
const playAudio = (audio) => {
const audioElement = new Audio(audio); // Create new Audio object
audioElement.play(); // Play the audio
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};

// Function to handle form submission
useEffect(() => {
scrollToBottom();
}, [messages]);

const handleSubmit = (e) => {
e.preventDefault();
const message = messageInput.trim();
if (message !== "") {
// Append your own message immediately to avoid waiting for the server
setMessages((prevMessages) => [
...prevMessages,
{ content: `You: ${message}`, position: "right" },
]);
socket.emit("send", message); // Emit 'send' event to the server
setMessageInput(""); // Clear the message input field
if (message) {
setMessages(prev => [...prev, {
id: Date.now(),
content: message,
position: 'right',
sender: 'You',
timestamp: new Date(),
status: 'sent'
}]);
setMessageInput('');
}
};
const handleJoin = (name) => {
setUserName(name);
socket.emit("join", name); // Notify the server that a user has joined
setIsModalOpen(false); // Close the modal after a successful join

const handleEmojiClick = (emoji) => {
setMessageInput(prev => prev + emoji);
setShowEmoji(false);
};
return (
<div className="discussion-main-container mt-20 mb-[50px]">
<InputModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSubmit={handleJoin}
/>

<div className="discussion-container-section flex items-center h-full justify-center flex-col w-full">
{/* Container for displaying chat messages */}
<div
className="discussion-container"
style={{ color: props.mode === "dark" ? "black" : "" }}
>
<div className="welcome-center fs-3 mt-3">{`Welcome ${userName} to the Bitbox Community`}</div>
{messages.map((message, index) => (
<div key={index} className={`message ${message.position}`}>
{message.content}
const handleJoin = (e) => {
e.preventDefault();
const name = e.target.username.value.trim();
if (name) {
setUserName(name);
setShowModal(false);
}
};

return (
<div className={`min-h-screen ${isDarkMode ? 'bg-gray-900' : 'bg-gray-50'} flex items-center justify-center p-4 transition-colors duration-300`}>
{/* Main Chat Container */}
<div className={`w-full max-w-5xl ${isDarkMode ? 'bg-gray-800' : 'bg-white'} rounded-xl shadow-2xl overflow-hidden transition-colors duration-300`}>
{/* Chat Header */}
<div className={`${isDarkMode ? 'bg-indigo-900' : 'bg-indigo-600'} p-4 transition-colors duration-300`}>
<div className="flex justify-between items-center">
<div className="flex items-center space-x-4">
<MessageCircle className="text-white" size={24} />
<div>
<h2 className="text-xl font-bold text-white">Bitbox Community</h2>
<p className="text-indigo-200 text-sm">Where ideas come alive</p>
</div>
</div>
<div className="flex items-center space-x-4">
<button
onClick={() => setShowUserList(!showUserList)}
className="flex items-center space-x-2 bg-indigo-500 hover:bg-indigo-400 px-3 py-1 rounded-full text-white text-sm transition-colors"
>
<Users size={16} />
<span>{onlineUsers.length} online</span>
</button>
<button
onClick={() => setIsDarkMode(!isDarkMode)}
className="p-2 rounded-full hover:bg-indigo-500 transition-colors"
>
{isDarkMode ? <Sun className="text-white" size={20} /> : <Moon className="text-white" size={20} />}
</button>
</div>
))}
</div>
</div>

{/* Form for sending messages */}
<div className="discussion-send pb-[20px] pt-10 w-full">
<form
id="discussion-send-container pb-[20px]"
className="w-full items-center flex justify-center "
onSubmit={handleSubmit}
>
<input
style={{ color: props.mode === "dark" ? "black" : "" }}
type="text"
name="messageImp"
id="messageInp"
placeholder="Type a message"
className="h-"
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
/>
<button
className="discussion-btn"
type="submit"
style={{ color: props.mode === "dark" ? "black" : "" }}
>
Send
</button>
</form>
<div className="flex h-[600px]">
{/* Users Sidebar - Slides in/out */}
<div className={`w-64 border-r ${isDarkMode ? 'bg-gray-800 border-gray-700' : 'bg-gray-50 border-gray-200'}
transform transition-transform duration-300 ${showUserList ? 'translate-x-0' : '-translate-x-full'} absolute lg:relative lg:translate-x-0`}>
<div className="p-4">
<h3 className={`font-semibold mb-4 ${isDarkMode ? 'text-white' : 'text-gray-800'}`}>Online Users</h3>
<div className="space-y-2">
{onlineUsers.map(user => (
<div key={user.id} className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${user.status === 'active' ? 'bg-green-400' : 'bg-yellow-400'}`} />
<span className={`text-sm ${isDarkMode ? 'text-gray-300' : 'text-gray-600'}`}>{user.name}</span>
</div>
))}
</div>
</div>
</div>

{/* Messages Container */}
<div className="flex-1 flex flex-col">
<div className={`flex-1 overflow-y-auto p-4 space-y-4 ${isDarkMode ? 'bg-gray-800' : 'bg-gray-50'}`}>
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.position === 'right' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[70%] rounded-2xl p-4 ${
message.position === 'right'
? `${isDarkMode ? 'bg-indigo-900' : 'bg-indigo-600'} text-white`
: `${isDarkMode ? 'bg-gray-700' : 'bg-gray-200'} ${isDarkMode ? 'text-white' : 'text-gray-800'}`
} transform hover:scale-[1.02] transition-transform duration-200`}
>
<div className="flex items-center space-x-2 mb-1">
<span className="font-medium text-sm">{message.sender}</span>
<span className="text-xs opacity-75">
{new Date(message.timestamp).toLocaleTimeString()}
</span>
</div>
<p className="text-sm leading-relaxed">{message.content}</p>
{message.position === 'right' && (
<div className="text-right mt-1">
<span className="text-xs opacity-75">✓✓</span>
</div>
)}
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>

{/* Message Input */}
<div className={`p-4 border-t ${isDarkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
<form onSubmit={handleSubmit} className="flex items-center space-x-4">
<div className="flex-1 relative">
<input
type="text"
value={messageInput}
onChange={(e) => setMessageInput(e.target.value)}
placeholder="Type your message..."
className={`w-full px-4 py-3 pr-12 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500
${isDarkMode ? 'bg-gray-700 text-white border-gray-600' : 'bg-gray-50 text-gray-800 border-gray-300'}
border transition-colors`}
/>
<button
type="button"
onClick={() => setShowEmoji(!showEmoji)}
className="absolute right-3 top-1/2 -translate-y-1/2"
>
<Smile className={`${isDarkMode ? 'text-gray-400' : 'text-gray-500'} hover:text-indigo-500 transition-colors`} size={20} />
</button>
</div>
<button
type="button"
className={`p-3 rounded-lg ${isDarkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'} transition-colors`}
>
<Paperclip className={`${isDarkMode ? 'text-gray-400' : 'text-gray-500'} hover:text-indigo-500 transition-colors`} size={20} />
</button>
<button
type="submit"
className="p-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors"
>
<Send size={20} />
</button>
</form>

{/* Emoji Picker */}
{showEmoji && (
<div className={`absolute bottom-20 right-4 p-3 grid grid-cols-4 gap-2 rounded-lg shadow-lg
${isDarkMode ? 'bg-gray-700' : 'bg-white'} border ${isDarkMode ? 'border-gray-600' : 'border-gray-200'}`}>
{emojis.map((emoji) => (
<button
key={emoji}
onClick={() => handleEmojiClick(emoji)}
className="text-2xl hover:scale-125 transition-transform"
>
{emoji}
</button>
))}
</div>
)}
</div>
</div>
</div>
</div>

{/* Join Modal */}
{showModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className={`${isDarkMode ? 'bg-gray-800' : 'bg-white'} rounded-lg p-6 w-full max-w-md transform transition-all duration-300 scale-100`}>
<h3 className={`text-2xl font-bold mb-6 ${isDarkMode ? 'text-white' : 'text-gray-800'}`}>
Join the Discussion
</h3>
<form onSubmit={handleJoin} className="space-y-4">
<div>
<label className={`block text-sm font-medium mb-2 ${isDarkMode ? 'text-gray-300' : 'text-gray-700'}`}>
Choose a username
</label>
<input
type="text"
name="username"
placeholder="Enter your username"
className={`w-full px-4 py-3 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500
${isDarkMode ? 'bg-gray-700 text-white border-gray-600' : 'bg-gray-50 text-gray-800 border-gray-300'}
border transition-colors`}
required
/>
</div>
<button
type="submit"
className="w-full px-4 py-3 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2
transition-colors transform hover:scale-[1.02] duration-200"
>
Join Discussion
</button>
</form>
</div>
</div>
)}
</div>
);
};

// Props Validation
Discussion.propTypes = {
title: PropTypes.string,
home: PropTypes.string,
community: PropTypes.string,
discussion: PropTypes.string,
myProjects: PropTypes.string,
about: PropTypes.string,
mode: PropTypes.string,
toggleMode: PropTypes.func,
showAlert: PropTypes.func,
isAuthenticated: PropTypes.bool,
};

export default Discussion;
export default Discussion;

0 comments on commit 18f22f5

Please sign in to comment.