Use Case

/

التطوير السريع

إنشاء نماذج أولية وظيفية بسرعة

Model

كلود 3.7 سونيت

إنشاء نموذج أولي لتطبيق دردشة في الوقت الفعلي باستخدام React وNode.js وSocket.io — الغرف الأساسية والمراسلة ووجود المستخدم

Try Query
كلود 3.7 سونيت
/
مبرمج

النموذج الأولي لتطبيق الدردشة في الوقت الفعلي مع React وNode.js وSocket.io

سأوجهك من خلال إنشاء تطبيق دردشة أساسي في الوقت الفعلي مع دعم الغرفة والمراسلة ومؤشرات حضور المستخدم. دعونا نقسم هذا إلى مكونات الواجهة الأمامية والخلفية.

هيكل المشروع

1chat-app/
2├── client/             # React frontend
3│   ├── public/
4│   └── src/
5├── server/             # Node.js backend
6└── package.json

الواجهة الخلفية (Node.js + Socket.io)

1. قم بإعداد الخادم

أولاً، قم بإنشاء دليل الخادم وتهيئة:

1mkdir -p chat-app/server
2cd chat-app/server
3npm init -y
4npm install express socket.io cors

2. ابتكر server.js

1const express = require('express');
2const http = require('http');
3const socketIo = require('socket.io');
4const cors = require('cors');
5
6const app = express();
7app.use(cors());
8
9const server = http.createServer(app);
10const io = socketIo(server, {
11  cors: {
12    origin: "http://localhost:3000",
13    methods: ["GET", "POST"]
14  }
15});
16
17// Track users in each room
18const rooms = {};
19
20io.on('connection', (socket) => {
21  console.log('New client connected:', socket.id);
22  
23  // User joins a room
24  socket.on('join_room', ({ username, room }) => {
25    socket.join(room);
26    
27    // Initialize room if it doesn't exist
28    if (!rooms[room]) {
29      rooms[room] = [];
30    }
31    
32    // Add user to room
33    rooms[room].push({ id: socket.id, username });
34    
35    // Broadcast to room that user has joined
36    io.to(room).emit('user_joined', { 
37      username, 
38      users: rooms[room],
39      message: `${username} has joined the room.` 
40    });
41    
42    // Send room history to the new user
43    socket.emit('room_users', rooms[room]);
44    
45    console.log(`${username} joined room: ${room}`);
46  });
47  
48  // Handle messages
49  socket.on('send_message', ({ room, username, message }) => {
50    const timestamp = new Date().toISOString();
51    io.to(room).emit('receive_message', { 
52      username, 
53      message, 
54      timestamp 
55    });
56  });
57  
58  // Handle user typing
59  socket.on('typing', ({ room, username }) => {
60    socket.to(room).emit('user_typing', { username });
61  });
62  
63  // Handle user disconnect
64  socket.on('disconnect', () => {
65    console.log('Client disconnected:', socket.id);
66    
67    // Find which room the user was in
68    for (const room in rooms) {
69      const userIndex = rooms[room].findIndex(user => user.id === socket.id);
70      
71      if (userIndex !== -1) {
72        const username = rooms[room][userIndex].username;
73        
74        // Remove user from room
75        rooms[room].splice(userIndex, 1);
76        
77        // Notify room that user has left
78        io.to(room).emit('user_left', { 
79          username, 
80          users: rooms[room],
81          message: `${username} has left the room.` 
82        });
83        
84        break;
85      }
86    }
87  });
88});
89
90const PORT = process.env.PORT || 4000;
91server.listen(PORT, () => {
92  console.log(`Server running on port ${PORT}`);
93});

الواجهة الأمامية (عميل React + Socket.io)

1. قم بإعداد تطبيق React

1npx create-react-app chat-app/client
2cd chat-app/client
3npm install socket.io-client

2. إنشاء مكونات

src/App.js

