Chat feature in virtual gift store
Project Overview:
Giftly is a virtual gift store management platform where users can communicate with the admin in real-time using a built-in chat feature. This feature enables seamless interaction between normal users and the admin, making the customer support process faster and more interactive. The chat functionality is built using Node.js, React.js, Mongoose, Socket.IO, and Express.js.
Key Features of the Chat System
- - Real-time Messaging: Communication happens instantly between users and the admin using Socket.IO.
- - User Roles:
- - Admin: Can view and respond to all users.
- - Normal users: Can only chat with the admin.
- - Persistent Chat History: Chat messages are stored in MongoDB and retrieved upon reconnecting.
- - Notifications: Real-time toast notifications for new messages.
Technologies Used
- Backend: Node.js, Express.js, Mongoose (MongoDB)
- Frontend: React.js, CSS
- WebSocket: Socket.IO for real-time communication
- Database: MongoDB (Atlas)
System Architecture
Frontend (React.js)
Main components for this feature include:
- ChatContainer: Manages the chat interface, handling authentication and message rendering.
- ChatList: Displays messages based on whether the user is the sender or receiver.
- Socket.IO Client: Initializes the connection and handles real-time updates.
- Backend (Node.js, Express.js)
- Routes: Handles HTTP requests and interacts with the MongoDB database.
- Socket.IO Server: Manages chat rooms and message broadcasting.
- Mongoose Models: Defines the schema for storing chat data.
Benefits of Using Socket.IO
- Real-Time Communication: Socket.IO allows instant message delivery without delays, creating a seamless chat experience.
- Cross-Browser Support: It provides real-time connections that work across different browsers and platforms.
- Automatic Reconnection: In case of network disruptions, Socket.IO automatically reconnects the user without losing the chat session.
- Broadcasting to Rooms: Messages are broadcasted only to users in specific chat rooms, optimizing the flow of communication.
Backend Code - Mongoose Schema Definition
This schema captures sender and receiver details, message content, profile images, and timestamps.
const mongoose = require('mongoose');
const chatSchema = new mongoose.Schema({
senderUsername: String,
receiverUsername: String,
message: String,
profileImage: String,
timestamp: { type: Date, default: Date.now }
});
const Chat = mongoose.model('chat-message', chatSchema);
module.exports = Chat;
Backend Setup (Express.js & Socket.IO)
The backend is configured to handle real-time message exchanges and store chat data in MongoDB.
const express = require("express");
const cors = require("cors");
require("dotenv").config();
const mongoose = require("mongoose");
const http = require("http");
const { Server } = require("socket.io");
const Chat = require("./model/chatModel");
const app = express();
const server = http.createServer(app);
// Middleware
app.use(cors({ origin: process.env.CLIENT_URL, credentials: true }));
app.use(express.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGO_URI, { dbName: 'Giftly-server-db' })
.then(() => console.log("Giftly DB connected"))
.catch((err) => console.log(err));
// Socket.IO Setup
const io = new Server(server, { cors: { origin: process.env.CLIENT_URL } });
io.on("connection", (socket) => {
console.log("User connected");
socket.on('joinRoom', ({ sender, receiver }) => {
const room = [sender, receiver].sort().join('-');
socket.join(room);
loadMessages(socket, sender, receiver); // Load previous chats
});
socket.on('newMessage', async (msg) => {
const newMessage = new Chat(msg);
await newMessage.save();
io.to([msg.senderUsername, msg.receiverUsername].sort().join('-')).emit('message', msg);
});
socket.on("disconnect", () => {
console.log("User disconnected");
});
});
server.listen(process.env.PORT, () => {
console.log(`Giftly server is running on port ${process.env.PORT}`);
});
Frontend (React.js) - ChatContainer Component
The ChatContainer component renders the chat interface and manages sending/receiving messages via Socket.IO.
import React, { useContext, useEffect, useState } from "react";
import socketIOClient from "socket.io-client";
import ChatLists from './ChatList';
import './style.css';
import toast from "react-hot-toast";
import { AuthContext } from "../../Provider/AuthProvider";
const socket = socketIOClient(process.env.REACT_APP_SERVER_URL, { autoConnect: false });
const ChatContainer = () => {
const { user } = useContext(AuthContext);
const [text, setText] = useState('');
const [userInfo, setUserInfo] = useState(() => JSON.parse(localStorage.getItem("userInfo")));
const [receiver, setReceiver] = useState(() => localStorage.getItem("receiver"));
const [chats, setChats] = useState([]);
// Fetch previous chats
const fetchPreviousChats = async (sender, receiver) => {
const response = await fetch(`${process.env.REACT_APP_SERVER_URL}/chat/getChats?sender=${sender}&receiver=${receiver}`);
const previousChats = await response.json();
setChats(previousChats);
};
// Initialize Socket
useEffect(() => {
socket.connect();
if (userInfo && receiver) {
socket.emit("joinRoom", { sender: userInfo.userName, receiver });
}
socket.on("message", (msg) => setChats(prev => [...prev, msg]));
return () => socket.disconnect();
}, [userInfo, receiver]);
const handleSend = () => {
if (text.trim()) {
const newChat = { senderUsername: userInfo.userName, receiverUsername: receiver, message: text };
socket.emit("newMessage", newChat);
setText('');
}
};
return (
<div className="chat-container">
<div className="chat-header">Chat with {receiver}</div>
<ChatLists chats={chats} />
<div className="chat-input">
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={handleSend}>Send</button>
</div>
</div>
);
};
export default ChatContainer;
Notification Handling in Frontend
Using react-hot-toast for real-time notifications when a new message arrives.
socket.on("notification", (notification) => {
if (notification.receiver === userInfo.userName) {
toast.success(notification.message, { duration: 6000, position: 'top-center' });
}
});
Socket.IO Methods Explained
- io.on('connection'): Establishes a WebSocket connection when a user connects.
- socket.emit('joinRoom'): Joins a chat room created between the sender and receiver.
- socket.on('newMessage'): Receives a new message from the client and stores it in the database.
- io.to(room).emit('message'): Broadcasts the message to users in a specific chat room.
- socket.on('disconnect'): Handles user disconnection and cleanup.
Future Enhancements
- File Sharing: Allow users to send images and files.
- Typing Indicators: Display real-time typing status for a better interaction experience.
- Push Notifications: Integrate push notifications to alert users when offline.
Setup Instructions
Backend Setup
Clone the repository:
git clone https://github.com/your-repo-url/giftly-chat-app.git
cd giftly-chat-app/server
npm install
Create a .env file in the server directory:
MONGO_URI=mongodb+srv://<username>:<password>@cluster0.mongodb.net/giftly-server-db
PORT=<your-server-port>
CLIENT_URL=http://localhost:5173
*Run the backend server:
*
npm run start
Frontend Setup (React.js)
Navigate to the client folder:
cd ../client
npm install
npm run dev
Create a .env file in the client folder:
REACT_APP_SERVER_URL=http://localhost:<your-server-port>
Final Thoughts
The Giftly chat feature is a scalable real-time chat solution, enabling instant communication between users and admins. Socket.IO simplifies the WebSocket management, ensuring smooth and efficient message exchanges with real-time notifications. Future enhancements will bring more interaction to users, including file sharing, typing indicators, and offline notifications.
Happy coding! 🚀
Top comments (0)