samirerty commited on
Commit
3c9f92f
·
verified ·
1 Parent(s): 3a8fc15

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +714 -707
index.html CHANGED
@@ -5,36 +5,38 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
  <title>چت روم مینیمال | Minimal Chat</title>
7
 
8
- <!-- Fonts: Vazirmatn for Persian -->
9
  <link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
10
- <!-- Icons: Material Symbols -->
11
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0" />
12
 
13
  <style>
14
  :root {
15
- --glass-bg: rgba(255, 255, 255, 0.1);
 
 
 
16
  --glass-border: rgba(255, 255, 255, 0.2);
17
  --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
18
- --primary-color: #6c5ce7;
19
- --accent-color: #a29bfe;
20
  --text-color: #ffffff;
21
- --text-secondary: #dfe6e9;
22
- --danger-color: #ff7675;
23
- --msg-bg-self: #6c5ce7;
24
- --msg-bg-other: rgba(255, 255, 255, 0.15);
25
- --font-main: 'Vazirmatn', sans-serif;
26
  }
27
 
28
  * {
29
  box-sizing: border-box;
30
  margin: 0;
31
  padding: 0;
 
32
  -webkit-tap-highlight-color: transparent;
33
  }
34
 
35
  body {
36
- font-family: var(--font-main);
37
- background: linear-gradient(135deg, #1e1e2f 0%, #2d3436 100%);
38
  color: var(--text-color);
39
  height: 100vh;
40
  overflow: hidden;
@@ -43,966 +45,971 @@
43
  align-items: center;
44
  }
45
 
46
- /* Animated Background Elements */
47
- .bg-shape {
48
- position: absolute;
49
- border-radius: 50%;
50
- filter: blur(80px);
51
- z-index: -1;
52
- animation: float 10s infinite ease-in-out;
53
- opacity: 0.6;
54
- }
55
- .shape-1 { width: 300px; height: 300px; background: #6c5ce7; top: -50px; left: -50px; }
56
- .shape-2 { width: 250px; height: 250px; background: #00cec9; bottom: -50px; right: -50px; animation-delay: 2s; }
57
-
58
- @keyframes float {
59
- 0%, 100% { transform: translate(0, 0); }
60
- 50% { transform: translate(20px, 40px); }
61
- }
62
-
63
- /* Glassmorphism Utilities */
64
  .glass {
65
  background: var(--glass-bg);
66
  backdrop-filter: blur(12px);
67
  -webkit-backdrop-filter: blur(12px);
68
  border: 1px solid var(--glass-border);
69
  box-shadow: var(--glass-shadow);
 
70
  }
71
 
72
  .glass-panel {
73
- border-radius: 20px;
74
- padding: 20px;
 
75
  }
76
 
77
- /* Auth Screen */
78
- #auth-screen {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  width: 100%;
80
- max-width: 400px;
81
  height: 100%;
82
  display: flex;
83
  flex-direction: column;
84
- justify-content: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  align-items: center;
86
- z-index: 100;
87
- transition: transform 0.5s ease;
88
  }
89
 
90
- .auth-content {
91
- width: 90%;
92
- text-align: center;
 
 
 
93
  }
94
 
95
- h1 { margin-bottom: 1.5rem; font-weight: 700; letter-spacing: -0.5px; }
96
-
97
- .input-group {
98
- margin-bottom: 1rem;
99
- text-align: right;
100
  }
101
 
102
- label { display: block; margin-bottom: 0.5rem; font-size: 0.9rem; color: var(--text-secondary); }
 
 
 
 
 
 
103
 
104
- input {
105
  width: 100%;
106
- padding: 12px 16px;
 
107
  border-radius: 12px;
108
  border: 1px solid var(--glass-border);
109
- background: rgba(0, 0, 0, 0.2);
110
  color: white;
111
- font-family: var(--font-main);
112
  font-size: 1rem;
113
- outline: none;
114
  transition: 0.3s;
115
  }
116
 
117
- input:focus { border-color: var(--primary-color); background: rgba(0, 0, 0, 0.4); }
 
 
 
118
 
119
  .btn {
120
- width: 100%;
121
- padding: 12px;
122
- border: none;
123
  border-radius: 12px;
 
124
  background: var(--primary-color);
125
  color: white;
126
- font-family: var(--font-main);
127
  font-weight: bold;
128
- font-size: 1rem;
129
  cursor: pointer;
130
- transition: transform 0.2s, background 0.2s;
131
- display: flex;
132
  align-items: center;
133
  justify-content: center;
134
  gap: 8px;
135
  }
136
 
137
- .btn:active { transform: scale(0.98); }
138
- .btn-outline { background: transparent; border: 1px solid var(--glass-border); }
139
- .btn-sm { padding: 6px 12px; font-size: 0.85rem; width: auto; }
140
-
141
- /* Main App Container */
142
- #app-container {
143
- width: 100%;
144
- height: 100%;
145
- max-width: 1200px;
146
- display: flex;
147
- opacity: 0;
148
- pointer-events: none;
149
- transition: opacity 0.5s;
150
- position: relative;
151
  }
152
 
153
- #app-container.active { opacity: 1; pointer-events: all; }
154
-
155
- /* Sidebar (Rooms) */
156
- .sidebar {
157
- width: 300px;
158
- display: flex;
159
- flex-direction: column;
160
- border-left: 1px solid var(--glass-border);
161
- background: rgba(0,0,0,0.2);
162
- backdrop-filter: blur(10px);
163
- transition: transform 0.3s ease;
164
- z-index: 10;
165
  }
166
 
167
- .sidebar-header {
168
- padding: 20px;
169
- border-bottom: 1px solid var(--glass-border);
170
- display: flex;
171
- justify-content: space-between;
172
- align-items: center;
173
  }
174
 
175
- .brand-link {
176
- font-size: 0.75rem;
177
- color: var(--accent-color);
178
- text-decoration: none;
179
- opacity: 0.8;
180
- margin-top: 5px;
181
- display: inline-block;
182
  }
183
 
184
  .room-list {
185
  flex: 1;
186
  overflow-y: auto;
187
- padding: 10px;
188
  }
189
 
190
- .room-item {
191
  display: flex;
192
  align-items: center;
193
- padding: 12px;
194
- border-radius: 12px;
195
- margin-bottom: 8px;
196
  cursor: pointer;
197
- transition: background 0.2s;
198
  }
199
 
200
- .room-item:hover, .room-item.active { background: rgba(255,255,255,0.1); }
201
-
202
- .avatar {
203
- width: 45px;
204
- height: 45px;
 
 
 
205
  border-radius: 50%;
206
- background: linear-gradient(45deg, #ff9a9e, #fad0c4);
207
- margin-left: 12px;
208
- display: flex;
209
- align-items: center;
210
- justify-content: center;
 
 
 
 
 
211
  font-weight: bold;
212
- color: #333;
213
- font-size: 1.2rem;
214
  }
215
 
216
- .room-info { flex: 1; }
217
- .room-name { font-weight: bold; font-size: 1rem; }
218
- .room-last-msg { font-size: 0.8rem; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 150px; }
 
 
 
 
 
219
 
220
- /* Chat Area */
221
- .chat-area {
222
- flex: 1;
 
 
 
 
 
 
223
  display: flex;
224
- flex-direction: column;
225
- position: relative;
226
- background: rgba(255,255,255,0.02);
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
 
229
  .chat-header {
230
- padding: 15px 20px;
231
  display: flex;
232
  align-items: center;
 
233
  border-bottom: 1px solid var(--glass-border);
234
- background: rgba(0,0,0,0.1);
235
  }
236
 
237
- .back-btn { display: none; margin-left: 10px; cursor: pointer; }
238
-
239
- .chat-messages {
240
  flex: 1;
241
  overflow-y: auto;
242
- padding: 20px;
243
  display: flex;
244
  flex-direction: column;
245
- gap: 15px;
246
  }
247
 
248
- /* Message Bubbles */
249
  .message {
250
  max-width: 75%;
251
- position: relative;
252
- animation: slideUp 0.3s ease;
253
- user-select: none; /* Prevent text selection for easier gesture handling */
254
- }
255
-
256
- @keyframes slideUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
257
-
258
- .message.self { align-self: flex-end; }
259
- .message.other { align-self: flex-start; }
260
-
261
- .bubble {
262
- padding: 10px 16px;
263
  border-radius: 18px;
 
 
264
  font-size: 0.95rem;
265
  line-height: 1.5;
266
- position: relative;
267
- word-wrap: break-word;
268
  }
269
 
270
- .message.self .bubble {
271
- background: var(--msg-bg-self);
272
- border-bottom-left-radius: 4px;
273
  }
274
 
275
- .message.other .bubble {
276
- background: var(--msg-bg-other);
 
277
  border-bottom-right-radius: 4px;
278
  }
279
 
 
 
 
 
 
 
280
  .msg-meta {
281
  font-size: 0.7rem;
282
- margin-top: 4px;
283
  opacity: 0.7;
284
- text-align: left;
 
285
  display: flex;
286
- align-items: center;
287
  justify-content: flex-end;
288
- gap: 4px;
 
289
  }
290
 
291
  .reply-preview {
 
 
 
 
292
  font-size: 0.8rem;
293
- padding-right: 8px;
294
- border-right: 3px solid rgba(255,255,255,0.5);
295
- margin-bottom: 6px;
296
- color: rgba(255,255,255,0.8);
297
- background: rgba(0,0,0,0.1);
298
- padding: 4px 8px;
299
- border-radius: 4px;
300
- display: inline-block;
301
  }
302
 
303
- .reactions {
304
- display: flex;
305
- gap: 5px;
306
- margin-top: 5px;
307
- justify-content: flex-end;
308
  }
309
-
310
- .reaction-badge {
311
- background: rgba(0,0,0,0.3);
312
- border-radius: 10px;
313
- padding: 2px 6px;
314
- font-size: 0.75rem;
 
 
 
 
 
 
 
 
315
  display: flex;
316
- align-items: center;
317
- gap: 2px;
318
  }
319
 
320
- /* Input Area */
321
- .chat-input-area {
322
- padding: 15px;
323
- background: rgba(0,0,0,0.2);
324
  display: flex;
325
  align-items: flex-end;
326
  gap: 10px;
327
- position: relative;
 
 
 
 
 
 
 
 
328
  }
329
 
330
  .reply-bar {
331
- position: absolute;
332
- top: -40px;
333
- left: 15px;
334
- right: 15px;
335
- background: rgba(255,255,255,0.1);
336
- padding: 8px;
337
- border-radius: 8px;
338
- font-size: 0.8rem;
339
  display: none;
 
 
 
 
 
340
  justify-content: space-between;
341
  align-items: center;
342
  }
343
 
344
- textarea {
345
- flex: 1;
346
- background: rgba(255,255,255,0.1);
347
  border: none;
348
- border-radius: 20px;
349
- padding: 12px;
350
  color: white;
351
- font-family: var(--font-main);
352
  resize: none;
353
  max-height: 100px;
354
- outline: none;
 
355
  }
356
 
357
- .icon-btn {
358
- background: transparent;
359
- border: none;
360
- color: var(--text-secondary);
361
- cursor: pointer;
362
- padding: 8px;
363
- border-radius: 50%;
364
- transition: 0.2s;
365
- display: flex;
366
- align-items: center;
367
- justify-content: center;
368
- }
369
-
370
- .icon-btn:hover { background: rgba(255,255,255,0.1); color: white; }
371
- .send-btn { background: var(--primary-color); color: white; }
372
- .send-btn:hover { background: var(--accent-color); }
373
-
374
- /* Context Menu (Long Press) */
375
- .context-menu {
376
- position: absolute;
377
- background: #2d3436;
378
- border: 1px solid var(--glass-border);
379
- border-radius: 12px;
380
- padding: 8px;
381
  display: flex;
382
- flex-direction: column;
383
  gap: 5px;
384
- box-shadow: 0 5px 15px rgba(0,0,0,0.5);
385
- z-index: 1000;
386
- display: none;
387
- min-width: 140px;
388
  }
389
 
390
- .ctx-item {
391
- padding: 8px 12px;
 
 
 
392
  display: flex;
393
  align-items: center;
394
- gap: 10px;
395
- cursor: pointer;
396
- border-radius: 8px;
397
- font-size: 0.9rem;
398
- color: white;
399
- }
400
-
401
- .ctx-item:hover { background: rgba(255,255,255,0.1); }
402
- .ctx-item.delete { color: var(--danger-color); }
403
-
404
- /* Reaction Picker */
405
- .reaction-picker {
406
- position: absolute;
407
- background: #2d3436;
408
- border-radius: 20px;
409
- padding: 5px 10px;
410
- display: flex;
411
- gap: 8px;
412
- box-shadow: 0 5px 15px rgba(0,0,0,0.5);
413
- z-index: 1001;
414
- display: none;
415
- transform: translateY(-10px);
416
- animation: fadeIn 0.2s;
417
  }
418
 
419
- @keyframes fadeIn { from { opacity: 0; transform: translateY(0); } to { opacity: 1; transform: translateY(-10px); } }
420
-
421
- .emoji-opt { cursor: pointer; font-size: 1.2rem; transition: transform 0.2s; }
422
- .emoji-opt:hover { transform: scale(1.2); }
423
-
424
- /* Modals */
425
  .modal-overlay {
426
  position: fixed;
427
- top: 0; left: 0; right: 0; bottom: 0;
 
 
 
428
  background: rgba(0,0,0,0.6);
429
  backdrop-filter: blur(5px);
430
- z-index: 2000;
431
- display: none;
432
  justify-content: center;
433
  align-items: center;
 
 
 
434
  }
435
 
436
- .modal-overlay.active { display: flex; }
 
 
 
437
 
438
- .modal-content {
439
- width: 90%;
440
  max-width: 350px;
441
- background: #2d3436;
442
- border: 1px solid var(--glass-border);
443
- border-radius: 20px;
444
  padding: 20px;
 
 
 
 
 
 
 
 
 
 
445
  text-align: center;
446
- box-shadow: 0 10px 40px rgba(0,0,0,0.5);
447
- animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
448
  }
449
 
450
- @keyframes popIn { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
- .modal-title { margin-bottom: 15px; font-size: 1.2rem; }
453
- .modal-actions { display: flex; gap: 10px; margin-top: 20px; }
 
454
 
455
- /* Toast Notification */
456
- .toast {
457
  position: fixed;
458
- bottom: 20px;
459
  left: 50%;
460
- transform: translateX(-50%) translateY(100px);
461
  background: rgba(0,0,0,0.8);
462
  color: white;
463
  padding: 10px 20px;
464
- border-radius: 30px;
465
  font-size: 0.9rem;
466
- z-index: 3000;
467
- transition: transform 0.3s ease;
468
- display: flex;
469
- align-items: center;
470
- gap: 8px;
471
  }
472
- .toast.show { transform: translateX(-50%) translateY(0); }
473
 
474
- /* Responsive */
475
- @media (max-width: 768px) {
476
- .sidebar {
477
- position: absolute;
478
- top: 0; bottom: 0; right: 0;
479
- width: 85%;
480
- transform: translateX(100%);
481
- box-shadow: -5px 0 15px rgba(0,0,0,0.5);
482
- }
483
- .sidebar.open { transform: translateX(0); }
484
- .back-btn { display: block; }
485
- #app-container { flex-direction: column; }
 
 
 
486
  }
487
  </style>
488
  </head>
489
  <body>
490
 
491
- <!-- Animated Background -->
492
- <div class="bg-shape shape-1"></div>
493
- <div class="bg-shape shape-2"></div>
494
-
495
- <!-- Auth Screen -->
496
- <section id="auth-screen" class="glass glass-panel">
497
- <div class="auth-content">
498
- <h1>ورود به چت روم</h1>
499
- <div class="input-group">
500
- <label>شماره موبایل</label>
501
- <input type="tel" id="mobile-input" placeholder="0912..." maxlength="11">
502
  </div>
503
- <button class="btn" onclick="Auth.login()">
504
- <span class="material-symbols-rounded">login</span>
505
- دریافت کد ورود
506
- </button>
507
- <div style="margin-top: 15px; font-size: 0.8rem; color: var(--text-secondary);">
508
- (برای تست کد ورود ۱۲۳۴ است)
 
 
 
 
 
 
 
 
 
 
509
  </div>
510
- </div>
511
- </section>
512
 
513
- <!-- Main App -->
514
- <main id="app-container">
515
-
516
- <!-- Sidebar -->
517
- <aside class="sidebar glass" id="sidebar">
518
- <div class="sidebar-header">
519
- <div>
520
- <h3 id="user-display-name">کاربر</h3>
521
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="brand-link">Built with anycoder</a>
522
- </div>
523
- <button class="icon-btn" onclick="UI.toggleSidebar()" title="منو">
524
- <span class="material-symbols-rounded">menu</span>
525
- </button>
526
  </div>
 
527
 
 
 
528
  <div class="room-list" id="room-list-container">
529
- <!-- Rooms injected via JS -->
530
  </div>
531
-
532
- <div style="padding: 15px; border-top: 1px solid var(--glass-border);">
533
- <button class="btn" onclick="Rooms.showCreateModal()">
534
- <span class="material-symbols-rounded">add</span>
535
- اتاق جدید (حداکثر ۳)
536
- </button>
537
  </div>
538
- </aside>
539
 
540
- <!-- Chat Area -->
541
- <section class="chat-area">
542
- <header class="chat-header">
543
- <span class="material-symbols-rounded back-btn" onclick="UI.toggleSidebar()">arrow_forward</span>
544
-
545
- <div class="avatar" id="current-room-avatar" style="width: 35px; height: 35px; font-size: 1rem; margin-left: 10px;">?</div>
546
- <div style="flex: 1;">
547
- <div class="room-name" id="current-room-name">انتخاب اتاق</div>
548
- <div style="font-size: 0.75rem; color: var(--text-secondary);">آنلاین</div>
549
- </div>
550
-
551
- <button class="icon-btn" onclick="Chat.copyLink()" title="کپی لینک">
552
- <span class="material-symbols-rounded">link</span>
553
- </button>
554
- <button class="icon-btn" onclick="UI.toggleSidebar()" style="display: none;" id="mobile-menu-btn">
555
- <span class="material-symbols-rounded">more_vert</span>
556
  </button>
557
- </header>
558
-
559
- <div class="chat-messages" id="messages-container">
560
- <!-- Messages injected via JS -->
561
- <div style="text-align: center; color: var(--text-secondary); margin-top: 50px;">
562
- اتاقی را برای شروع گفتگو انتخاب کنید
563
  </div>
 
 
 
564
  </div>
565
 
566
- <div class="reply-bar" id="reply-bar">
567
- <span id="reply-text-preview">پاسخ به...</span>
568
- <span class="material-symbols-rounded icon-btn" onclick="Chat.cancelReply()" style="color: var(--danger-color);">close</span>
569
  </div>
570
 
571
- <div class="chat-input-area" id="input-area">
572
- <button class="icon-btn">
573
- <span class="material-symbols-rounded">attach_file</span>
574
  </button>
575
- <textarea id="msg-input" rows="1" placeholder="پیامی بنویسید..."></textarea>
576
- <button class="icon-btn send-btn" onclick="Chat.sendMessage()">
577
- <span class="material-symbols-rounded">send</span>
 
 
 
 
 
 
578
  </button>
579
  </div>
580
  </section>
581
 
582
- </main>
583
 
584
  <!-- Modals -->
585
- <div class="modal-overlay" id="create-room-modal">
586
- <div class="modal-content">
587
- <h3 class="modal-title">ایجاد اتاق جدید</h3>
588
- <div class="input-group">
589
- <input type="text" id="new-room-name" placeholder="نام اتاق">
590
- </div>
591
- <div class="modal-actions">
592
- <button class="btn btn-outline btn-sm" onclick="UI.closeModal('create-room-modal')">لغو</button>
593
- <button class="btn btn-sm" onclick="Rooms.createRoom()">ایجاد</button>
594
  </div>
595
  </div>
596
  </div>
597
 
598
- <!-- Context Menu -->
599
- <div class="context-menu" id="context-menu">
600
- <div class="ctx-item" onclick="Chat.startReply()">
601
- <span class="material-symbols-rounded">reply</span> پاسخ
602
- </div>
603
- <div class="ctx-item" onclick="Reactions.showPicker()">
604
- <span class="material-symbols-rounded">sentiment_satisfied_alt</span> واکنش
605
- </div>
606
- <div class="ctx-item delete" onclick="Chat.deleteMessage()">
607
- <span class="material-symbols-rounded">delete</span> حذف
 
 
 
 
 
608
  </div>
609
  </div>
610
 
611
- <!-- Reaction Picker -->
612
- <div class="reaction-picker" id="reaction-picker">
613
- <span class="emoji-opt" onclick="Reactions.add('❤️')">❤️</span>
614
- <span class="emoji-opt" onclick="Reactions.add('👍')">👍</span>
615
- <span class="emoji-opt" onclick="Reactions.add('😂')">😂</span>
616
- <span class="emoji-opt" onclick="Reactions.add('😮')">😮</span>
617
- <span class="emoji-opt" onclick="Reactions.add('😢')">😢</span>
618
- <span class="emoji-opt" onclick="Reactions.add('😡')">😡</span>
 
 
 
 
 
619
  </div>
620
 
621
  <!-- Toast -->
622
- <div class="toast" id="toast">
623
- <span class="material-symbols-rounded">info</span>
624
- <span id="toast-msg">پیام سیستم</span>
625
- </div>
626
 
627
  <script>
628
- /**
629
- * 🚀 MINIMAL CHAT ROOM - SINGLE FILE IMPLEMENTATION
630
- * Modules: Store, Auth, UI, Rooms, Chat, Gestures, Reactions
631
- */
632
-
633
- // --- STORE (State Management) ---
634
- const Store = {
635
- user: { phone: '', name: '' },
636
  activeRoomId: null,
637
- rooms: [
638
- { id: 1, name: 'گروه عمومی', lastMsg: 'به چت روم خوش آمدید', avatar: 'ع' },
639
- { id: 2, name: 'دوستان', lastMsg: 'کجایی؟', avatar: 'د' }
640
- ],
641
- messages: {
642
- 1: [
643
- { id: 101, text: 'سلام به همه!', sender: 'other', time: '10:00', reactions: [] },
644
- { id: 102, text: 'سلام، حالتون چطوره؟', sender: 'self', time: '10:05', reactions: [{emoji:'👍', count:1}] }
645
- ],
646
- 2: [
647
- { id: 201, text: 'امشب میای؟', sender: 'other', time: '09:30', reactions: [] }
648
- ]
 
 
 
 
 
 
 
 
649
  },
650
- selectedMessageId: null, // For context menu actions
651
- replyToId: null
 
 
 
 
 
 
 
 
652
  };
653
 
654
- // --- AUTH MODULE ---
655
  const Auth = {
656
- login: () => {
657
- const input = document.getElementById('mobile-input');
658
- const phone = input.value;
659
-
660
- if (phone.length < 10) {
661
- UI.showToast('شماره موبایل نامعتبر است');
662
  return;
663
  }
664
-
665
- // Simulate API call and OTP
666
- const otp = prompt('کد تایید (۱۲۳۴):');
667
- if (otp === '1234') {
668
- Store.user.phone = phone;
669
- Store.user.name = 'کاربر ' + phone.slice(-4);
670
-
671
- document.getElementById('user-display-name').textContent = Store.user.name;
672
-
673
- // Transition
674
- document.getElementById('auth-screen').style.transform = 'translateY(-100%)';
675
- setTimeout(() => {
676
- document.getElementById('auth-screen').style.display = 'none';
677
- document.getElementById('app-container').classList.add('active');
678
- Rooms.renderList();
679
- }, 500);
680
  } else {
681
- UI.showToast('کد اشتباه است');
 
 
 
 
 
 
 
 
 
 
 
682
  }
683
  }
684
  };
685
 
686
- // --- UI MODULE ---
687
- const UI = {
688
- toggleSidebar: () => {
689
- const sidebar = document.getElementById('sidebar');
690
- sidebar.classList.toggle('open');
691
- },
 
 
 
 
692
 
693
- showToast: (msg) => {
694
- const toast = document.getElementById('toast');
695
- document.getElementById('toast-msg').textContent = msg;
696
- toast.classList.add('show');
697
- setTimeout(() => toast.classList.remove('show'), 3000);
698
- },
699
 
700
- openModal: (id) => {
701
- document.getElementById(id).classList.add('active');
702
- UI.closeContextMenu();
 
 
 
 
 
703
  },
704
-
705
- closeModal: (id) => {
706
- document.getElementById(id).classList.remove('active');
 
 
 
707
  },
708
-
709
- closeContextMenu: () => {
710
- document.getElementById('context-menu').style.display = 'none';
711
- document.getElementById('reaction-picker').style.display = 'none';
712
- }
713
- };
714
-
715
- // --- ROOMS MODULE ---
716
- const Rooms = {
717
- renderList: () => {
718
  const container = document.getElementById('room-list-container');
719
  container.innerHTML = '';
 
 
 
 
 
720
 
721
- Store.rooms.forEach(room => {
722
- const div = document.createElement('div');
723
- div.className = `room-item ${Store.activeRoomId === room.id ? 'active' : ''}`;
724
- div.onclick = () => Rooms.joinRoom(room.id);
725
- div.innerHTML = `
726
- <div class="avatar">${room.avatar}</div>
727
  <div class="room-info">
728
  <div class="room-name">${room.name}</div>
729
  <div class="room-last-msg">${room.lastMsg}</div>
730
  </div>
 
731
  `;
732
- container.appendChild(div);
733
  });
734
- },
 
735
 
736
- joinRoom: (id) => {
737
- Store.activeRoomId = id;
738
- const room = Store.rooms.find(r => r.id === id);
739
-
740
- document.getElementById('current-room-name').textContent = room.name;
741
- document.getElementById('current-room-avatar').textContent = room.avatar;
 
 
 
 
 
 
 
 
 
 
 
 
742
 
743
- // Close sidebar on mobile
744
- document.getElementById('sidebar').classList.remove('open');
 
745
 
746
- Rooms.renderList(); // Update active state class
747
- Chat.renderMessages();
748
- },
 
749
 
750
- showCreateModal: () => {
751
- if (Store.rooms.length >= 3) {
752
- UI.showToast('شما حداکثر ظرفیت اتاق (۳) را پر کرده‌اید');
753
- return;
754
- }
755
- UI.openModal('create-room-modal');
756
  },
757
-
758
- createRoom: () => {
759
- const nameInput = document.getElementById('new-room-name');
760
- const name = nameInput.value.trim();
761
- if (!name) return;
762
-
763
- const newId = Date.now();
764
- Store.rooms.push({
765
- id: newId,
766
- name: name,
767
- lastMsg: 'اتاق ساخته شد',
768
- avatar: name.charAt(0)
769
- });
770
- Store.messages[newId] = [];
771
-
772
- UI.closeModal('create-room-modal');
773
- nameInput.value = '';
774
- Rooms.renderList();
775
- Rooms.joinRoom(newId);
776
- UI.showToast('اتاق جدید ساخته شد');
777
- }
778
- };
779
-
780
- // --- CHAT MODULE ---
781
- const Chat = {
782
- renderMessages: () => {
783
- const container = document.getElementById('messages-container');
784
- container.innerHTML = '';
785
 
786
- const msgs = Store.messages[Store.activeRoomId] || [];
 
 
 
 
 
 
 
787
 
788
- if (msgs.length === 0) {
789
- container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); margin-top: 50px;">پیامی نیست. شروع کنید!</div>';
790
- return;
791
- }
 
 
 
 
 
 
 
792
 
793
  msgs.forEach(msg => {
794
- const div = document.createElement('div');
795
- div.className = `message ${msg.sender}`;
796
- div.dataset.id = msg.id;
797
 
798
- // Reply HTML
799
- let replyHtml = '';
 
 
 
 
 
800
  if (msg.replyTo) {
801
- const originalMsg = msgs.find(m => m.id === msg.replyTo);
802
- if(originalMsg) {
803
- replyHtml = `<div class="reply-preview">${originalMsg.text}</div>`;
804
- }
 
 
805
  }
806
 
807
- // Reactions HTML
808
- let reactionsHtml = '';
 
809
  if (msg.reactions && msg.reactions.length > 0) {
810
- reactionsHtml = `<div class="reactions">
811
- ${msg.reactions.map(r => `<span class="reaction-badge">${r.emoji} ${r.count}</span>`).join('')}
812
- </div>`;
 
 
 
813
  }
814
 
815
- div.innerHTML = `
816
- <div class="bubble">
817
- ${replyHtml}
818
- ${msg.text}
819
- <div class="msg-meta">
820
- ${msg.time}
821
- ${msg.sender === 'self' ? '<span class="material-symbols-rounded" style="font-size:12px">done_all</span>' : ''}
822
- </div>
823
- ${reactionsHtml}
824
  </div>
825
  `;
826
-
827
- // Attach Events for Gestures
828
- Gestures.attach(div, msg.id);
829
-
830
- container.appendChild(div);
831
  });
832
 
833
- // Scroll to bottom
834
  container.scrollTop = container.scrollHeight;
835
  },
836
-
837
- sendMessage: () => {
838
- const input = document.getElementById('msg-input');
839
- const text = input.value.trim();
840
 
841
- if (!text || !Store.activeRoomId) return;
842
-
843
- const newMsg = {
844
- id: Date.now(),
845
- text: text,
846
- sender: 'self',
847
- time: new Date().toLocaleTimeString('fa-IR', {hour: '2-digit', minute:'2-digit'}),
848
- replyTo: Store.replyToId,
849
- reactions: []
850
- };
851
-
852
- Store.messages[Store.activeRoomId].push(newMsg);
853
 
854
- // Update last message in sidebar
855
- const roomIdx = Store.rooms.findIndex(r => r.id === Store.activeRoomId);
856
- if(roomIdx > -1) Store.rooms[roomIdx].lastMsg = text;
857
-
858
- input.value = '';
859
- Chat.cancelReply();
860
- Chat.renderMessages();
861
- Rooms.renderList();
862
-
863
- // Simulate Reply from Bot
864
- if (Math.random() > 0.5) {
865
- setTimeout(() => {
866
- const botMsg = {
867
- id: Date.now() + 1,
868
- text: 'این یک پاسخ خودکار است 🤖',
869
- sender: 'other',
870
- time: new Date().toLocaleTimeString('fa-IR', {hour: '2-digit', minute:'2-digit'}),
871
- reactions: []
872
- };
873
- Store.messages[Store.activeRoomId].push(botMsg);
874
- Store.rooms[roomIdx].lastMsg = botMsg.text;
875
- Chat.renderMessages();
876
- Rooms.renderList();
877
- }, 1500);
878
- }
879
  },
880
-
881
- deleteMessage: () => {
882
- if (!Store.selectedMessageId) return;
883
-
884
- const msgs = Store.messages[Store.activeRoomId];
885
- const idx = msgs.findIndex(m => m.id === Store.selectedMessageId);
886
-
 
887
  if (idx > -1) {
888
  msgs.splice(idx, 1);
889
- Chat.renderMessages();
890
- UI.showToast('پیام حذف شد');
 
891
  }
892
- UI.closeContextMenu();
893
  },
894
-
895
- startReply: () => {
896
- Store.replyToId = Store.selectedMessageId;
897
- const msgs = Store.messages[Store.activeRoomId];
898
- const msg = msgs.find(m => m.id === Store.selectedMessageId);
899
-
 
 
 
900
  if (msg) {
901
- document.getElementById('reply-bar').style.display = 'flex';
902
- document.getElementById('reply-text-preview').textContent = `پاسخ به: ${msg.text.substring(0, 20)}...`;
903
- document.getElementById('msg-input').focus();
 
904
  }
905
- UI.closeContextMenu();
 
906
  },
 
 
907
 
908
- cancelReply: () => {
909
- Store.replyToId = null;
910
- document.getElementById('reply-bar').style.display = 'none';
 
 
911
  },
912
-
913
- copyLink: () => {
914
- const link = `https://chat.app/room/${Store.activeRoomId}`;
915
- navigator.clipboard.writeText(link).then(() => {
916
- UI.showToast('لینک اتاق کپی شد');
917
- });
 
 
 
 
 
918
  }
919
  };
920
 
921
- // --- REACTIONS MODULE ---
922
- const Reactions = {
923
- showPicker: () => {
924
- const menu = document.getElementById('context-menu');
925
- const picker = document.getElementById('reaction-picker');
926
-
927
- // Position picker near menu
928
- const rect = menu.getBoundingClientRect();
929
- picker.style.top = (rect.top - 50) + 'px';
930
- picker.style.left = rect.left + 'px';
931
- picker.style.display = 'flex';
932
- menu.style.display = 'none';
933
- },
934
 
935
- add: (emoji) => {
936
- const msgs = Store.messages[Store.activeRoomId];
937
- const msg = msgs.find(m => m.id === Store.selectedMessageId);
 
 
 
 
 
 
938
 
939
- if (msg) {
940
- // Check if reaction exists
941
- const existing = msg.reactions.find(r => r.emoji === emoji);
942
- if (existing) {
943
- existing.count++;
944
- } else {
945
- msg.reactions.push({ emoji: emoji, count: 1 });
946
  }
947
- Chat.renderMessages();
948
  }
949
- UI.closeContextMenu();
950
- document.getElementById('reaction-picker').style.display = 'none';
951
- }
952
- };
953
-
954
- // --- GESTURES MODULE (Touch/Long Press) ---
955
- const Gestures = {
956
- attach: (element, msgId) => {
957
- let pressTimer;
958
- const menu = document.getElementById('context-menu');
959
-
960
- const start = (e) => {
961
- // Ignore if clicking links or buttons inside
962
- if (e.target.tagName === 'BUTTON' || e.target.closest('button')) return;
963
-
964
- pressTimer = setTimeout(() => {
965
- Store.selectedMessageId = msgId;
966
-
967
- // Calculate position
968
- const touch = e.touches ? e.touches[0] : e;
969
- menu.style.top = (touch.clientY - 20) + 'px';
970
- menu.style.left = (touch.clientX - 70) + 'px';
971
- menu.style.display = 'flex';
972
-
973
- // Haptic feedback if available
974
- if (navigator.vibrate) navigator.vibrate(50);
975
- }, 500); // 500ms long press
976
- };
977
 
978
- const cancel = () => {
979
- clearTimeout(pressTimer);
980
- };
 
 
 
 
 
 
 
981
 
982
- element.addEventListener('touchstart', start);
983
- element.addEventListener('touchend', cancel);
984
- element.addEventListener('touchmove', cancel);
985
-
986
- // Mouse events for desktop testing
987
- element.addEventListener('mousedown', start);
988
- element.addEventListener('mouseup', cancel);
989
- element.addEventListener('mouseleave', cancel);
990
  }
991
  };
992
 
993
- // --- INITIALIZATION ---
994
- // Close menus when clicking outside
995
- document.addEventListener('click', (e) => {
996
- const menu = document.getElementById('context-menu');
997
- const picker = document.getElementById('reaction-picker');
998
- if (!menu.contains(e.target) && !picker.contains(e.target)) {
999
- UI.closeContextMenu();
1000
- }
 
 
1001
  });
1002
 
1003
- // Handle Enter key to send
1004
- document.getElementById('msg-input').addEventListener('keypress', (e) => {
1005
- if (e.key === 'Enter' && !e.shiftKey) {
1006
- e.preventDefault();
1007
- Chat.sendMessage();
1008
- }
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
  <title>چت روم مینیمال | Minimal Chat</title>
7
 
8
+ <!-- فونت وزیرمتن -->
9
  <link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
10
+ <!-- آیکون‌ها -->
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
 
13
  <style>
14
  :root {
15
+ --primary-color: #6c5ce7;
16
+ --secondary-color: #a29bfe;
17
+ --bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ --glass-bg: rgba(255, 255, 255, 0.15);
19
  --glass-border: rgba(255, 255, 255, 0.2);
20
  --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
 
 
21
  --text-color: #ffffff;
22
+ --text-secondary: #e0e0e0;
23
+ --danger: #ff7675;
24
+ --success: #00b894;
25
+ --msg-me: rgba(108, 92, 231, 0.6);
26
+ --msg-other: rgba(255, 255, 255, 0.1);
27
  }
28
 
29
  * {
30
  box-sizing: border-box;
31
  margin: 0;
32
  padding: 0;
33
+ outline: none;
34
  -webkit-tap-highlight-color: transparent;
35
  }
36
 
37
  body {
38
+ font-family: 'Vazirmatn', sans-serif;
39
+ background: var(--bg-gradient);
40
  color: var(--text-color);
41
  height: 100vh;
42
  overflow: hidden;
 
45
  align-items: center;
46
  }
47
 
48
+ /* --- Glassmorphism Utilities --- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  .glass {
50
  background: var(--glass-bg);
51
  backdrop-filter: blur(12px);
52
  -webkit-backdrop-filter: blur(12px);
53
  border: 1px solid var(--glass-border);
54
  box-shadow: var(--glass-shadow);
55
+ border-radius: 16px;
56
  }
57
 
58
  .glass-panel {
59
+ background: rgba(0, 0, 0, 0.2);
60
+ border-radius: 12px;
61
+ padding: 10px;
62
  }
63
 
64
+ /* --- App Container --- */
65
+ #app {
66
+ width: 100%;
67
+ height: 100%;
68
+ max-width: 480px; /* Mobile view simulation on desktop */
69
+ position: relative;
70
+ overflow: hidden;
71
+ background: rgba(255, 255, 255, 0.05);
72
+ }
73
+
74
+ @media (min-width: 481px) {
75
+ #app {
76
+ height: 95vh;
77
+ border-radius: 24px;
78
+ }
79
+ }
80
+
81
+ /* --- Screens --- */
82
+ .screen {
83
+ position: absolute;
84
+ top: 0;
85
+ left: 0;
86
  width: 100%;
 
87
  height: 100%;
88
  display: flex;
89
  flex-direction: column;
90
+ transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1), opacity 0.3s;
91
+ opacity: 0;
92
+ pointer-events: none;
93
+ transform: scale(0.95);
94
+ z-index: 1;
95
+ }
96
+
97
+ .screen.active {
98
+ opacity: 1;
99
+ pointer-events: all;
100
+ transform: scale(1);
101
+ z-index: 10;
102
+ }
103
+
104
+ /* --- Header --- */
105
+ header {
106
+ padding: 15px;
107
+ display: flex;
108
+ justify-content: space-between;
109
  align-items: center;
110
+ z-index: 20;
 
111
  }
112
 
113
+ .logo {
114
+ font-weight: bold;
115
+ font-size: 1.2rem;
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 10px;
119
  }
120
 
121
+ .anycoder-link {
122
+ font-size: 0.7rem;
123
+ color: rgba(255,255,255,0.6);
124
+ text-decoration: none;
 
125
  }
126
 
127
+ /* --- Auth Screen --- */
128
+ #auth-screen {
129
+ justify-content: center;
130
+ align-items: center;
131
+ text-align: center;
132
+ padding: 30px;
133
+ }
134
 
135
+ .auth-input {
136
  width: 100%;
137
+ padding: 15px;
138
+ margin: 10px 0;
139
  border-radius: 12px;
140
  border: 1px solid var(--glass-border);
141
+ background: rgba(255, 255, 255, 0.1);
142
  color: white;
 
143
  font-size: 1rem;
144
+ text-align: center;
145
  transition: 0.3s;
146
  }
147
 
148
+ .auth-input:focus {
149
+ background: rgba(255, 255, 255, 0.2);
150
+ border-color: var(--secondary-color);
151
+ }
152
 
153
  .btn {
154
+ padding: 12px 24px;
 
 
155
  border-radius: 12px;
156
+ border: none;
157
  background: var(--primary-color);
158
  color: white;
159
+ font-family: inherit;
160
  font-weight: bold;
 
161
  cursor: pointer;
162
+ transition: transform 0.1s, background 0.3s;
163
+ display: inline-flex;
164
  align-items: center;
165
  justify-content: center;
166
  gap: 8px;
167
  }
168
 
169
+ .btn:active {
170
+ transform: scale(0.96);
 
 
 
 
 
 
 
 
 
 
 
 
171
  }
172
 
173
+ .btn-full {
174
+ width: 100%;
175
+ margin-top: 20px;
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
+ .btn-icon {
179
+ width: 40px;
180
+ height: 40px;
181
+ padding: 0;
182
+ border-radius: 50%;
 
183
  }
184
 
185
+ /* --- Rooms List --- */
186
+ #rooms-screen {
187
+ padding-top: 60px; /* Space for header */
 
 
 
 
188
  }
189
 
190
  .room-list {
191
  flex: 1;
192
  overflow-y: auto;
193
+ padding: 15px;
194
  }
195
 
196
+ .room-card {
197
  display: flex;
198
  align-items: center;
199
+ padding: 15px;
200
+ margin-bottom: 12px;
 
201
  cursor: pointer;
202
+ transition: 0.2s;
203
  }
204
 
205
+ .room-card:active {
206
+ background: rgba(255, 255, 255, 0.1);
207
+ transform: scale(0.98);
208
+ }
209
+
210
+ .room-avatar {
211
+ width: 50px;
212
+ height: 50px;
213
  border-radius: 50%;
214
+ margin-left: 15px;
215
+ object-fit: cover;
216
+ border: 2px solid var(--secondary-color);
217
+ }
218
+
219
+ .room-info {
220
+ flex: 1;
221
+ }
222
+
223
+ .room-name {
224
  font-weight: bold;
225
+ font-size: 1.1rem;
226
+ margin-bottom: 4px;
227
  }
228
 
229
+ .room-last-msg {
230
+ font-size: 0.85rem;
231
+ color: var(--text-secondary);
232
+ white-space: nowrap;
233
+ overflow: hidden;
234
+ text-overflow: ellipsis;
235
+ max-width: 200px;
236
+ }
237
 
238
+ .fab {
239
+ position: absolute;
240
+ bottom: 30px;
241
+ left: 30px;
242
+ width: 60px;
243
+ height: 60px;
244
+ border-radius: 50%;
245
+ background: var(--success);
246
+ color: white;
247
  display: flex;
248
+ justify-content: center;
249
+ align-items: center;
250
+ font-size: 1.5rem;
251
+ box-shadow: 0 4px 15px rgba(0,0,0,0.3);
252
+ cursor: pointer;
253
+ z-index: 100;
254
+ transition: transform 0.2s;
255
+ }
256
+
257
+ .fab:hover {
258
+ transform: rotate(90deg);
259
+ }
260
+
261
+ /* --- Chat Screen --- */
262
+ #chat-screen {
263
+ padding-top: 0;
264
  }
