anycoder-b6fc935a / index.html
samirerty's picture
Upload folder using huggingface_hub
3c9f92f verified
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>چت روم مینیمال | Minimal Chat</title>
<!-- فونت وزیرمتن -->
<link href="https://cdn.jsdelivr.net/gh/rastikerdar/vazirmatn@v33.003/Vazirmatn-font-face.css" rel="stylesheet" type="text/css" />
<!-- آیکون‌ها -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--bg-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--glass-bg: rgba(255, 255, 255, 0.15);
--glass-border: rgba(255, 255, 255, 0.2);
--glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
--text-color: #ffffff;
--text-secondary: #e0e0e0;
--danger: #ff7675;
--success: #00b894;
--msg-me: rgba(108, 92, 231, 0.6);
--msg-other: rgba(255, 255, 255, 0.1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
outline: none;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: 'Vazirmatn', sans-serif;
background: var(--bg-gradient);
color: var(--text-color);
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
/* --- Glassmorphism Utilities --- */
.glass {
background: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow);
border-radius: 16px;
}
.glass-panel {
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
padding: 10px;
}
/* --- App Container --- */
#app {
width: 100%;
height: 100%;
max-width: 480px; /* Mobile view simulation on desktop */
position: relative;
overflow: hidden;
background: rgba(255, 255, 255, 0.05);
}
@media (min-width: 481px) {
#app {
height: 95vh;
border-radius: 24px;
}
}
/* --- Screens --- */
.screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1), opacity 0.3s;
opacity: 0;
pointer-events: none;
transform: scale(0.95);
z-index: 1;
}
.screen.active {
opacity: 1;
pointer-events: all;
transform: scale(1);
z-index: 10;
}
/* --- Header --- */
header {
padding: 15px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 20;
}
.logo {
font-weight: bold;
font-size: 1.2rem;
display: flex;
align-items: center;
gap: 10px;
}
.anycoder-link {
font-size: 0.7rem;
color: rgba(255,255,255,0.6);
text-decoration: none;
}
/* --- Auth Screen --- */
#auth-screen {
justify-content: center;
align-items: center;
text-align: center;
padding: 30px;
}
.auth-input {
width: 100%;
padding: 15px;
margin: 10px 0;
border-radius: 12px;
border: 1px solid var(--glass-border);
background: rgba(255, 255, 255, 0.1);
color: white;
font-size: 1rem;
text-align: center;
transition: 0.3s;
}
.auth-input:focus {
background: rgba(255, 255, 255, 0.2);
border-color: var(--secondary-color);
}
.btn {
padding: 12px 24px;
border-radius: 12px;
border: none;
background: var(--primary-color);
color: white;
font-family: inherit;
font-weight: bold;
cursor: pointer;
transition: transform 0.1s, background 0.3s;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn:active {
transform: scale(0.96);
}
.btn-full {
width: 100%;
margin-top: 20px;
}
.btn-icon {
width: 40px;
height: 40px;
padding: 0;
border-radius: 50%;
}
/* --- Rooms List --- */
#rooms-screen {
padding-top: 60px; /* Space for header */
}
.room-list {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.room-card {
display: flex;
align-items: center;
padding: 15px;
margin-bottom: 12px;
cursor: pointer;
transition: 0.2s;
}
.room-card:active {
background: rgba(255, 255, 255, 0.1);
transform: scale(0.98);
}
.room-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
margin-left: 15px;
object-fit: cover;
border: 2px solid var(--secondary-color);
}
.room-info {
flex: 1;
}
.room-name {
font-weight: bold;
font-size: 1.1rem;
margin-bottom: 4px;
}
.room-last-msg {
font-size: 0.85rem;
color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200px;
}
.fab {
position: absolute;
bottom: 30px;
left: 30px;
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--success);
color: white;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5rem;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
cursor: pointer;
z-index: 100;
transition: transform 0.2s;
}
.fab:hover {
transform: rotate(90deg);
}
/* --- Chat Screen --- */
#chat-screen {
padding-top: 0;
}
.chat-header {
height: 70px;
display: flex;
align-items: center;
padding: 0 10px;
border-bottom: 1px solid var(--glass-border);
}
.chat-body {
flex: 1;
overflow-y: auto;
padding: 15px;
display: flex;
flex-direction: column;
gap: 10px;
}
.message {
max-width: 75%;
padding: 10px 14px;
border-radius: 18px;
position: relative;
animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
font-size: 0.95rem;
line-height: 1.5;
}
@keyframes popIn {
from { opacity: 0; transform: translateY(10px) scale(0.9); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.message.me {
align-self: flex-start; /* RTL: Start is Right */
background: var(--msg-me);
border-bottom-right-radius: 4px;
}
.message.other {
align-self: flex-end; /* RTL: End is Left */
background: var(--msg-other);
border-bottom-left-radius: 4px;
}
.msg-meta {
font-size: 0.7rem;
opacity: 0.7;
margin-top: 4px;
text-align: left; /* Always left for time in bubbles usually, or right in RTL */
display: flex;
justify-content: flex-end;
gap: 5px;
align-items: center;
}
.reply-preview {
background: rgba(0,0,0,0.2);
padding: 5px 8px;
border-radius: 8px;
margin-bottom: 8px;
font-size: 0.8rem;
border-right: 3px solid var(--secondary-color);
display: flex;
flex-direction: column;
}
.reply-owner {
font-size: 0.7rem;
font-weight: bold;
color: var(--secondary-color);
}
.message-actions {
position: absolute;
top: -30px;
left: 0;
background: rgba(0,0,0,0.8);
border-radius: 20px;
padding: 5px;
display: none; /* Shown on long press */
gap: 10px;
white-space: nowrap;
}
.message.selected .message-actions {
display: flex;
}
.chat-footer {
padding: 10px;
background: rgba(0, 0, 0, 0.2);
display: flex;
align-items: flex-end;
gap: 10px;
}
.chat-input-wrapper {
flex: 1;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
padding: 8px 15px;
display: flex;
flex-direction: column;
}
.reply-bar {
display: none;
border-bottom: 1px solid rgba(255,255,255,0.1);
padding-bottom: 5px;
margin-bottom: 5px;
font-size: 0.8rem;
color: var(--secondary-color);
justify-content: space-between;
align-items: center;
}
.chat-input {
width: 100%;
background: transparent;
border: none;
color: white;
resize: none;
max-height: 100px;
min-height: 24px;
font-family: inherit;
}
/* --- Reactions --- */
.reaction-bar {
display: flex;
gap: 5px;
margin-top: 5px;
justify-content: flex-end;
}
.reaction-badge {
font-size: 0.7rem;
background: rgba(0,0,0,0.3);
border-radius: 10px;
padding: 2px 6px;
display: flex;
align-items: center;
gap: 2px;
}
/* --- Modals & Overlays --- */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.6);
backdrop-filter: blur(5px);
z-index: 1000;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
pointer-events: none;
transition: 0.3s;
}
.modal-overlay.open {
opacity: 1;
pointer-events: all;
}
.modal {
width: 85%;
max-width: 350px;
padding: 20px;
transform: translateY(20px);
transition: 0.3s;
}
.modal-overlay.open .modal {
transform: translateY(0);
}
.modal h3 {
margin-bottom: 15px;
text-align: center;
}
.reaction-picker {
display: flex;
justify-content: space-around;
margin-top: 15px;
}
.emoji-btn {
font-size: 1.5rem;
background: none;
border: none;
cursor: pointer;
transition: transform 0.2s;
}
.emoji-btn:hover {
transform: scale(1.3);
}
/* --- Toast Notification --- */
#toast {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: rgba(0,0,0,0.8);
color: white;
padding: 10px 20px;
border-radius: 20px;
font-size: 0.9rem;
opacity: 0;
transition: 0.3s;
pointer-events: none;
z-index: 2000;
white-space: nowrap;
}
#toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* Scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.2);
border-radius: 3px;
}
</style>
</head>
<body>
<div id="app" class="glass">
<!-- Header (Global) -->
<header class="glass-panel" style="margin: 10px; border-radius: 12px; position: absolute; top:0; width: calc(100% - 20px); z-index: 50;">
<div class="logo">
<i class="fa-solid fa-comments"></i>
<span>مینیمال چت</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i>
</a>
</header>
<!-- 1. Auth Screen -->
<section id="auth-screen" class="screen active">
<div style="text-align: center; margin-bottom: 30px;">
<i class="fa-solid fa-lock" style="font-size: 3rem; margin-bottom: 20px; opacity: 0.8;"></i>
<h2>ورود به حساب</h2>
<p style="opacity: 0.7; font-size: 0.9rem;">برای شروع شماره موبایل خود را وارد کنید</p>
</div>
<div id="step-phone">
<input type="tel" id="phone-input" class="auth-input" placeholder="0912..." maxlength="11">
<button class="btn btn-full" onclick="Auth.sendCode()">دریافت کد تایید</button>
</div>
<div id="step-code" style="display: none;">
<input type="number" id="code-input" class="auth-input" placeholder="کد 4 رقمی (تست: 1234)" maxlength="4">
<button class="btn btn-full" onclick="Auth.verifyCode()">تایید و ورود</button>
<p style="margin-top: 15px; font-size: 0.8rem; cursor: pointer; opacity: 0.8;" onclick="Auth.reset()">ویرایش شماره</p>
</div>
</section>
<!-- 2. Rooms List Screen -->
<section id="rooms-screen" class="screen">
<div class="room-list" id="room-list-container">
<!-- Rooms injected here -->
</div>
<!-- Create Room FAB -->
<div class="fab" onclick="UI.openModal('create-room-modal')">
<i class="fa-solid fa-plus"></i>
</div>
</section>
<!-- 3. Chat Screen -->
<section id="chat-screen" class="screen">
<div class="chat-header glass-panel" style="border-radius: 0; margin: 0; width: 100%;">
<button class="btn btn-icon" onclick="UI.showScreen('rooms-screen')">
<i class="fa-solid fa-arrow-right"></i>
</button>
<div class="room-info" style="margin-right: 10px; margin-left: 10px;">
<div class="room-name" id="chat-title">نام اتاق</div>
<div style="font-size: 0.7rem; opacity: 0.7;">آنلاین</div>
</div>
<button class="btn btn-icon" onclick="Chat.copyLink()">
<i class="fa-solid fa-link"></i>
</button>
</div>
<div class="chat-body" id="chat-container" ontouchstart="Gestures.handleTouchStart(event)" ontouchend="Gestures.handleTouchEnd(event)">
<!-- Messages injected here -->
</div>
<div class="chat-footer">
<button class="btn btn-icon" style="background: rgba(255,255,255,0.1);" onclick="UI.openModal('reactions-modal')">
<i class="fa-regular fa-face-smile"></i>
</button>
<div class="chat-input-wrapper">
<div class="reply-bar" id="reply-bar">
<span id="reply-text-preview">پاسخ به...</span>
<i class="fa-solid fa-xmark" style="cursor: pointer;" onclick="Chat.cancelReply()"></i>
</div>
<textarea id="message-input" class="chat-input" rows="1" placeholder="پیام خود را بنویسید..."></textarea>
</div>
<button class="btn btn-icon" style="background: var(--primary-color);" onclick="Chat.sendMessage()">
<i class="fa-solid fa-paper-plane"></i>
</button>
</div>
</section>
</div>
<!-- Modals -->
<!-- Create Room Modal -->
<div id="create-room-modal" class="modal-overlay">
<div class="modal glass">
<h3>ایجاد اتاق جدید</h3>
<input type="text" id="new-room-name" class="auth-input" placeholder="نام اتاق">
<div style="display: flex; gap: 10px; margin-top: 20px;">
<button class="btn" style="flex: 1; background: rgba(255,255,255,0.2);" onclick="UI.closeModal('create-room-modal')">لغو</button>
<button class="btn" style="flex: 1; background: var(--success);" onclick="RoomManager.createRoom()">ساخت</button>
</div>
</div>
</div>
<!-- Reaction Picker Modal (Contextual) -->
<div id="reactions-modal" class="modal-overlay">
<div class="modal glass">
<h3>انتخاب ایموجی</h3>
<div class="reaction-picker">
<button class="emoji-btn" onclick="Chat.addReaction('❤️')">❤️</button>
<button class="emoji-btn" onclick="Chat.addReaction('👍')">👍</button>
<button class="emoji-btn" onclick="Chat.addReaction('😂')">😂</button>
<button class="emoji-btn" onclick="Chat.addReaction('😮')">😮</button>
<button class="emoji-btn" onclick="Chat.addReaction('😢')">😢</button>
<button class="emoji-btn" onclick="Chat.addReaction('😡')">😡</button>
</div>
<div style="text-align: center; margin-top: 15px;">
<button class="btn" style="background: rgba(255,255,255,0.2); font-size: 0.8rem; padding: 5px 15px;" onclick="UI.closeModal('reactions-modal')">انصراف</button>
</div>
</div>
</div>
<!-- Context Menu (Long Press) -->
<div id="context-menu" class="modal-overlay" style="z-index: 3000;">
<div class="modal glass" style="position: absolute; bottom: 100px; width: auto; min-width: 150px; padding: 5px;">
<button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="Chat.replyToMsg()">
<i class="fa-solid fa-reply"></i> پاسخ
</button>
<button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="Chat.deleteMsg()">
<i class="fa-solid fa-trash"></i> حذف
</button>
<button class="btn" style="width: 100%; justify-content: flex-start; background: transparent;" onclick="UI.closeModal('context-menu')">
<i class="fa-solid fa-xmark"></i> لغو
</button>
</div>
</div>
<!-- Toast -->
<div id="toast">پیام سیستم</div>
<script>
// --- State Management ---
const State = {
user: null,
rooms: [],
activeRoomId: null,
messages: {}, // { roomId: [msgs] }
selectedMsgId: null,
replyToMsgId: null
};
// --- Data Mockup ---
const MockData = {
avatars: [
'https://picsum.photos/seed/room1/100/100',
'https://picsum.photos/seed/room2/100/100',
'https://picsum.photos/seed/room3/100/100'
]
};
// --- Utility ---
const Utils = {
id: () => Math.random().toString(36).substr(2, 9),
time: () => {
const d = new Date();
return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0');
},
save: () => localStorage.setItem('minimalChatState', JSON.stringify(State)),
load: () => {
const data = localStorage.getItem('minimalChatState');
if (data) {
const parsed = JSON.parse(data);
State.user = parsed.user;
State.rooms = parsed.rooms;
State.messages = parsed.messages;
}
}
};
// --- Auth Module ---
const Auth = {
sendCode: () => {
const phone = document.getElementById('phone-input').value;
if (phone.length < 10 || !phone.startsWith('09')) {
UI.toast('شماره موبایل نامعتبر است');
return;
}
UI.toast('کد تایید ارسال شد: 1234');
document.getElementById('step-phone').style.display = 'none';
document.getElementById('step-code').style.display = 'block';
},
verifyCode: () => {
const code = document.getElementById('code-input').value;
if (code === '1234') {
const phone = document.getElementById('phone-input').value;
State.user = { phone: phone, name: 'کاربر' };
Utils.save();
UI.toast('خوش آمدید!');
UI.showScreen('rooms-screen');
RoomManager.render();
} else {
UI.toast('کد اشتباه است');
}
},
reset: () => {
document.getElementById('step-phone').style.display = 'block';
document.getElementById('step-code').style.display = 'none';
},
check: () => {
Utils.load();
if (State.user) {
UI.showScreen('rooms-screen');
RoomManager.render();
}
}
};
// --- Room Manager Module ---
const RoomManager = {
createRoom: () => {
const name = document.getElementById('new-room-name').value;
if (!name) return UI.toast('نام اتاق الزامی است');
if (State.rooms.length >= 3) {
UI.closeModal('create-room-modal');
return UI.toast('حداکثر ۳ اتاق مجاز است');
}
const newRoom = {
id: Utils.id(),
name: name,
avatar: MockData.avatars[State.rooms.length % 3],
lastMsg: 'اتاق جدید ایجاد شد'
};
State.rooms.unshift(newRoom);
State.messages[newRoom.id] = [];
Utils.save();
document.getElementById('new-room-name').value = '';
UI.closeModal('create-room-modal');
RoomManager.render();
UI.toast('اتاق ایجاد شد');
},
joinRoom: (id) => {
State.activeRoomId = id;
const room = State.rooms.find(r => r.id === id);
document.getElementById('chat-title').innerText = room.name;
Chat.render();
UI.showScreen('chat-screen');
},
render: () => {
const container = document.getElementById('room-list-container');
container.innerHTML = '';
if (State.rooms.length === 0) {
container.innerHTML = '<div style="text-align:center; opacity:0.6; margin-top:50px;">هنوز اتاقی ندارید.<br>برای ساخت دکمه + را بزنید.</div>';
return;
}
State.rooms.forEach(room => {
const el = document.createElement('div');
el.className = 'room-card glass';
el.onclick = () => RoomManager.joinRoom(room.id);
el.innerHTML = `
<img src="${room.avatar}" class="room-avatar" alt="Avatar">
<div class="room-info">
<div class="room-name">${room.name}</div>
<div class="room-last-msg">${room.lastMsg}</div>
</div>
<div style="font-size:0.7rem; opacity:0.5;">${Utils.time()}</div>
`;
container.appendChild(el);
});
}
};
// --- Chat System Module ---
const Chat = {
sendMessage: () => {
const input = document.getElementById('message-input');
const text = input.value.trim();
if (!text) return;
const msg = {
id: Utils.id(),
text: text,
sender: 'me',
time: Utils.time(),
replyTo: State.replyToMsgId ? Chat.findMsg(State.replyToMsgId) : null,
reactions: []
};
if (!State.messages[State.activeRoomId]) State.messages[State.activeRoomId] = [];
State.messages[State.activeRoomId].push(msg);
// Update last message in room list
const room = State.rooms.find(r => r.id === State.activeRoomId);
room.lastMsg = text;
Chat.clearReply();
input.value = '';
Chat.render();
Utils.save();
// Simulate reply
setTimeout(() => {
Chat.receiveMockReply();
}, 2000);
},
receiveMockReply: () => {
const replies = ['خیلی جالبه!', 'باشه حتما', 'می‌فهمم 😊', 'خوبه', 'فعلاً سرم شلوغه'];
const text = replies[Math.floor(Math.random() * replies.length)];
const msg = {
id: Utils.id(),
text: text,
sender: 'other',
time: Utils.time(),
replyTo: null,
reactions: []
};
State.messages[State.activeRoomId].push(msg);
Chat.render();
Utils.save();
},
findMsg: (id) => {
return State.messages[State.activeRoomId].find(m => m.id === id);
},
render: () => {
const container = document.getElementById('chat-container');
container.innerHTML = '';
const msgs = State.messages[State.activeRoomId] || [];
msgs.forEach(msg => {
const el = document.createElement('div');
el.className = `message ${msg.sender}`;
el.dataset.id = msg.id;
// Long press event attached to element
el.addEventListener('touchstart', (e) => Gestures.msgTouchStart(e, msg.id), {passive: true});
el.addEventListener('touchend', (e) => Gestures.msgTouchEnd(e), {passive: true});
let html = '';
// Reply Preview
if (msg.replyTo) {
html += `
<div class="reply-preview">
<span class="reply-owner">${msg.replyTo.sender === 'me' ? 'شما' : 'دیگران'}</span>
<span>${msg.replyTo.text}</span>
</div>
`;
}
html += `<div>${msg.text}</div>`;
// Reactions
if (msg.reactions && msg.reactions.length > 0) {
const uniqueReactions = [...new Set(msg.reactions)];
html += `<div class="reaction-bar">`;
uniqueReactions.forEach(r => {
html += `<span class="reaction-badge">${r}</span>`;
});
html += `</div>`;
}
html += `<div class="msg-meta">${msg.time} <i class="fa-solid fa-check-double" style="font-size:0.6rem"></i></div>`;
// Context Actions (Hidden by default)
html += `
<div class="message-actions">
<button class="btn-icon" style="width:30px; height:30px; font-size:0.8rem;" onclick="Chat.prepareReply('${msg.id}')">پاسخ</button>
<button class="btn-icon" style="width:30px; height:30px; font-size:0.8rem; color:var(--danger)" onclick="Chat.deleteMsg('${msg.id}')">حذف</button>
</div>
`;
el.innerHTML = html;
container.appendChild(el);
});
container.scrollTop = container.scrollHeight;
},
prepareReply: (id) => {
const msg = Chat.findMsg(id);
if (!msg) return;
State.replyToMsgId = id;
const preview = document.getElementById('reply-bar');
const text = document.getElementById('reply-text-preview');
preview.style.display = 'flex';
text.innerText = `پاسخ به: ${msg.text}`;
UI.closeModal('context-menu');
document.getElementById('message-input').focus();
},
clearReply: () => {
State.replyToMsgId = null;
document.getElementById('reply-bar').style.display = 'none';
},
deleteMsg: (id) => {
if(!id) id = State.selectedMsgId;
const msgs = State.messages[State.activeRoomId];
const idx = msgs.findIndex(m => m.id === id);
if (idx > -1) {
msgs.splice(idx, 1);
Chat.render();
Utils.save();
UI.toast('پیام حذف شد');
}
UI.closeModal('context-menu');
},
copyLink: () => {
// Simulate copying a room link
const dummyLink = `https://minimalchat.app/room/${State.activeRoomId}`;
navigator.clipboard.writeText(dummyLink).then(() => {
UI.toast('لینک اتاق کپی شد');
});
},
addReaction: (emoji) => {
const msg = Chat.findMsg(State.selectedMsgId);
if (msg) {
if (!msg.reactions) msg.reactions = [];
msg.reactions.push(emoji);
Chat.render();
Utils.save();
}
UI.closeModal('reactions-modal');
UI.closeModal('context-menu');
},
cancelReply: () => Chat.clearReply()
};
// --- UI Module ---
const UI = {
showScreen: (screenId) => {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(screenId).classList.add('active');
},
toast: (msg) => {
const el = document.getElementById('toast');
el.innerText = msg;
el.classList.add('show');
setTimeout(() => el.classList.remove('show'), 3000);
},
openModal: (id) => {
document.getElementById(id).classList.add('open');
},
closeModal: (id) => {
document.getElementById(id).classList.remove('open');
}
};
// --- Gestures Module ---
const Gestures = {
touchStartX: 0,
touchStartY: 0,
longPressTimer: null,
isLongPress: false,
handleTouchStart: (e) => {
Gestures.touchStartX = e.changedTouches[0].screenX;
},
handleTouchEnd: (e) => {
const touchEndX = e.changedTouches[0].screenX;
// Swipe Right (in RTL, Right is Back) -> Actually Swipe Left is back visually in LTR, but in RTL:
// To go "Back", usually swipe from Right to Left.
// Let's implement: Swipe from Right to Left (negative delta) to go back.
if (touchEndX < Gestures.touchStartX - 50) {
// Swipe detected (Right to Left)
const currentScreen = document.querySelector('.screen.active').id;
if (currentScreen === 'chat-screen') {
UI.showScreen('rooms-screen');
}
}
},
msgTouchStart: (e, id) => {
Gestures.isLongPress = false;
Gestures.longPressTimer = setTimeout(() => {
Gestures.isLongPress = true;
State.selectedMsgId = id;
UI.openModal('context-menu');
// Visual feedback
e.target.closest('.message').classList.add('selected');
}, 600);
},
msgTouchEnd: (e) => {
clearTimeout(Gestures.longPressTimer);
if (Gestures.isLongPress) {
e.target.closest('.message').classList.remove('selected');
// Prevent click if it was a long press
// e.preventDefault();
}
}
};
// --- Init ---
window.addEventListener('DOMContentLoaded', () => {
Auth.check();
// Textarea auto-resize
const tx = document.getElementById('message-input');
tx.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
});
</script>
</body>
</html>