1import React, { useState } from 'react';
2import './App.css';
3import JoinRoom from './components/JoinRoom';
4import ChatRoom from './components/ChatRoom';
5
6function App() {
7  const [joined, setJoined] = useState(false);
8  const [userData, setUserData] = useState({ username: '', room: '' });
9
10  const handleJoinRoom = (username, room) => {
11    setUserData({ username, room });
12    setJoined(true);
13  };
14
15  return (
16    <div className="App">
17      <header className="App-header">
18        <h1>Real-Time Chat App</h1>
19      </header>
20      <main>
21        {!joined ? (
22          <JoinRoom onJoin={handleJoinRoom} />
23        ) : (
24          <ChatRoom username={userData.username} room={userData.room} />
25        )}
26      </main>
27    </div>
28  );
29}
30
31export default App;

src/components/JoinRoom.js

1import React, { useState } from 'react';
2
3function JoinRoom({ onJoin }) {
4  const [username, setUsername] = useState('');
5  const [room, setRoom] = useState('');
6
7  const handleSubmit = (e) => {
8    e.preventDefault();
9    if (username && room) {
10      onJoin(username, room);
11    }
12  };
13
14  return (
15    <div className="join-container">
16      <h2>Join a Chat Room</h2>
17      <form onSubmit={handleSubmit}>
18        <div className="form-group">
19          <label htmlFor="username">Username:</label>
20          <input
21            type="text"
22            id="username"
23            value={username}
24            onChange={(e) => setUsername(e.target.value)}
25            required
26          />
27        </div>
28        <div className="form-group">
29          <label htmlFor="room">Room:</label>
30          <input
31            type="text"
32            id="room"
33            value={room}
34            onChange={(e) => setRoom(e.target.value)}
35            required
36          />
37        </div>
38        <button type="submit">Join Room</button>
39      </form>
40    </div>
41  );
42}
43
44export default JoinRoom;

src/components/ChatRoom.js