265
 
266
  .chat-header {
267
+ height: 70px;
268
  display: flex;
269
  align-items: center;
270
+ padding: 0 10px;
271
  border-bottom: 1px solid var(--glass-border);
 
272
  }
273
 
274
+ .chat-body {
 
 
275
  flex: 1;
276
  overflow-y: auto;
277
+ padding: 15px;
278
  display: flex;
279
  flex-direction: column;
280
+ gap: 10px;
281
  }
282
 
 
283
  .message {
284
  max-width: 75%;
285
+ padding: 10px 14px;
 
 
 
 
 
 
 
 
 
 
 
286
  border-radius: 18px;
287
+ position: relative;
288
+ animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
289
  font-size: 0.95rem;
290
  line-height: 1.5;
 
 
291
  }
292
 
293
+ @keyframes popIn {
294
+ from { opacity: 0; transform: translateY(10px) scale(0.9); }
295
+ to { opacity: 1; transform: translateY(0) scale(1); }
296
  }
297
 
298
+ .message.me {
299
+ align-self: flex-start; /* RTL: Start is Right */
300
+ background: var(--msg-me);
301
  border-bottom-right-radius: 4px;
302
  }
303
 
304
+ .message.other {
305
+ align-self: flex-end; /* RTL: End is Left */
306
+ background: var(--msg-other);
307
+ border-bottom-left-radius: 4px;
308
+ }
309
+
310
  .msg-meta {
311
  font-size: 0.7rem;
 
312
  opacity: 0.7;
313
+ margin-top: 4px;
314
+ text-align: left; /* Always left for time in bubbles usually, or right in RTL */
315
  display: flex;
 
316
  justify-content: flex-end;
317
+ gap: 5px;
318
+ align-items: center;
319
  }
