samirerty commited on
Commit
5074056
·
verified ·
1 Parent(s): c9c664a

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +1317 -19
index.html CHANGED
@@ -1,19 +1,1317 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The user wants to convert the provided HTML/CSS/JavaScript chat room demo into a real working application using HTML, CSS, PHP, and a database (implied by "real" and "host").
2
+
3
+ Key requirements:
4
+ 1. Convert the frontend demo to a real backend application
5
+ 2. Use PHP for backend
6
+ 3. Use MySQL/MariaDB for database (implied by "real")
7
+ 4. Keep the Persian (Farsi) language and RTL layout
8
+ 5. Maintain the modern UI design from the original code
9
+ 6. Make it functional (real login/register, real chat messages stored in DB)
10
+ 7. Should be deployable on a web host
11
+
12
+ Since I need to provide a single HTML file as per system instructions, but the user is asking for PHP/MySQL backend, I need to provide:
13
+ 1. The complete PHP code structure
14
+ 2. SQL schema
15
+ 3. Instructions on how to set it up
16
+ 4. But since I can only provide one file, I should provide a comprehensive solution that includes all PHP files concatenated or clearly separated with comments, plus the SQL setup.
17
+
18
+ Wait, the system says "ALWAYS GIVE THE RESPONSE INTO A SINGLE HTML FILE" but the user is asking for PHP. This is a conflict. However, looking at the instructions carefully: "Generate complete, working HTML code that can be run immediately."
19
+
20
+ But PHP cannot run in a single HTML file in the browser. I need to interpret this as: provide the complete source code for the application, which happens to be PHP files. Since I must output a single file, I'll provide a comprehensive solution with clear file separators (like comments indicating different files) containing all the PHP, SQL, and frontend code needed.
21
+
22
+ Actually, looking at the system prompt again: "Generate complete, working HTML code that can be run immediately." This suggests the user wants code that works immediately. But PHP requires a server.
23
+
24
+ Given the constraints, I should:
25
+ 1. Provide the complete PHP application code with multiple files indicated
26
+ 2. Provide the SQL schema
27
+ 3. Provide instructions
28
+ 4. But keep it as one text block/file with clear separators
29
+
30
+ Or, I could create a single-file PHP solution that handles everything (routing, database setup, frontend) in one index.php file. This would technically be a single file that can run on a PHP host.
31
+
32
+ Yes, that's the best approach for this constraint. I'll create a single PHP file (index.php) that:
33
+ - Sets up the database on first run
34
+ - Handles all backend logic (auth, chat CRUD)
35
+ - Serves the frontend HTML/CSS/JS
36
+ - Uses AJAX/Fetch for real-time-like updates (polling)
37
+
38
+ Features to implement:
39
+ 1. User registration/login with PHP sessions
40
+ 2. MySQL database for users, rooms, messages
41
+ 3. Real chat functionality (messages stored in DB)
42
+ 4. Multiple rooms support
43
+ 5. Online status tracking
44
+ 6. Responsive design (maintain the original beautiful UI)
45
+ 7. Security: Prepared statements, password hashing, XSS protection
46
+
47
+ Database schema:
48
+ - users (id, username, phone, password, avatar, status, last_active)
49
+ - rooms (id, name, type, created_by)
50
+ - messages (id, room_id, user_id, message, created_at)
51
+ - room_members (id, room_id, user_id, joined_at)
52
+
53
+ Since it's one file, I'll use a "setup mode" - if tables don't exist, create them.
54
+
55
+ For real-time feel without WebSockets (which require separate server), I'll use polling every 3 seconds.
56
+
57
+ Structure of the single PHP file:
58
+ 1. PHP Backend Logic (session start, db connection, request handlers)
59
+ 2. Database setup (create tables if not exist)
60
+ 3. API endpoints (handled via GET/POST parameters)
61
+ 4. HTML/CSS/JS Frontend (the beautiful UI from original, adapted for real backend)
62
+
63
+ Security considerations:
64
+ - Use PDO with prepared statements
65
+ - Password_hash() for passwords
66
+ - htmlspecialchars() for output
67
+ - CSRF tokens
68
+ - Input validation
69
+
70
+ Let's build this step by step:
71
+
72
+ Database connection config (at top):
73
+ - Define DB credentials (user will change these)
74
+ - Try to connect, if fail show setup instructions
75
+
76
+ Routing:
77
+ - If $_GET['action'] exists, handle API call
78
+ - Else show the app interface
79
+
80
+ API Endpoints:
81
+ - login
82
+ - register
83
+ - get_messages
84
+ - send_message
85
+ - get_rooms
86
+ - create_room
87
+ - logout
88
+
89
+ Frontend:
90
+ - Same beautiful Persian UI as original
91
+ - But now AJAX calls to the same file with ?action=...
92
+ - Poll for new messages
93
+ - Show real user data from session
94
+
95
+ I'll maintain the exact styling from the user's provided code but adapt the JavaScript to work with the PHP backend.
96
+
97
+ Since it's one file, I need to be careful about output buffering and headers.
98
+
99
+ Let me write the code:
100
+
101
+ File: index.php (all-in-one)
102
+
103
+ Sections:
104
+ 1. PHP Logic (top)
105
+ - Session start
106
+ - DB config
107
+ - Helper functions
108
+ - Request routing
109
+
110
+ 2. Database Setup
111
+ - Create tables if not exist
112
+
113
+ 3. API Handlers
114
+ - JSON responses for AJAX
115
+
116
+ 4. HTML Output (if not API call)
117
+ - Same structure as original but with PHP session integration
118
+
119
+ The UI should look almost identical to what the user provided, but functional.
120
+
121
+ Key adaptations:
122
+ - Login form submits to ?action=login via fetch
123
+ - Register form submits to ?action=register
124
+ - Messages load from ?action=get_messages
125
+ - Send message to ?action=send_message
126
+ - Auto-refresh messages every 2-3 seconds
127
+
128
+ I'll include the "Built with anycoder" link as required.
129
+
130
+ Let's code this comprehensive solution. </think> Here is the complete **real chat application** in a single PHP file (`index.php`). This includes the database setup, backend API, and the beautiful Persian UI you provided.
131
+
132
+ **Instructions:**
133
+ 1. Save this as `index.php`
134
+ 2. Update the database credentials at the top (host, dbname, user, pass)
135
+ 3. Upload to your PHP host (PHP 7.4+ with MySQL/MariaDB)
136
+ 4. Visit the page - it will auto-create the database tables on first run
137
+
138
+ ```php
139
+ <?php
140
+ // ============================================
141
+ // چت روم واقعی - Real Chat Room
142
+ // Built with anycoder: https://huggingface.co/spaces/akhaliq/anycoder
143
+ // ============================================
144
+
145
+ session_start();
146
+
147
+ // Database Configuration - تنظیمات دیتابیس
148
+ define('DB_HOST', 'localhost');
149
+ define('DB_NAME', 'chatroom_db');
150
+ define('DB_USER', 'root');
151
+ define('DB_PASS', '');
152
+
153
+ // Connect to Database
154
+ try {
155
+ $pdo = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=utf8mb4", DB_USER, DB_PASS);
156
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
157
+ $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
158
+ } catch(PDOException $e) {
159
+ die("Connection failed: " . $e->getMessage());
160
+ }
161
+
162
+ // Initialize Database Tables (Auto-setup on first run)
163
+ function initDB($pdo) {
164
+ $pdo->exec("CREATE TABLE IF NOT EXISTS users (
165
+ id INT AUTO_INCREMENT PRIMARY KEY,
166
+ username VARCHAR(100) NOT NULL,
167
+ phone VARCHAR(20) UNIQUE NOT NULL,
168
+ password VARCHAR(255) NOT NULL,
169
+ avatar VARCHAR(10) DEFAULT '👤',
170
+ status ENUM('online', 'offline') DEFAULT 'offline',
171
+ last_active TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
172
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
173
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_persian_ci");
174
+
175
+ $pdo->exec("CREATE TABLE IF NOT EXISTS rooms (
176
+ id INT AUTO_INCREMENT PRIMARY KEY,
177
+ name VARCHAR(100) NOT NULL,
178
+ type ENUM('public', 'private') DEFAULT 'public',
179
+ created_by INT,
180
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
181
+ FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
182
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_persian_ci");
183
+
184
+ $pdo->exec("CREATE TABLE IF NOT EXISTS messages (
185
+ id INT AUTO_INCREMENT PRIMARY KEY,
186
+ room_id INT NOT NULL,
187
+ user_id INT NOT NULL,
188
+ message TEXT NOT NULL,
189
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
190
+ FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE,
191
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
192
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_persian_ci");
193
+
194
+ $pdo->exec("CREATE TABLE IF NOT EXISTS room_members (
195
+ id INT AUTO_INCREMENT PRIMARY KEY,
196
+ room_id INT NOT NULL,
197
+ user_id INT NOT NULL,
198
+ joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
199
+ UNIQUE KEY unique_membership (room_id, user_id),
200
+ FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE,
201
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
202
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_persian_ci");
203
+
204
+ // Insert default public room if empty
205
+ $stmt = $pdo->query("SELECT COUNT(*) FROM rooms");
206
+ if ($stmt->fetchColumn() == 0) {
207
+ $pdo->prepare("INSERT INTO rooms (name, type) VALUES (?, ?)")
208
+ ->execute(['عمومی', 'public']);
209
+ $pdo->prepare("INSERT INTO rooms (name, type) VALUES (?, ?)")
210
+ ->execute(['خصوصی', 'private']);
211
+ }
212
+ }
213
+
214
+ // Initialize on first run
215
+ initDB($pdo);
216
+
217
+ // API Handler
218
+ if (isset($_GET['action'])) {
219
+ header('Content-Type: application/json; charset=utf-8');
220
+ $action = $_GET['action'];
221
+
222
+ switch($action) {
223
+ case 'register':
224
+ handleRegister($pdo);
225
+ break;
226
+ case 'login':
227
+ handleLogin($pdo);
228
+ break;
229
+ case 'logout':
230
+ handleLogout();
231
+ break;
232
+ case 'get_rooms':
233
+ getRooms($pdo);
234
+ break;
235
+ case 'get_messages':
236
+ getMessages($pdo);
237
+ break;
238
+ case 'send_message':
239
+ sendMessage($pdo);
240
+ break;
241
+ case 'create_room':
242
+ createRoom($pdo);
243
+ break;
244
+ case 'get_online_users':
245
+ getOnlineUsers($pdo);
246
+ break;
247
+ }
248
+ exit;
249
+ }
250
+
251
+ // Backend Functions
252
+ function handleRegister($pdo) {
253
+ $data = json_decode(file_get_contents('php://input'), true);
254
+ $username = trim($data['username'] ?? '');
255
+ $phone = trim($data['phone'] ?? '');
256
+ $password = $data['password'] ?? '';
257
+
258
+ if (empty($username) || empty($phone) || empty($password)) {
259
+ echo json_encode(['success' => false, 'error' => 'تمام فیلدها الزامی است']);
260
+ return;
261
+ }
262
+
263
+ if (!preg_match('/^09[0-9]{9}$/', $phone)) {
264
+ echo json_encode(['success' => false, 'error' => 'شماره موبایل نامعتبر است']);
265
+ return;
266
+ }
267
+
268
+ $hash = password_hash($password, PASSWORD_BCRYPT);
269
+ $avatar = mb_substr($username, 0, 1);
270
+
271
+ try {
272
+ $stmt = $pdo->prepare("INSERT INTO users (username, phone, password, avatar) VALUES (?, ?, ?, ?)");
273
+ $stmt->execute([$username, $phone, $hash, $avatar]);
274
+
275
+ $userId = $pdo->lastInsertId();
276
+
277
+ // Auto-join public rooms
278
+ $rooms = $pdo->query("SELECT id FROM rooms WHERE type = 'public'")->fetchAll();
279
+ $stmt = $pdo->prepare("INSERT INTO room_members (room_id, user_id) VALUES (?, ?)");
280
+ foreach ($rooms as $room) {
281
+ $stmt->execute([$room['id'], $userId]);
282
+ }
283
+
284
+ $_SESSION['user_id'] = $userId;
285
+ $_SESSION['username'] = $username;
286
+ $_SESSION['avatar'] = $avatar;
287
+
288
+ echo json_encode(['success' => true, 'user' => ['id' => $userId, 'username' => $username, 'avatar' => $avatar]]);
289
+ } catch (PDOException $e) {
290
+ if ($e->getCode() == 23000) {
291
+ echo json_encode(['success' => false, 'error' => 'این شماره موبایل قبلاً ثبت شده است']);
292
+ } else {
293
+ echo json_encode(['success' => false, 'error' => 'خطا در ثبت‌نام']);
294
+ }
295
+ }
296
+ }
297
+
298
+ function handleLogin($pdo) {
299
+ $data = json_decode(file_get_contents('php://input'), true);
300
+ $phone = trim($data['phone'] ?? '');
301
+ $password = $data['password'] ?? '';
302
+
303
+ $stmt = $pdo->prepare("SELECT * FROM users WHERE phone = ?");
304
+ $stmt->execute([$phone]);
305
+ $user = $stmt->fetch();
306
+
307
+ if ($user && password_verify($password, $user['password'])) {
308
+ $_SESSION['user_id'] = $user['id'];
309
+ $_SESSION['username'] = $user['username'];
310
+ $_SESSION['avatar'] = $user['avatar'];
311
+
312
+ // Update status
313
+ $pdo->prepare("UPDATE users SET status = 'online' WHERE id = ?")->execute([$user['id']]);
314
+
315
+ echo json_encode(['success' => true, 'user' => ['id' => $user['id'], 'username' => $user['username'], 'avatar' => $user['avatar']]]);
316
+ } else {
317
+ echo json_encode(['success' => false, 'error' => 'شماره موبایل یا رمز عبور اشتباه است']);
318
+ }
319
+ }
320
+
321
+ function handleLogout() {
322
+ if (isset($_SESSION['user_id'])) {
323
+ global $pdo;
324
+ $pdo->prepare("UPDATE users SET status = 'offline' WHERE id = ?")->execute([$_SESSION['user_id']]);
325
+ }
326
+ session_destroy();
327
+ echo json_encode(['success' => true]);
328
+ }
329
+
330
+ function getRooms($pdo) {
331
+ if (!isset($_SESSION['user_id'])) {
332
+ echo json_encode(['success' => false]);
333
+ return;
334
+ }
335
+
336
+ $stmt = $pdo->prepare("
337
+ SELECT r.*,
338
+ (SELECT COUNT(*) FROM room_members WHERE room_id = r.id) as member_count,
339
+ (SELECT COUNT(*) FROM users u
340
+ JOIN room_members rm ON u.id = rm.user_id
341
+ WHERE rm.room_id = r.id AND u.status = 'online') as online_count
342
+ FROM rooms r
343
+ JOIN room_members rm ON r.id = rm.room_id
344
+ WHERE rm.user_id = ?
345
+ ORDER BY r.id ASC
346
+ ");
347
+ $stmt->execute([$_SESSION['user_id']]);
348
+ $rooms = $stmt->fetchAll();
349
+
350
+ echo json_encode(['success' => true, 'rooms' => $rooms]);
351
+ }
352
+
353
+ function getMessages($pdo) {
354
+ if (!isset($_SESSION['user_id'])) {
355
+ echo json_encode(['success' => false]);
356
+ return;
357
+ }
358
+
359
+ $roomId = intval($_GET['room_id'] ?? 0);
360
+ $lastId = intval($_GET['last_id'] ?? 0);
361
+
362
+ // Verify membership
363
+ $check = $pdo->prepare("SELECT id FROM room_members WHERE room_id = ? AND user_id = ?");
364
+ $check->execute([$roomId, $_SESSION['user_id']]);
365
+ if (!$check->fetch()) {
366
+ echo json_encode(['success' => false, 'error' => 'Access denied']);
367
+ return;
368
+ }
369
+
370
+ $stmt = $pdo->prepare("
371
+ SELECT m.*, u.username, u.avatar
372
+ FROM messages m
373
+ JOIN users u ON m.user_id = u.id
374
+ WHERE m.room_id = ? AND m.id > ?
375
+ ORDER BY m.id ASC
376
+ LIMIT 50
377
+ ");
378
+ $stmt->execute([$roomId, $lastId]);
379
+ $messages = $stmt->fetchAll();
380
+
381
+ // Update last active
382
+ $pdo->prepare("UPDATE users SET last_active = NOW() WHERE id = ?")->execute([$_SESSION['user_id']]);
383
+
384
+ echo json_encode(['success' => true, 'messages' => $messages]);
385
+ }
386
+
387
+ function sendMessage($pdo) {
388
+ if (!isset($_SESSION['user_id'])) {
389
+ echo json_encode(['success' => false]);
390
+ return;
391
+ }
392
+
393
+ $data = json_decode(file_get_contents('php://input'), true);
394
+ $roomId = intval($data['room_id'] ?? 0);
395
+ $message = trim($data['message'] ?? '');
396
+
397
+ if (empty($message)) {
398
+ echo json_encode(['success' => false]);
399
+ return;
400
+ }
401
+
402
+ // Verify membership
403
+ $check = $pdo->prepare("SELECT id FROM room_members WHERE room_id = ? AND user_id = ?");
404
+ $check->execute([$roomId, $_SESSION['user_id']]);
405
+ if (!$check->fetch()) {
406
+ echo json_encode(['success' => false, 'error' => 'Access denied']);
407
+ return;
408
+ }
409
+
410
+ $stmt = $pdo->prepare("INSERT INTO messages (room_id, user_id, message) VALUES (?, ?, ?)");
411
+ $stmt->execute([$roomId, $_SESSION['user_id'], $message]);
412
+
413
+ echo json_encode(['success' => true, 'id' => $pdo->lastInsertId()]);
414
+ }
415
+
416
+ function createRoom($pdo) {
417
+ if (!isset($_SESSION['user_id'])) {
418
+ echo json_encode(['success' => false]);
419
+ return;
420
+ }
421
+
422
+ $data = json_decode(file_get_contents('php://input'), true);
423
+ $name = trim($data['name'] ?? '');
424
+ $type = $data['type'] ?? 'public';
425
+
426
+ if (empty($name)) {
427
+ echo json_encode(['success' => false, 'error' => 'نام اتاق الزامی است']);
428
+ return;
429
+ }
430
+
431
+ // Check room limit (max 3 per user)
432
+ $stmt = $pdo->prepare("SELECT COUNT(*) FROM rooms WHERE created_by = ?");
433
+ $stmt->execute([$_SESSION['user_id']]);
434
+ if ($stmt->fetchColumn() >= 3) {
435
+ echo json_encode(['success' => false, 'error' => 'حداکثر ۳ اتاق می‌توانید بسازید']);
436
+ return;
437
+ }
438
+
439
+ $stmt = $pdo->prepare("INSERT INTO rooms (name, type, created_by) VALUES (?, ?, ?)");
440
+ $stmt->execute([$name, $type, $_SESSION['user_id']]);
441
+ $roomId = $pdo->lastInsertId();
442
+
443
+ // Creator joins automatically
444
+ $pdo->prepare("INSERT INTO room_members (room_id, user_id) VALUES (?, ?)")
445
+ ->execute([$roomId, $_SESSION['user_id']]);
446
+
447
+ echo json_encode(['success' => true, 'room' => ['id' => $roomId, 'name' => $name, 'type' => $type]]);
448
+ }
449
+
450
+ function getOnlineUsers($pdo) {
451
+ $roomId = intval($_GET['room_id'] ?? 0);
452
+ $stmt = $pdo->prepare("
453
+ SELECT COUNT(*) as count
454
+ FROM users u
455
+ JOIN room_members rm ON u.id = rm.user_id
456
+ WHERE rm.room_id = ? AND u.status = 'online'
457
+ AND u.last_active > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
458
+ ");
459
+ $stmt->execute([$roomId]);
460
+ $result = $stmt->fetch();
461
+ echo json_encode(['success' => true, 'count' => $result['count']]);
462
+ }
463
+
464
+ // Check if user is logged in
465
+ $isLoggedIn = isset($_SESSION['user_id']);
466
+ $currentUser = $isLoggedIn ? [
467
+ 'id' => $_SESSION['user_id'],
468
+ 'username' => $_SESSION['username'],
469
+ 'avatar' => $_SESSION['avatar']
470
+ ] : null;
471
+ ?>
472
+
473
+ <!DOCTYPE html>
474
+ <html lang="fa" dir="rtl">
475
+ <head>
476
+ <meta charset="UTF-8">
477
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
478
+ <title>چت روم واقعی | گفتگوی آنلاین</title>
479
+
480
+ <!-- Persian Font: Vazirmatn -->
481
+ <link rel="preconnect" href="https://fonts.googleapis.com">
482
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
483
+ <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
484
+
485
+ <!-- Icons -->
486
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
487
+
488
+ <style>
489
+ :root {
490
+ --primary: #6366f1;
491
+ --primary-dark: #4f46e5;
492
+ --secondary: #ec4899;
493
+ --background: #0f172a;
494
+ --surface: rgba(30, 41, 59, 0.7);
495
+ --text: #f8fafc;
496
+ --text-muted: #94a3b8;
497
+ --border: rgba(148, 163, 184, 0.1);
498
+ --success: #10b981;
499
+ --warning: #f59e0b;
500
+ --glass: rgba(255, 255, 255, 0.05);
501
+ --shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
502
+ }
503
+
504
+ [data-theme="light"] {
505
+ --background: #f1f5f9;
506
+ --surface: rgba(255, 255, 255, 0.8);
507
+ --text: #1e293b;
508
+ --text-muted: #64748b;
509
+ --border: rgba(148, 163, 184, 0.2);
510
+ --glass: rgba(255, 255, 255, 0.6);
511
+ }
512
+
513
+ * {
514
+ margin: 0;
515
+ padding: 0;
516
+ box-sizing: border-box;
517
+ }
518
+
519
+ html {
520
+ scroll-behavior: smooth;
521
+ }
522
+
523
+ body {
524
+ font-family: 'Vazirmatn', sans-serif;
525
+ background: var(--background);
526
+ color: var(--text);
527
+ min-height: 100vh;
528
+ overflow-x: hidden;
529
+ transition: background 0.3s ease, color 0.3s ease;
530
+ }
531
+
532
+ /* Animated Background */
533
+ .bg-animation {
534
+ position: fixed;
535
+ top: 0;
536
+ left: 0;
537
+ width: 100%;
538
+ height: 100%;
539
+ z-index: -1;
540
+ overflow: hidden;
541
+ }
542
+
543
+ .bg-animation .circle {
544
+ position: absolute;
545
+ border-radius: 50%;
546
+ filter: blur(80px);
547
+ opacity: 0.4;
548
+ animation: float 20s infinite ease-in-out;
549
+ }
550
+
551
+ .circle:nth-child(1) {
552
+ width: 400px;
553
+ height: 400px;
554
+ background: var(--primary);
555
+ top: -100px;
556
+ right: -100px;
557
+ animation-delay: 0s;
558
+ }
559
+
560
+ .circle:nth-child(2) {
561
+ width: 300px;
562
+ height: 300px;
563
+ background: var(--secondary);
564
+ bottom: -50px;
565
+ left: -50px;
566
+ animation-delay: 5s;
567
+ }
568
+
569
+ .circle:nth-child(3) {
570
+ width: 250px;
571
+ height: 250px;
572
+ background: var(--success);
573
+ top: 50%;
574
+ left: 50%;
575
+ animation-delay: 10s;
576
+ }
577
+
578
+ @keyframes float {
579
+ 0%, 100% { transform: translate(0, 0) scale(1); }
580
+ 33% { transform: translate(30px, -50px) scale(1.1); }
581
+ 66% { transform: translate(-20px, 20px) scale(0.9); }
582
+ }
583
+
584
+ /* Header */
585
+ header {
586
+ position: fixed;
587
+ top: 0;
588
+ width: 100%;
589
+ padding: 1rem 2rem;
590
+ background: var(--glass);
591
+ backdrop-filter: blur(12px);
592
+ border-bottom: 1px solid var(--border);
593
+ z-index: 1000;
594
+ display: flex;
595
+ justify-content: space-between;
596
+ align-items: center;
597
+ }
598
+
599
+ .logo {
600
+ font-size: 1.5rem;
601
+ font-weight: 900;
602
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
603
+ -webkit-background-clip: text;
604
+ -webkit-text-fill-color: transparent;
605
+ display: flex;
606
+ align-items: center;
607
+ gap: 0.5rem;
608
+ }
609
+
610
+ .header-actions {
611
+ display: flex;
612
+ gap: 1rem;
613
+ align-items: center;
614
+ }
615
+
616
+ .built-with {
617
+ font-size: 0.75rem;
618
+ color: var(--text-muted);
619
+ text-decoration: none;
620
+ padding: 0.5rem 1rem;
621
+ border-radius: 2rem;
622
+ background: var(--glass);
623
+ border: 1px solid var(--border);
624
+ transition: all 0.3s ease;
625
+ }
626
+
627
+ .built-with:hover {
628
+ background: var(--primary);
629
+ color: white;
630
+ transform: translateY(-2px);
631
+ }
632
+
633
+ .theme-toggle {
634
+ background: var(--glass);
635
+ border: 1px solid var(--border);
636
+ color: var(--text);
637
+ width: 40px;
638
+ height: 40px;
639
+ border-radius: 50%;
640
+ cursor: pointer;
641
+ display: flex;
642
+ align-items: center;
643
+ justify-content: center;
644
+ transition: all 0.3s ease;
645
+ }
646
+
647
+ .theme-toggle:hover {
648
+ transform: rotate(180deg);
649
+ background: var(--primary);
650
+ }
651
+
652
+ /* Main Container */
653
+ .container {
654
+ max-width: 1200px;
655
+ margin: 0 auto;
656
+ padding: 6rem 2rem 2rem;
657
+ }
658
+
659
+ /* Hero Section */
660
+ .hero {
661
+ text-align: center;
662
+ padding: 4rem 0;
663
+ animation: fadeInUp 1s ease;
664
+ }
665
+
666
+ @keyframes fadeInUp {
667
+ from { opacity: 0; transform: translateY(30px); }
668
+ to { opacity: 1; transform: translateY(0); }
669
+ }
670
+
671
+ .hero h1 {
672
+ font-size: clamp(2rem, 5vw, 4rem);
673
+ font-weight: 900;
674
+ margin-bottom: 1rem;
675
+ line-height: 1.2;
676
+ }
677
+
678
+ .hero h1 .emoji {
679
+ display: inline-block;
680
+ animation: bounce 2s infinite;
681
+ }
682
+
683
+ @keyframes bounce {
684
+ 0%, 100% { transform: translateY(0); }
685
+ 50% { transform: translateY(-10px); }
686
+ }
687
+
688
+ .subtitle {
689
+ font-size: 1.25rem;
690
+ color: var(--text-muted);
691
+ margin-bottom: 2rem;
692
+ font-weight: 300;
693
+ }
694
+
695
+ /* Features Grid */
696
+ .features {
697
+ display: grid;
698
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
699
+ gap: 2rem;
700
+ margin: 4rem 0;
701
+ perspective: 1000px;
702
+ }
703
+
704
+ .feature {
705
+ background: var(--surface);
706
+ backdrop-filter: blur(10px);
707
+ border: 1px solid var(--border);
708
+ border-radius: 1.5rem;
709
+ padding: 2rem;
710
+ text-align: center;
711
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
712
+ position: relative;
713
+ overflow: hidden;
714
+ cursor: pointer;
715
+ }
716
+
717
+ .feature::before {
718
+ content: '';
719
+ position: absolute;
720
+ top: 0;
721
+ left: 0;
722
+ width: 100%;
723
+ height: 100%;
724
+ background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.05));
725
+ opacity: 0;
726
+ transition: opacity 0.3s;
727
+ }
728
+
729
+ .feature:hover {
730
+ transform: translateY(-10px) rotateX(5deg);
731
+ box-shadow: var(--shadow);
732
+ border-color: var(--primary);
733
+ }
734
+
735
+ .feature:hover::before {
736
+ opacity: 1;
737
+ }
738
+
739
+ .icon {
740
+ font-size: 3rem;
741
+ margin-bottom: 1rem;
742
+ display: inline-block;
743
+ transition: transform 0.3s;
744
+ }
745
+
746
+ .feature:hover .icon {
747
+ transform: scale(1.2) rotate(10deg);
748
+ }
749
+
750
+ .feature h3 {
751
+ font-size: 1.25rem;
752
+ margin-bottom: 0.5rem;
753
+ color: var(--text);
754
+ }
755
+
756
+ .feature p {
757
+ color: var(--text-muted);
758
+ font-size: 0.95rem;
759
+ line-height: 1.6;
760
+ }
761
+
762
+ /* CTA Section */
763
+ .cta {
764
+ display: flex;
765
+ gap: 1rem;
766
+ justify-content: center;
767
+ flex-wrap: wrap;
768
+ margin: 3rem 0;
769
+ }
770
+
771
+ .btn {
772
+ padding: 1rem 2.5rem;
773
+ border-radius: 2rem;
774
+ font-family: inherit;
775
+ font-size: 1rem;
776
+ font-weight: 700;
777
+ cursor: pointer;
778
+ transition: all 0.3s ease;
779
+ border: none;
780
+ text-decoration: none;
781
+ display: inline-flex;
782
+ align-items: center;
783
+ gap: 0.5rem;
784
+ position: relative;
785
+ overflow: hidden;
786
+ }
787
+
788
+ .btn-primary {
789
+ background: linear-gradient(135deg, var(--primary), var(--primary-dark));
790
+ color: white;
791
+ box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4);
792
+ }
793
+
794
+ .btn-secondary {
795
+ background: var(--glass);
796
+ color: var(--text);
797
+ border: 2px solid var(--border);
798
+ backdrop-filter: blur(10px);
799
+ }
800
+
801
+ .btn:hover {
802
+ transform: translateY(-3px);
803
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
804
+ }
805
+
806
+ .btn:active {
807
+ transform: translateY(-1px);
808
+ }
809
+
810
+ /* Modal Styles */
811
+ .modal {
812
+ display: none;
813
+ position: fixed;
814
+ top: 0;
815
+ left: 0;
816
+ width: 100%;
817
+ height: 100%;
818
+ background: rgba(0, 0, 0, 0.8);
819
+ backdrop-filter: blur(5px);
820
+ z-index: 2000;
821
+ justify-content: center;
822
+ align-items: center;
823
+ opacity: 0;
824
+ transition: opacity 0.3s;
825
+ }
826
+
827
+ .modal.active {
828
+ display: flex;
829
+ opacity: 1;
830
+ }
831
+
832
+ .modal-content {
833
+ background: var(--surface);
834
+ border: 1px solid var(--border);
835
+ border-radius: 2rem;
836
+ padding: 2.5rem;
837
+ width: 90%;
838
+ max-width: 450px;
839
+ position: relative;
840
+ transform: scale(0.9);
841
+ transition: transform 0.3s;
842
+ box-shadow: var(--shadow);
843
+ }
844
+
845
+ .modal.active .modal-content {
846
+ transform: scale(1);
847
+ }
848
+
849
+ .close-modal {
850
+ position: absolute;
851
+ top: 1rem;
852
+ left: 1rem;
853
+ background: none;
854
+ border: none;
855
+ color: var(--text-muted);
856
+ font-size: 1.5rem;
857
+ cursor: pointer;
858
+ width: 40px;
859
+ height: 40px;
860
+ border-radius: 50%;
861
+ display: flex;
862
+ align-items: center;
863
+ justify-content: center;
864
+ transition: all 0.3s;
865
+ }
866
+
867
+ .close-modal:hover {
868
+ background: var(--glass);
869
+ color: var(--text);
870
+ transform: rotate(90deg);
871
+ }
872
+
873
+ .form-group {
874
+ margin-bottom: 1.5rem;
875
+ }
876
+
877
+ .form-group label {
878
+ display: block;
879
+ margin-bottom: 0.5rem;
880
+ color: var(--text);
881
+ font-weight: 500;
882
+ }
883
+
884
+ .form-group input, .form-group select {
885
+ width: 100%;
886
+ padding: 0.75rem 1rem;
887
+ border: 2px solid var(--border);
888
+ border-radius: 1rem;
889
+ background: var(--glass);
890
+ color: var(--text);
891
+ font-family: inherit;
892
+ font-size: 1rem;
893
+ transition: all 0.3s;
894
+ }
895
+
896
+ .form-group input:focus, .form-group select:focus {
897
+ outline: none;
898
+ border-color: var(--primary);
899
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
900
+ }
901
+
902
+ .form-submit {
903
+ width: 100%;
904
+ margin-top: 1rem;
905
+ }
906
+
907
+ .error-msg {
908
+ color: var(--secondary);
909
+ font-size: 0.875rem;
910
+ margin-top: 0.5rem;
911
+ display: none;
912
+ }
913
+
914
+ /* Chat Application Interface */
915
+ #chat-app {
916
+ display: none;
917
+ height: calc(100vh - 80px);
918
+ margin-top: 80px;
919
+ }
920
+
921
+ .chat-container {
922
+ display: grid;
923
+ grid-template-columns: 300px 1fr;
924
+ height: 100%;
925
+ gap: 1rem;
926
+ padding: 1rem;
927
+ }
928
+
929
+ .sidebar {
930
+ background: var(--surface);
931
+ border: 1px solid var(--border);
932
+ border-radius: 1.5rem;
933
+ padding: 1.5rem;
934
+ display: flex;
935
+ flex-direction: column;
936
+ gap: 1rem;
937
+ backdrop-filter: blur(10px);
938
+ overflow-y: auto;
939
+ }
940
+
941
+ .user-profile {
942
+ text-align: center;
943
+ padding-bottom: 1rem;
944
+ border-bottom: 1px solid var(--border);
945
+ }
946
+
947
+ .avatar {
948
+ width: 80px;
949
+ height: 80px;
950
+ border-radius: 50%;
951
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
952
+ display: flex;
953
+ align-items: center;
954
+ justify-content: center;
955
+ font-size: 2rem;
956
+ margin: 0 auto 0.5rem;
957
+ border: 3px solid var(--border);
958
+ cursor: pointer;
959
+ transition: transform 0.3s;
960
+ }
961
+
962
+ .avatar:hover {
963
+ transform: scale(1.1);
964
+ }
965
+
966
+ .rooms-list {
967
+ flex: 1;
968
+ overflow-y: auto;
969
+ }
970
+
971
+ .room-item {
972
+ padding: 1rem;
973
+ margin-bottom: 0.5rem;
974
+ border-radius: 1rem;
975
+ cursor: pointer;
976
+ transition: all 0.3s;
977
+ display: flex;
978
+ align-items: center;
979
+ gap: 0.75rem;
980
+ border: 1px solid transparent;
981
+ }
982
+
983
+ .room-item:hover, .room-item.active {
984
+ background: var(--glass);
985
+ border-color: var(--primary);
986
+ }
987
+
988
+ .room-item.active {
989
+ background: rgba(99, 102, 241, 0.1);
990
+ }
991
+
992
+ .chat-area {
993
+ background: var(--surface);
994
+ border: 1px solid var(--border);
995
+ border-radius: 1.5rem;
996
+ display: flex;
997
+ flex-direction: column;
998
+ overflow: hidden;
999
+ backdrop-filter: blur(10px);
1000
+ }
1001
+
1002
+ .chat-header {
1003
+ padding: 1.5rem;
1004
+ border-bottom: 1px solid var(--border);
1005
+ display: flex;
1006
+ justify-content: space-between;
1007
+ align-items: center;
1008
+ }
1009
+
1010
+ .chat-messages {
1011
+ flex: 1;
1012
+ overflow-y: auto;
1013
+ padding: 1.5rem;
1014
+ display: flex;
1015
+ flex-direction: column;
1016
+ gap: 1rem;
1017
+ }
1018
+
1019
+ .message {
1020
+ max-width: 70%;
1021
+ padding: 1rem 1.25rem;
1022
+ border-radius: 1.25rem;
1023
+ position: relative;
1024
+ animation: messageSlide 0.3s ease;
1025
+ word-wrap: break-word;
1026
+ }
1027
+
1028
+ @keyframes messageSlide {
1029
+ from { opacity: 0; transform: translateY(10px); }
1030
+ to { opacity: 1; transform: translateY(0); }
1031
+ }
1032
+
1033
+ .message.sent {
1034
+ align-self: flex-start;
1035
+ background: linear-gradient(135deg, var(--primary), var(--primary-dark));
1036
+ color: white;
1037
+ border-bottom-right-radius: 0.25rem;
1038
+ }
1039
+
1040
+ .message.received {
1041
+ align-self: flex-end;
1042
+ background: var(--glass);
1043
+ border: 1px solid var(--border);
1044
+ border-bottom-left-radius: 0.25rem;
1045
+ }
1046
+
1047
+ .message-author {
1048
+ font-size: 0.75rem;
1049
+ opacity: 0.8;
1050
+ margin-bottom: 0.25rem;
1051
+ font-weight: 700;
1052
+ }
1053
+
1054
+ .message-time {
1055
+ font-size: 0.7rem;
1056
+ opacity: 0.6;
1057
+ margin-top: 0.5rem;
1058
+ display: block;
1059
+ text-align: left;
1060
+ }
1061
+
1062
+ .chat-input {
1063
+ padding: 1.5rem;
1064
+ border-top: 1px solid var(--border);
1065
+ display: flex;
1066
+ gap: 1rem;
1067
+ }
1068
+
1069
+ .chat-input input {
1070
+ flex: 1;
1071
+ padding: 0.75rem 1.25rem;
1072
+ border: 2px solid var(--border);
1073
+ border-radius: 2rem;
1074
+ background: var(--glass);
1075
+ color: var(--text);
1076
+ font-family: inherit;
1077
+ font-size: 1rem;
1078
+ }
1079
+
1080
+ .chat-input input:focus {
1081
+ outline: none;
1082
+ border-color: var(--primary);
1083
+ }
1084
+
1085
+ .send-btn {
1086
+ width: 50px;
1087
+ height: 50px;
1088
+ border-radius: 50%;
1089
+ background: var(--primary);
1090
+ color: white;
1091
+ border: none;
1092
+ cursor: pointer;
1093
+ display: flex;
1094
+ align-items: center;
1095
+ justify-content: center;
1096
+ transition: all 0.3s;
1097
+ font-size: 1.25rem;
1098
+ }
1099
+
1100
+ .send-btn:hover {
1101
+ background: var(--primary-dark);
1102
+ transform: scale(1.1);
1103
+ }
1104
+
1105
+ .send-btn:disabled {
1106
+ opacity: 0.5;
1107
+ cursor: not-allowed;
1108
+ }
1109
+
1110
+ /* Responsive */
1111
+ @media (max-width: 768px) {
1112
+ .chat-container {
1113
+ grid-template-columns: 1fr;
1114
+ position: relative;
1115
+ }
1116
+
1117
+ .sidebar {
1118
+ position: absolute;
1119
+ right: -100%;
1120
+ top: 0;
1121
+ height: 100%;
1122
+ width: 280px;
1123
+ z-index: 100;
1124
+ transition: right 0.3s;
1125
+ box-shadow: var(--shadow);
1126
+ }
1127
+
1128
+ .sidebar.open {
1129
+ right: 0;
1130
+ }
1131
+
1132
+ .mobile-menu-btn {
1133
+ display: block !important;
1134
+ }
1135
+
1136
+ .features {
1137
+ grid-template-columns: 1fr;
1138
+ }
1139
+ }
1140
+
1141
+ .mobile-menu-btn {
1142
+ display: none;
1143
+ background: var(--glass);
1144
+ border: 1px solid var(--border);
1145
+ color: var(--text);
1146
+ padding: 0.5rem 1rem;
1147
+ border-radius: 0.5rem;
1148
+ cursor: pointer;
1149
+ }
1150
+
1151
+ /* Scrollbar */
1152
+ ::-webkit-scrollbar {
1153
+ width: 8px;
1154
+ }
1155
+
1156
+ ::-webkit-scrollbar-track {
1157
+ background: transparent;
1158
+ }
1159
+
1160
+ ::-webkit-scrollbar-thumb {
1161
+ background: var(--border);
1162
+ border-radius: 4px;
1163
+ }
1164
+
1165
+ ::-webkit-scrollbar-thumb:hover {
1166
+ background: var(--primary);
1167
+ }
1168
+
1169
+ /* Loading Animation */
1170
+ .typing-indicator {
1171
+ display: none;
1172
+ align-self: flex-start;
1173
+ background: var(--glass);
1174
+ padding: 1rem 1.5rem;
1175
+ border-radius: 1.25rem;
1176
+ border-bottom-right-radius: 0.25rem;
1177
+ margin-bottom: 1rem;
1178
+ }
1179
+
1180
+ .typing-indicator.active {
1181
+ display: flex;
1182
+ gap: 0.25rem;
1183
+ }
1184
+
1185
+ .typing-dot {
1186
+ width: 8px;
1187
+ height: 8px;
1188
+ background: var(--text-muted);
1189
+ border-radius: 50%;
1190
+ animation: typing 1.4s infinite;
1191
+ }
1192
+
1193
+ .typing-dot:nth-child(2) { animation-delay: 0.2s; }
1194
+ .typing-dot:nth-child(3) { animation-delay: 0.4s; }
1195
+
1196
+ @keyframes typing {
1197
+ 0%, 60%, 100% { transform: translateY(0); }
1198
+ 30% { transform: translateY(-10px); }
1199
+ }
1200
+
1201
+ .toast {
1202
+ position: fixed;
1203
+ bottom: 2rem;
1204
+ left: 50%;
1205
+ transform: translateX(-50%) translateY(100px);
1206
+ background: var(--surface);
1207
+ color: var(--text);
1208
+ padding: 1rem 2rem;
1209
+ border-radius: 1rem;
1210
+ border: 1px solid var(--border);
1211
+ box-shadow: var(--shadow);
1212
+ z-index: 3000;
1213
+ opacity: 0;
1214
+ transition: all 0.3s;
1215
+ }
1216
+
1217
+ .toast.show {
1218
+ transform: translateX(-50%) translateY(0);
1219
+ opacity: 1;
1220
+ }
1221
+
1222
+ .toast.error {
1223
+ border-color: var(--secondary);
1224
+ background: rgba(236, 72, 153, 0.1);
1225
+ }
1226
+
1227
+ .toast.success {
1228
+ border-color: var(--success);
1229
+ background: rgba(16, 185, 129, 0.1);
1230
+ }
1231
+ </style>
1232
+ </head>
1233
+ <body>
1234
+ <!-- Animated Background -->
1235
+ <div class="bg-animation">
1236
+ <div class="circle"></div>
1237
+ <div class="circle"></div>
1238
+ <div class="circle"></div>
1239
+ </div>
1240
+
1241
+ <!-- Header -->
1242
+ <header>
1243
+ <div class="logo">
1244
+ <i class="fas fa-comments"></i>
1245
+ <span>چت روم</span>
1246
+ </div>
1247
+ <div class="header-actions">
1248
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
1249
+ <i class="fas fa-code"></i> Built with anycoder
1250
+ </a>
1251
+ <button class="theme-toggle" onclick="toggleTheme()" title="تغییر تم">
1252
+ <i class="fas fa-moon"></i>
1253
+ </button>
1254
+ </div>
1255
+ </header>
1256
+
1257
+ <?php if (!$isLoggedIn): ?>
1258
+ <!-- Landing Page -->
1259
+ <div id="landing" class="container">
1260
+ <div class="hero">
1261
+ <h1><span class="emoji">💬</span> چت روم واقعی</h1>
1262
+ <p class="subtitle">محلی امن و سریع برای گفتگو با دوستان و همکاران</p>
1263
+ </div>
1264
+
1265
+ <div class="features">
1266
+ <div class="feature" onclick="showRegister()">
1267
+ <div class="icon">🔐</div>
1268
+ <h3>ثبت‌نام واقعی</h3>
1269
+ <p>با شماره موبایل و رمز عبور امن ثبت‌نام کنید. اطلاعات در دیتابیس ذخیره می‌شود</p>
1270
+ </div>
1271
+
1272
+ <div class="feature" onclick="showRegister()">
1273
+ <div class="icon">👥</div>
1274
+ <h3>اتاق‌های چند نفره</h3>
1275
+ <p>تا ۳ اتاق بسازید و با کاربران واقعی چت کنید. پیام‌ها لحظه‌ای ذخیره و نمایش داده می‌شوند</p>
1276
+ </div>
1277
+
1278
+ <div class="feature" onclick="showRegister()">
1279
+ <div class="icon">⚡</div>
1280
+ <h3>چت زنده</h3>
1281
+ <p>بروزرسانی خودکار هر ۳ ثانیه. مشاهده وضعیت آنلاین بودن کاربران</p>
1282
+ </div>
1283
+ </div>
1284
+
1285
+ <div class="cta">
1286
+ <button onclick="showRegister()" class="btn btn-primary">
1287
+ <i class="fas fa-rocket"></i>
1288
+ شروع کنید
1289
+ </button>
1290
+ <button onclick="showLogin()" class="btn btn-secondary">
1291
+ <i class="fas fa-sign-in-alt"></i>
1292
+ ورود
1293
+ </button>
1294
+ </div>
1295
+ </div>
1296
+
1297
+ <!-- Auth Modals -->
1298
+ <div id="login-modal" class="modal">
1299
+ <div class="modal-content">
1300
+ <button class="close-modal" onclick="closeModals()">
1301
+ <i class="fas fa-times"></i>
1302
+ </button>
1303
+ <h2 style="margin-bottom: 1.5rem; text-align: center;">
1304
+ <i class="fas fa-sign-in-alt" style="color: var(--primary);"></i>
1305
+ ورود به حساب
1306
+ </h2>
1307
+ <form id="login-form" onsubmit="handleLogin(event)">
1308
+ <div class="form-group">
1309
+ <label>شماره موبایل</label>
1310
+ <input type="tel" name="phone" placeholder="09*********" required pattern="09[0-9]{9}">
1311
+ </div>
1312
+ <div class="form-group">
1313
+ <label>رمز عبور</label>
1314
+ <input type="password" name="password" placeholder="••••••••" required>
1315
+ </div>
1316
+ <div class="error-msg" id="login-error"></div>
1317
+ <button type="submit" class="btn btn-primary form-submit">