1import React, { useState, useEffect, useRef } from 'react';
2import io from 'socket.io-client';
3
4const ENDPOINT = 'http://localhost:4000';
5
6function ChatRoom({ username, room }) {
7  const [messages, setMessages] = useState([]);
8  const [message, setMessage] = useState('');
9  const [users, setUsers] = useState([]);
10  const [typingUsers, setTypingUsers] = useState([]);
11  const socketRef = useRef();
12  const messagesEndRef = useRef();
13
14  // Initialize socket connection
15  useEffect(() => {
16    socketRef.current = io(ENDPOINT);
17    
18    // Join room
19    socketRef.current.emit('join_room', { username, room });
20    
21    // Listen for messages
22    socketRef.current.on('receive_message', (data) => {
23      setMessages((prevMessages) => [...prevMessages, data]);
24    });
25    
26    // Listen for user joined
27    socketRef.current.on('user_joined', (data) => {
28      setUsers(data.users);
29      setMessages((prevMessages) => [
30        ...prevMessages, 
31        { system: true, message: data.message, timestamp: new Date().toISOString() }
32      ]);
33    });
34    
35    // Listen for user left
36    socketRef.current.on('user_left', (data) => {
37      setUsers(data.users);
38      setMessages((prevMessages) => [
39        ...prevMessages, 
40        { system: true, message: data.message, timestamp: new Date().toISOString() }
41      ]);
42    });
43    
44    // Get current users in room
45    socketRef.current.on('room_users', (data) => {
46      setUsers(data);
47    });
48    
49    // Listen for typing events
50    socketRef.current.on('user_typing', (data) => {
51      setTypingUsers((prevTypers) => {
52        if (!prevTypers.includes(data.username)) {
53          return [...prevTypers, data.username];
54        }
55        return prevTypers;
56      });
57      
58      // Remove typing indicator after 2 seconds
59      setTimeout(() => {
60        setTypingUsers((prevTypers) => 
61          prevTypers.filter(user => user !== data.username)
62        );
63      }, 2000);
64    });
65    
66    // Cleanup on unmount
67    return () => {
68      socketRef.current.disconnect();
69    };
70  }, [username, room]);
71  
72  // Auto-scroll to bottom when new messages arrive
73  useEffect(() => {
74    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
75  }, [messages]);
76  
77  // Handle sending messages
78  const handleSendMessage = (e) => {
79    e.preventDefault();
80    if (message.trim()) {
81      socketRef.current.emit('send_message', {
82        room,
83        username,
84        message
85      });
86      setMessage('');
87    }
88  };
89  
90  // Handle typing indicator
91  const handleTyping = () => {
92    socketRef.current.emit('typing', { room, username });
93  };
94  
95  // Format timestamp
96  const formatTime = (timestamp) => {
97    return new Date(timestamp).toLocaleTimeString([], { 
98      hour: '2-digit', 
99      minute: '2-digit' 
100    });
101  };
102
103  return (
104    <div className="chat-container">
105      <div className="chat-header">
106        <h2>Room: {room}</h2>
107      </div>
108      
109      <div className="chat-main">
110        <div className="chat-sidebar">
111          <h3>Users in Room</h3>
112          <ul className="users-list">
113            {users.map((user, index) => (
114              <li key={index} className={user.username === username ? 'current-user' : ''}>
115                {user.username} {user.username === username ? '(You)' : ''}
116              </li>
117            ))}
118          </ul>
119        </div>
120        
121        <div className="chat-messages">
122          {messages.map((msg, index) => (
123            <div 
124              key={index} 
125              className={`message ${msg.system ? 'system-message' : 
126                (msg.username === username ? 'my-message' : 'other-message')}`}
127            >
128              {!msg.system && (
129                <div className="message-header">
130                  <span className="username">{msg.username}</span>
131                  <span className="timestamp">{formatTime(msg.timestamp)}</span>
132                </div>
133              )}
134              <p className="message-text">{msg.message}</p>
135            </div>
136          ))}
137          
138          {typingUsers.length > 0 && (
139            <div className="typing-indicator">
140              {typingUsers.join(', ')} {typingUsers.length === 1 ? 'is' : 'are'} typing...
141            </div>
142          )}
143          
144          <div ref={messagesEndRef} />
145        </div>
146      </div>
147      
148      <form className="message-form" onSubmit={handleSendMessage}>
149        <input
150          type="text"
151          value={message}
152          onChange={(e) => setMessage(e.target.value)}
153          onKeyPress={handleTyping}
154          placeholder="Type a message..."
155        />
156        <button type="submit">Send</button>
157      </form>
158    </div>
159  );
160}
161
162export default ChatRoom;

3. أضف بعض CSS الأساسية

src/App.css