320
 
321
  .reply-preview {
322
+ background: rgba(0,0,0,0.2);
323
+ padding: 5px 8px;
324
+ border-radius: 8px;
325
+ margin-bottom: 8px;
326
  font-size: 0.8rem;
327
+ border-right: 3px solid var(--secondary-color);
328
+ display: flex;
329
+ flex-direction: column;
 
 
 
 
 
330
  }
331
 
332
+ .reply-owner {
333
+ font-size: 0.7rem;
334
+ font-weight: bold;
335
+ color: var(--secondary-color);
 
336
  }
337
+
338
+ .message-actions {
339
+ position: absolute;
340
+ top: -30px;
341
+ left: 0;
342
+ background: rgba(0,0,0,0.8);
343
+ border-radius: 20px;
344
+ padding: 5px;
345
+ display: none; /* Shown on long press */
346
+ gap: 10px;
347
+ white-space: nowrap;
348
+ }
349
+
350
+ .message.selected .message-actions {
351
  display: flex;
 
 
352
  }
353
 
354
+ .chat-footer {
355
+ padding: 10px;
356
+ background: rgba(0, 0, 0, 0.2);
 
357
  display: flex;
358
  align-items: flex-end;
359
  gap: 10px;
360
+ }
361
+
362
+ .chat-input-wrapper {
363
+ flex: 1;
364
+ background: rgba(255, 255, 255, 0.1);
365
+ border-radius: 20px;
366
+ padding: 8px 15px;
367
+ display: flex;
368
+ flex-direction: column;
369
  }
370
 
371
  .reply-bar {
 
 
 
 
 
 
 
 
372
  display: none;
373
+ border-bottom: 1px solid rgba(255,255,255,0.1);
374
+ padding-bottom: 5px;
375
+ margin-bottom: 5px;
376
+ font-size: 0.8rem;
377
+ color: var(--secondary-color);
378
  justify-content: space-between;
379
  align-items: center;
380
  }
381
 
382
+ .chat-input {
383
+ width: 100%;
384
+ background: transparent;
385
  border: none;
 
 
386
  color: white;
 
387
  resize: none;
388
  max-height: 100px;
389
+ min-height: 24px;
390
+ font-family: inherit;
391
  }
392
 
393
+ /* --- Reactions --- */
394
+ .reaction-bar {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  display: flex;
 
396
  gap: 5px;
397
+ margin-top: 5px;
398
+ justify-content: flex-end;
 
 
399
  }
400
 
401
+ .reaction-badge {
402
+ font-size: 0.7rem;
403
+ background: rgba(0,0,0,0.3);
404
+ border-radius: 10px;
405
+ padding: 2px 6px;
406
  display: flex;
407
  align-items: center;
408
+ gap: 2px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  }
410
 
411
+ /* --- Modals & Overlays --- */
 
 
 
 
 