1* {
2  box-sizing: border-box;
3  margin: 0;
4  padding: 0;
5}
6
7body {
8  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9  line-height: 1.6;
10  background-color: #f5f5f5;
11}
12
13.App {
14  max-width: 1200px;
15  margin: 0 auto;
16  padding: 1rem;
17}
18
19.App-header {
20  text-align: center;
21  margin-bottom: 2rem;
22  color: #333;
23}
24
25/* Join Room Styles */
26.join-container {
27  max-width: 500px;
28  margin: 0 auto;
29  background: white;
30  border-radius: 8px;
31  padding: 2rem;
32  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
33}
34
35.join-container h2 {
36  margin-bottom: 1.5rem;
37  text-align: center;
38}
39
40.form-group {
41  margin-bottom: 1rem;
42}
43
44.form-group label {
45  display: block;
46  margin-bottom: 0.5rem;
47  font-weight: 500;
48}
49
50.form-group input {
51  width: 100%;
52  padding: 0.75rem;
53  border: 1px solid #ddd;
54  border-radius: 4px;
55  font-size: 1rem;
56}
57
58button {
59  background-color: #4caf50;
60  color: white;
61  border: none;
62  padding: 0.75rem 1.5rem;
63  font-size: 1rem;
64  border-radius: 4px;
65  cursor: pointer;
66  transition: background-color 0.2s;
67}
68
69button:hover {
70  background-color: #45a049;
71}
72
73/* Chat Room Styles */
74.chat-container {
75  display: flex;
76  flex-direction: column;
77  height: 80vh;
78  background: white;
79  border-radius: 8px;
80  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
81}
82
83.chat-header {
84  padding: 1rem;
85  background: #4caf50;
86  color: white;
87  border-top-left-radius: 8px;
88  border-top-right-radius: 8px;
89}
90
91.chat-main {
92  display: flex;
93  flex: 1;
94  overflow: hidden;
95}
96
97.chat-sidebar {
98  width: 200px;
99  background: #f8f8f8;
100  padding: 1rem;
101  border-right: 1px solid #ddd;
102  overflow-y: auto;
103}
104
105.chat-sidebar h3 {
106  margin-bottom: 1rem;
107  font-size: 1.1rem;
108}
109
110.users-list {
111  list-style: none;
112}
113
114.users-list li {
115  padding: 0.5rem 0;
116  border-bottom: 1px solid #eee;
117}
118
119.current-user {
120  font-weight: bold;
121  color: #4caf50;
122}
123
124.chat-messages {
125  flex: 1;
126  padding: 1rem;
127  overflow-y: auto;
128}
129
130.message {
131  margin-bottom: 1rem;
132  padding: 0.75rem;
133  border-radius: 8px;
134  max-width: 80%;
135}
136
137.my-message {
138  background-color: #e3f2fd;
139  margin-left: auto;
140}
141
142.other-message {
143  background-color: #f1f1f1;
144}
145
146.system-message {
147  background-color: #fffde7;
148  text-align: center;
149  font-style: italic;
150  padding: 0.5rem;
151  margin: 0.5rem auto;
152  max-width: 90%;
153  color: #666;
154}
155
156.message-header {
157  display: flex;
158  justify-content: space-between;
159  margin-bottom: 0.5rem;
160  font-size: 0.85rem;
161}
162
163.username {
164  font-weight: bold;
165}
166
167.timestamp {
168  color: #777;
169}
170
171.typing-indicator {
172  font-style: italic;
173  color: #777;
174  padding: 0.5rem;
175}
176
177.message-form {
178  display: flex;
179  padding: 1rem;
180  border-top: 1px solid #ddd;
181}
182
183.message-form input {
184  flex: 1;
185  padding: 0.75rem;
186  border: 1px solid #ddd;
187  border-radius: 4px;
188  margin-right: 0.5rem;
189  font-size: 1rem;
190}

تشغيل التطبيق

1. بدء تشغيل الخادم:

1cd chat-app/server
2node server.js

2. ابدأ تشغيل عميل React:

1cd chat-app/client
2npm start

3. افتح المتصفح الخاص بك إلى http://localhost:3000

الميزات التي تم تنفيذها

  1. الدردشة القائمة على الغرفة:
    • يمكن للمستخدمين الانضمام إلى غرف دردشة محددة
    • يمكن أن توجد غرف متعددة في وقت واحد
  2. المراسلة في الوقت الفعلي:
    • يتم تسليم الرسائل على الفور لجميع المستخدمين في الغرفة
  3. حضور المستخدم:
    • يُظهر الأشخاص الموجودين حاليًا في الغرفة
    • إشعارات عند انضمام المستخدمين أو مغادرتهم
  4. مؤشرات الكتابة:
    • يظهر عندما يكتب شخص ما رسالة
  5. تنسيق الرسائل:
    • أنماط مختلفة لرسائلك مقابل الآخرين
    • رسائل النظام لأحداث الانضمام/المغادرة
    • الطوابع الزمنية على الرسائل

يوفر هذا النموذج الأولي أساسًا متينًا لتطبيق الدردشة في الوقت الفعلي. يمكنك توسيعه بميزات مثل:

  • مصادقة المستخدم
  • سجل الرسائل الدائم
  • المراسلة الخاصة
  • مشاركة الملفات
  • اقرأ الإيصالات
  • الرموز التعبيرية المخصصة/ردود الفعل

Explore Related Use Cases

Experience Ninja's AI Assistant

Try for free today. Plans starting at $19/month.