412
  .modal-overlay {
413
  position: fixed;
414
+ top: 0;
415
+ left: 0;
416
+ width: 100%;
417
+ height: 100%;
418
  background: rgba(0,0,0,0.6);
419
  backdrop-filter: blur(5px);
420
+ z-index: 1000;
421
+ display: flex;
422
  justify-content: center;
423
  align-items: center;
424
+ opacity: 0;
425
+ pointer-events: none;
426
+ transition: 0.3s;
427
  }
428
 
429
+ .modal-overlay.open {
430
+ opacity: 1;
431
+ pointer-events: all;
432
+ }
433
 
434
+ .modal {
435
+ width: 85%;
436
  max-width: 350px;
 
 
 
437
  padding: 20px;
438
+ transform: translateY(20px);
439
+ transition: 0.3s;
440
+ }
441
+
442
+ .modal-overlay.open .modal {
443
+ transform: translateY(0);
444
+ }
445
+
446
+ .modal h3 {
447
+ margin-bottom: 15px;
448
  text-align: center;
 
 
449
  }
450
 
451
+ .reaction-picker {
452
+ display: flex;
453
+ justify-content: space-around;
454
+ margin-top: 15px;
455
+ }
456
+
457
+ .emoji-btn {
458
+ font-size: 1.5rem;
459
+ background: none;
460
+ border: none;
461
+ cursor: pointer;
462
+ transition: transform 0.2s;
463
+ }
464
 
465
+ .emoji-btn:hover {
466
+ transform: scale(1.3);
467
+ }
468
 
469
+ /* --- Toast Notification --- */
470
+ #toast {
471
  position: fixed;
472
+ bottom: 80px;
473
  left: 50%;
474
+ transform: translateX(-50%) translateY(20px);
475
  background: rgba(0,0,0,0.8);
476
  color: white;
477
  padding: 10px 20px;
478
+ border-radius: 20px;
479
  font-size: 0.9rem;
480
+ opacity: 0;
481
+ transition: 0.3s;
482
+ pointer-events: none;
483
+ z-index: 2000;
484
+ white-space: nowrap;
485
  }
 
486
 
487
+ #toast.show {
488
+ opacity: 1;
489
+ transform: translateX(-50%) translateY(0);
490
+ }
491
+
492
+ /* Scrollbar */
493
+ ::-webkit-scrollbar {
494
+ width: 6px;
495
+ }
496
+ ::-webkit-scrollbar-track {
497
+ background: transparent;
498
+ }
499
+ ::-webkit-scrollbar-thumb {
500
+ background: rgba(255,255,255,0.2);
501
+ border-radius: 3px;
502
  }
503
  </style>
504
  </head>
505
  <body>
506
 
507
+ <div id="app" class="glass">
508
+
509
+ <!-- Header (Global) -->
510
+ <header class="glass-panel" style="margin: 10px; border-radius: 12px; position: absolute; top:0; width: calc(100% - 20px); z-index: 50;">
511
+ <div class="logo">
512
+ <i class="fa-solid fa-comments"></i>
513
+ <span>مینیمال چت</span>
 
 
 
 
514
  </div>
515
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
516
+ Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i>
517
+ </a>
518
+ </header>
519
+
520
+ <!-- 1. Auth Screen -->
521
+ <section id="auth-screen" class="screen active">
522
+ <div style="text-align: center; margin-bottom: 30px;">
523
+ <i class="fa-solid fa-lock" style="font-size: 3rem; margin-bottom: 20px; opacity: 0.8;"></i>
524
+ <h2>ورود به حساب</h2>
525
+ <p style="opacity: 0.7; font-size: 0.9rem;">برای شروع شماره موبایل خود را وارد کنید</p>
526
+ </div>
527
+
528
+ <div id="step-phone">
529
+ <input type="tel" id="phone-input" class="auth-input" placeholder="0912..." maxlength="11">
530
+ <button class="btn btn-full" onclick="Auth.sendCode()">دریافت کد تایید</button>
531
  </div>
 
 
532
 
533
+ <div id="step-code" style="display: none;">
534
+ <input type="number" id="code-input" class="auth-input" placeholder="کد 4 رقمی (تست: 1234)" maxlength="4">
535
+ <button class="btn btn-full" onclick="Auth.verifyCode()">تایید و ورود</button>
536
+ <p style="margin-top: 15px; font-size: 0.8rem; cursor: pointer; opacity: 0.8;" onclick="Auth.reset()">ویرایش شماره</p>
 
 
 
 
 
 
 
 
 
537
  </div>
538
+ </section>
539
 
540
+ <!-- 2. Rooms List Screen -->
541
+ <section id="rooms-screen" class="screen">
542
  <div class="room-list" id="room-list-container">
543
+ <!-- Rooms injected here -->
544
  </div>
545
+
546
+ <!-- Create Room FAB -->
547
+ <div class="fab" onclick="UI.openModal('create-room-modal')">
548
+ <i class="fa-solid fa-plus"></i>
 
 
549
  </div>
550
+ </section>
551
 
552
+ <!-- 3. Chat Screen -->
553
+ <section id="chat-screen" class="screen">
554
+ <div class="chat-header glass-panel" style="border-radius: 0; margin: 0; width: 100%;">
555
+ <button class="btn btn-icon" onclick="UI.showScreen('rooms-screen')">
556
+ <i class="fa-solid fa-arrow-right"></i>
 
 
 
 
 
 
 
 
 
 
 
557
  </button>
558
+ <div class="room-info" style="margin-right: 10px; margin-left: 10px;">
559
+ <div class="room-name" id="chat-title">نام اتاق</div>
560
+ <div style="font-size: 0.7rem; opacity: 0.7;">آنلاین</div>
 
 
 
561
  </div>
562
+ <button class="btn btn-icon" onclick="Chat.copyLink()">
563
+ <i class="fa-solid fa-link"></i>
564
+ </button>
565
  </div>
566
 
567
+ <div class="chat-body" id="chat-container" ontouchstart="Gestures.handleTouchStart(event)" ontouchend="Gestures.handleTouchEnd(event)">
568
+ <!-- Messages injected here -->
 
569
  </div>
570
 
571
+ <div class="chat-footer">
572
+ <button class="btn btn-icon" style="background: rgba(255,255,255,0.1);" onclick="UI.openModal('reactions-modal')">
573
+ <i class="fa-regular fa-face-smile"></i>
574
  </button>
575
+ <div class="chat-input-wrapper">
576
+ <div class="reply-bar" id="reply-bar">
577
+ <span id="reply-text-preview">پاسخ به...</span>
578
+ <i class="fa-solid fa-xmark" style="cursor: pointer;" onclick="Chat.cancelReply()"></i>
579
+ </div>
580
+ <textarea id="message-input" class="chat-input" rows="1" placeholder="پیام خود را بنویسید..."></textarea>
581
+ </div>
582
+ <button class="btn btn-icon" style="background: var(--primary-color);" onclick="Chat.sendMessage()">
583
+ <i class="fa-solid fa-paper-plane"></i>
584
  </button>
585
  </div>
586
  </section>
587
 
588
+ </div>
589
 
590
  <!-- Modals -->
591
+
592
+ <!-- Create Room Modal -->
593
+ <div id="create-room-modal" class="modal-overlay">
594
+ <div class="modal glass">
595
+ <h3>ایجاد اتاق جدید</h3>
596
+ <input type="text" id="new-room-name" class="auth-input" placeholder="نام اتاق">
597
+ <div style="display: flex; gap: 10px; margin-top: 20px;">
598
+ <button class="btn" style="flex: 1; background: rgba(255,255,255,0.2);" onclick="UI.closeModal('create-room-modal')">لغو</button>
599
+ <button class="btn" style="flex: 1; background: var(--success);" onclick="RoomManager.createRoom()">ساخت</button>
600
  </div>
601
  </div>
602
  </div>
603
 
604
+ <!-- Reaction Picker Modal (Contextual) -->
605
+ <div id="reactions-modal" class="modal-overlay">
606
+ <div class="modal glass">
607
+ <h3>انتخاب ایموجی</h3>
608
+ <div class="reaction-picker">
609
+ <button class="emoji-btn" onclick="Chat.addReaction('❤️')">❤️</button>
610
+ <button class="emoji-btn" onclick="Chat.addReaction('👍')">👍</button>
611
+ <button class="emoji-btn" onclick="Chat.addReaction('😂')">😂</button>
612
+ <button class="emoji-btn" onclick="Chat.addReaction('😮')">😮</button>
613
+ <button class="emoji-btn" onclick="Chat.addReaction('😢')">😢</button>
614
+ <button class="emoji-btn" onclick="Chat.addReaction('😡')">😡</button>
615
+ </div>
616
+ <div style="text-align: center; margin-top: 15px;">
617
+ <button class="btn" style="background: rgba(255,255,255,0.2); font-size: 0.8rem; padding: 5px 15px;" onclick="UI.closeModal('reactions-modal')">انصراف</button>
618
+ </div>
619
  </div>
620
  </div>
621
 
622
+ <!-- Context Menu (Long Press) -->
623
+ <div id="context-menu" class="modal-overlay" style="z-index: 3000;">
624
+ <div class="modal glass" style="position: absolute; bottom: 100px; width: auto; min-width: 150px; padding: 5px;">
625
+ <button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="Chat.replyToMsg()">
626
+ <i class="fa-solid fa-reply"></i> پاسخ
627
+ </button>
628
+ <button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="Chat.deleteMsg()">
629
+ <i class="fa-solid fa-trash"></i> حذف
630
+ </button>
631
+ <button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="UI.closeModal('context-menu')">
632
+ <i class="fa-solid fa-xmark"></i> لغو
633
+ </button>
634
+ </div>
635
  </div>
636
 
637
  <!-- Toast -->
638
+ <div id="toast">پیام سیستم</div>
 
 
 
639
 
640
  <script>
641
+ // --- State Management ---
642
+ const State = {
643
+ user: null,
644
+ rooms: [],
 
 
 
 
645
  activeRoomId: null,
646
+ messages: {}, // { roomId: [msgs] }
647
+ selectedMsgId: null,
648
+ replyToMsgId: null
649
+ };
650
+
651
+ // --- Data Mockup ---
652
+ const MockData = {
653
+ avatars: [
654
+ 'https://picsum.photos/seed/room1/100/100',
655
+ 'https://picsum.photos/seed/room2/100/100',
656
+ 'https://picsum.photos/seed/room3/100/100'
657
+ ]
658
+ };
659
+
660
+ // --- Utility ---
661
+ const Utils = {
662
+ id: () => Math.random().toString(36).substr(2, 9),
663
+ time: () => {
664
+ const d = new Date();
665
+ return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0');
666
  },
667
+ save: () => localStorage.setItem('minimalChatState', JSON.stringify(State)),
668
+ load: () => {
669
+ const data = localStorage.getItem('minimalChatState');
670
+ if (data) {
671
+ const parsed = JSON.parse(data);
672
+ State.user = parsed.user;
673
+ State.rooms = parsed.rooms;
674
+ State.messages = parsed.messages;
675
+ }
676
+ }
677
  };
678
 
679
+ // --- Auth Module ---
680
  const Auth = {
681
+ sendCode: () => {
682
+ const phone = document.getElementById('phone-input').value;
683
+ if (phone.length < 10 || !phone.startsWith('09')) {
684
+ UI.toast('شماره موبایل نامعتبر است');
 
 
685
  return;
686
  }
687
+ UI.toast('کد تایید ارسال شد: 1234');
688
+ document.getElementById('step-phone').style.display = 'none';
689
+ document.getElementById('step-code').style.display = 'block';
690
+ },
691
+ verifyCode: () => {
692
+ const code = document.getElementById('code-input').value;
693
+ if (code === '1234') {
694
+ const phone = document.getElementById('phone-input').value;
695
+ State.user = { phone: phone, name: 'کاربر' };
696
+ Utils.save();
697
+ UI.toast('خوش آمدید!');
698
+ UI.showScreen('rooms-screen');
699
+ RoomManager.render();
 
 
 
700
  } else {
701
+ UI.toast('کد اشتباه است');
702
+ }
703
+ },
704
+ reset: () => {
705
+ document.getElementById('step-phone').style.display = 'block';
706
+ document.getElementById('step-code').style.display = 'none';
707
+ },
708
+ check: () => {
709
+ Utils.load();
710
+ if (State.user) {
711
+ UI.showScreen('rooms-screen');
712
+ RoomManager.render();
713
  }
714
  }
715
  };
716
 
717
+ // --- Room Manager Module ---
718
+ const RoomManager = {
719
+ createRoom: () => {
720
+ const name = document.getElementById('new-room-name').value;
721
+ if (!name) return UI.toast('نام اتاق الزامی است');
722
+
723
+ if (State.rooms.length >= 3) {
724
+ UI.closeModal('create-room-modal');
725
+ return UI.toast('حداکثر ۳ اتاق مجاز است');
726
+ }
727
 
728
+ const newRoom = {
729
+ id: Utils.id(),
730
+ name: name,
731
+ avatar: MockData.avatars[State.rooms.length % 3],
732
+ lastMsg: 'اتاق جدید ایجاد شد'
733
+ };
734
 
735
+ State.rooms.unshift(newRoom);
736
+ State.messages[newRoom.id] = [];
737
+ Utils.save();
738
+
739
+ document.getElementById('new-room-name').value = '';
740
+ UI.closeModal('create-room-modal');
741
+ RoomManager.render();
742
+ UI.toast('اتاق ایجاد شد');
743
  },
744
+ joinRoom: (id) => {
745
+ State.activeRoomId = id;
746
+ const room = State.rooms.find(r => r.id === id);
747
+ document.getElementById('chat-title').innerText = room.name;
748
+ Chat.render();
749
+ UI.showScreen('chat-screen');
750
  },
751
+ render: () => {
 
 
 
 
 
 
 
 
 
752
  const container = document.getElementById('room-list-container');
753
  container.innerHTML = '';
754
+
755
+ if (State.rooms.length === 0) {
756
+ container.innerHTML = '<div style="text-align:center; opacity:0.6; margin-top:50px;">هنوز اتاقی ندارید.<br>برای ساخت دکمه + را بزنید.</div>';
757
+ return;
758
+ }
759
 
760
+ State.rooms.forEach(room => {
761
+ const el = document.createElement('div');
762
+ el.className = 'room-card glass';
763
+ el.onclick = () => RoomManager.joinRoom(room.id);
764
+ el.innerHTML = `
765
+ <img src="${room.avatar}" class="room-avatar" alt="Avatar">
766
  <div class="room-info">
767
  <div class="room-name">${room.name}</div>
768
  <div class="room-last-msg">${room.lastMsg}</div>
769
  </div>
770
+ <div style="font-size:0.7rem; opacity:0.5;">${Utils.time()}</div>
771
  `;
772
+ container.appendChild(el);
773
  });
774
+ }
775
+ };
776
 
777
+ // --- Chat System Module ---
778
+ const Chat = {
779
+ sendMessage: () => {
780
+ const input = document.getElementById('message-input');
781
+ const text = input.value.trim();
782
+ if (!text) return;
783
+
784
+ const msg = {
785
+ id: Utils.id(),
786
+ text: text,
787
+ sender: 'me',
788
+ time: Utils.time(),
789
+ replyTo: State.replyToMsgId ? Chat.findMsg(State.replyToMsgId) : null,
790
+ reactions: []
791
+ };
792
+
793
+ if (!State.messages[State.activeRoomId]) State.messages[State.activeRoomId] = [];
794
+ State.messages[State.activeRoomId].push(msg);
795
 
796
+ // Update last message in room list
797
+ const room = State.rooms.find(r => r.id === State.activeRoomId);
798
+ room.lastMsg = text;
799
 
800
+ Chat.clearReply();
801
+ input.value = '';
802
+ Chat.render();
803
+ Utils.save();
804
 
805
+ // Simulate reply
806
+ setTimeout(() => {
807
+ Chat.receiveMockReply();
808
+ }, 2000);
 
 
809
  },
810
+ receiveMockReply: () => {
811
+ const replies = ['خیلی جالبه!', 'باشه حتما', 'می‌فهمم 😊', 'خوبه', 'فعلاً سرم شلوغه'];
812
+ const text = replies[Math.floor(Math.random() * replies.length)];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
813
 
814
+ const msg = {
815
+ id: Utils.id(),
816
+ text: text,
817
+ sender: 'other',
818
+ time: Utils.time(),
819
+ replyTo: null,
820
+ reactions: []
821
+ };
822
 
823
+ State.messages[State.activeRoomId].push(msg);
824
+ Chat.render();
825
+ Utils.save();
826
+ },
827
+ findMsg: (id) => {
828
+ return State.messages[State.activeRoomId].find(m => m.id === id);
829
+ },
830
+ render: () => {
831
+ const container = document.getElementById('chat-container');
832
+ container.innerHTML = '';
833
+ const msgs = State.messages[State.activeRoomId] || [];
834
 
835
  msgs.forEach(msg => {
836
+ const el = document.createElement('div');
837
+ el.className = `message ${msg.sender}`;
838
+ el.dataset.id = msg.id;
839
 
840
+ // Long press event attached to element
841
+ el.addEventListener('touchstart', (e) => Gestures.msgTouchStart(e, msg.id), {passive: true});
842
+ el.addEventListener('touchend', (e) => Gestures.msgTouchEnd(e), {passive: true});
843
+
844
+ let html = '';
845
+
846
+ // Reply Preview
847
  if (msg.replyTo) {
848
+ html += `
849
+ <div class="reply-preview">
850
+ <span class="reply-owner">${msg.replyTo.sender === 'me' ? 'شما' : 'دیگران'}</span>
851
+ <span>${msg.replyTo.text}</span>
852
+ </div>
853
+ `;
854
  }
855
 
856
+ html += `<div>${msg.text}</div>`;
857
+
858
+ // Reactions
859
  if (msg.reactions && msg.reactions.length > 0) {
860
+ const uniqueReactions = [...new Set(msg.reactions)];
861
+ html += `<div class="reaction-bar">`;
862
+ uniqueReactions.forEach(r => {
863
+ html += `<span class="reaction-badge">${r}</span>`;
864
+ });
865
+ html += `</div>`;
866
  }
867
 
868
+ html += `<div class="msg-meta">${msg.time} <i class="fa-solid fa-check-double" style="font-size:0.6rem"></i></div>`;
869
+
870
+ // Context Actions (Hidden by default)
871
+ html += `
872
+ <div class="message-actions">
873
+ <button class="btn-icon" style="width:30px; height:30px; font-size:0.8rem;" onclick="Chat.prepareReply('${msg.id}')">پاسخ</button>
874
+ <button class="btn-icon" style="width:30px; height:30px; font-size:0.8rem; color:var(--danger)" onclick="Chat.deleteMsg('${msg.id}')">حذف</button>
 
 
875
  </div>
876
  `;
877
+
878
+ el.innerHTML = html;
879
+ container.appendChild(el);
 
 
880
  });
881
 
 
882
  container.scrollTop = container.scrollHeight;
883
  },
884
+ prepareReply: (id) => {
885
+ const msg = Chat.findMsg(id);
886
+ if (!msg) return;
 
887
 
888
+ State.replyToMsgId = id;
889
+ const preview = document.getElementById('reply-bar');
890
+ const text = document.getElementById('reply-text-preview');
 
 
 
 
 
 
 
 
 
891
 
892
+ preview.style.display = 'flex';
893
+ text.innerText = `پاسخ به: ${msg.text}`;
894
+ UI.closeModal('context-menu');
895
+ document.getElementById('message-input').focus();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
896
  },
897
+ clearReply: () => {
898
+ State.replyToMsgId = null;
899
+ document.getElementById('reply-bar').style.display = 'none';
900
+ },
901
+ deleteMsg: (id) => {
902
+ if(!id) id = State.selectedMsgId;
903
+ const msgs = State.messages[State.activeRoomId];
904
+ const idx = msgs.findIndex(m => m.id === id);
905
  if (idx > -1) {
906
  msgs.splice(idx, 1);
907
+ Chat.render();
908
+ Utils.save();
909
+ UI.toast('پیام حذف شد');
910
  }
911
+ UI.closeModal('context-menu');
912
  },
913
+ copyLink: () => {
914
+ // Simulate copying a room link
915
+ const dummyLink = `https://minimalchat.app/room/${State.activeRoomId}`;
916
+ navigator.clipboard.writeText(dummyLink).then(() => {
917
+ UI.toast('لینک اتاق کپی شد');
918
+ });
919
+ },
920
+ addReaction: (emoji) => {
921
+ const msg = Chat.findMsg(State.selectedMsgId);
922
  if (msg) {
923
+ if (!msg.reactions) msg.reactions = [];
924
+ msg.reactions.push(emoji);
925
+ Chat.render();
926
+ Utils.save();
927
  }
928
+ UI.closeModal('reactions-modal');
929
+ UI.closeModal('context-menu');
930
  },
931
+ cancelReply: () => Chat.clearReply()
932
+ };
933
 
934
+ // --- UI Module ---
935
+ const UI = {
936
+ showScreen: (screenId) => {
937
+ document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
938
+ document.getElementById(screenId).classList.add('active');
939
  },
940
+ toast: (msg) => {
941
+ const el = document.getElementById('toast');
942
+ el.innerText = msg;
943
+ el.classList.add('show');
944
+ setTimeout(() => el.classList.remove('show'), 3000);
945
+ },
946
+ openModal: (id) => {
947
+ document.getElementById(id).classList.add('open');
948
+ },
949
+ closeModal: (id) => {
950
+ document.getElementById(id).classList.remove('open');
951
  }
952
  };
953
 
954
+ // --- Gestures Module ---
955
+ const Gestures = {
956
+ touchStartX: 0,
957
+ touchStartY: 0,
958
+ longPressTimer: null,
959
+ isLongPress: false,
 
 
 
 
 
 
 
960
 
961
+ handleTouchStart: (e) => {
962
+ Gestures.touchStartX = e.changedTouches[0].screenX;
963
+ },
964
+
965
+ handleTouchEnd: (e) => {
966
+ const touchEndX = e.changedTouches[0].screenX;
967
+ // Swipe Right (in RTL, Right is Back) -> Actually Swipe Left is back visually in LTR, but in RTL:
968
+ // To go "Back", usually swipe from Right to Left.
969
+ // Let's implement: Swipe from Right to Left (negative delta) to go back.
970
 
971
+ if (touchEndX < Gestures.touchStartX - 50) {
972
+ // Swipe detected (Right to Left)
973
+ const currentScreen = document.querySelector('.screen.active').id;
974
+ if (currentScreen === 'chat-screen') {
975
+ UI.showScreen('rooms-screen');
 
 
976
  }
 
977
  }
978
+ },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
 
980
+ msgTouchStart: (e, id) => {
981
+ Gestures.isLongPress = false;
982
+ Gestures.longPressTimer = setTimeout(() => {
983
+ Gestures.isLongPress = true;
984
+ State.selectedMsgId = id;
985
+ UI.openModal('context-menu');
986
+ // Visual feedback
987
+ e.target.closest('.message').classList.add('selected');
988
+ }, 600);
989
+ },
990
 
991
+ msgTouchEnd: (e) => {
992
+ clearTimeout(Gestures.longPressTimer);
993
+ if (Gestures.isLongPress) {
994
+ e.target.closest('.message').classList.remove('selected');
995
+ // Prevent click if it was a long press
996
+ // e.preventDefault();
997
+ }
 
998
  }
999
  };
1000
 
1001
+ // --- Init ---
1002
+ window.addEventListener('DOMContentLoaded', () => {
1003
+ Auth.check();
1004
+
1005
+ // Textarea auto-resize
1006
+ const tx = document.getElementById('message-input');
1007
+ tx.addEventListener('input', function() {
1008
+ this.style.height = 'auto';
1009
+ this.style.height = (this.scrollHeight) + 'px';
1010
+ });
1011
  });
1012
 
1013
+ </script>
1014
+ </body>
1015
+ </html>