Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Modern Chat Interface</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| /* Custom scrollbar */ | |
| .custom-scrollbar::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| border-radius: 10px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb { | |
| background: #888; | |
| border-radius: 10px; | |
| } | |
| .custom-scrollbar::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| /* Animation for new messages */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .message-animation { | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| /* Pulse animation for active users */ | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .pulse-active { | |
| animation: pulse 2s infinite; | |
| } | |
| /* Tab styling */ | |
| .tab { | |
| position: relative; | |
| padding: 0.5rem 1rem; | |
| cursor: pointer; | |
| border-radius: 0.5rem 0.5rem 0 0; | |
| transition: all 0.2s; | |
| } | |
| .tab.active { | |
| background-color: white; | |
| color: #4f46e5; | |
| font-weight: 600; | |
| } | |
| .tab.active::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -1px; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background-color: #4f46e5; | |
| } | |
| .tab-badge { | |
| position: absolute; | |
| top: -0.5rem; | |
| right: -0.5rem; | |
| background-color: #ef4444; | |
| color: white; | |
| border-radius: 50%; | |
| width: 1.25rem; | |
| height: 1.25rem; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 0.75rem; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 font-sans"> | |
| <!-- Main Container --> | |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> | |
| <!-- Login Section (shown by default) --> | |
| <div id="login-section" class="bg-white rounded-xl shadow-lg p-8 max-w-md mx-auto mb-8 transition-all duration-300"> | |
| <div class="text-center mb-6"> | |
| <h1 class="text-3xl font-bold text-indigo-600">Welcome to ChatApp</h1> | |
| <p class="text-gray-500 mt-2">Connect with people around the world</p> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label> | |
| <input type="text" id="username" placeholder="Enter your username" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"> | |
| </div> | |
| <button id="login-btn" | |
| class="w-full bg-indigo-600 text-white py-2 px-4 rounded-lg hover:bg-indigo-700 transition flex items-center justify-center"> | |
| <i class="fas fa-sign-in-alt mr-2"></i> Login | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Main Chat Interface (hidden by default) --> | |
| <div id="chat-interface" class="hidden"> | |
| <!-- Header with user info --> | |
| <div class="flex justify-between items-center mb-6"> | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-indigo-500 flex items-center justify-center text-white font-bold text-xl mr-3" id="user-avatar"> | |
| U | |
| </div> | |
| <div> | |
| <h2 class="font-bold text-lg" id="username-display">Username</h2> | |
| <p class="text-sm text-gray-500">Online</p> | |
| </div> | |
| </div> | |
| <button id="logout-btn" class="text-gray-500 hover:text-gray-700 transition"> | |
| <i class="fas fa-sign-out-alt text-xl"></i> | |
| </button> | |
| </div> | |
| <!-- Chat tabs --> | |
| <div class="flex mb-4 bg-gray-200 rounded-t-lg overflow-hidden"> | |
| <div id="room-tab" class="tab active relative" data-type="room"> | |
| <i class="fas fa-hashtag mr-2"></i>Rooms | |
| <span id="room-notification" class="tab-badge hidden"></span> | |
| </div> | |
| <div id="private-tab" class="tab" data-type="private"> | |
| <i class="fas fa-user-friends mr-2"></i>Private | |
| <span id="private-notification" class="tab-badge hidden"></span> | |
| </div> | |
| </div> | |
| <!-- Dual List Layout --> | |
| <div id="room-section" class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <!-- Rooms User is In --> | |
| <div class="bg-white rounded-xl shadow p-4"> | |
| <div class="flex justify-between items-center mb-3"> | |
| <h3 class="font-semibold text-indigo-600 flex items-center"> | |
| <i class="fas fa-door-open mr-2"></i> Your Rooms | |
| </h3> | |
| <span class="bg-indigo-100 text-indigo-800 text-xs px-2 py-1 rounded-full" id="room-count">0</span> | |
| </div> | |
| <div id="user-rooms" class="space-y-2 max-h-40 overflow-y-auto custom-scrollbar"> | |
| <p class="text-gray-500 text-sm italic">You haven't joined any rooms yet</p> | |
| </div> | |
| </div> | |
| <!-- Online Users --> | |
| <div class="bg-white rounded-xl shadow p-4"> | |
| <div class="flex justify-between items-center mb-3"> | |
| <h3 class="font-semibold text-green-600 flex items-center"> | |
| <i class="fas fa-users mr-2"></i> Online Users | |
| </h3> | |
| <span class="bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full" id="user-count">0</span> | |
| </div> | |
| <div id="online-users" class="space-y-2 max-h-40 overflow-y-auto custom-scrollbar"> | |
| <p class="text-gray-500 text-sm italic">Loading users...</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Private chat section (hidden by default) --> | |
| <div id="private-section" class="hidden mb-6"> | |
| <div class="bg-white rounded-xl shadow p-4"> | |
| <div class="flex justify-between items-center mb-3"> | |
| <h3 class="font-semibold text-purple-600 flex items-center"> | |
| <i class="fas fa-comments mr-2"></i> Private Conversations | |
| </h3> | |
| <span class="bg-purple-100 text-purple-800 text-xs px-2 py-1 rounded-full" id="private-count">0</span> | |
| </div> | |
| <div id="private-chats" class="space-y-2 max-h-40 overflow-y-auto custom-scrollbar"> | |
| <p class="text-gray-500 text-sm italic">No private conversations yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Chat Area --> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-6"> | |
| <!-- Chat header --> | |
| <div id="chat-header" class="p-4 border-b border-gray-200 bg-gray-50 flex items-center"> | |
| <div id="chat-type-icon" class="text-indigo-600 mr-2"> | |
| <i class="fas fa-hashtag"></i> | |
| </div> | |
| <h3 id="chat-title" class="font-semibold">Select a chat</h3> | |
| </div> | |
| <!-- Messages Display --> | |
| <div id="messages-container" class="p-4 h-64 overflow-y-auto custom-scrollbar bg-gray-50"> | |
| <div class="text-center text-gray-500 py-10"> | |
| <i class="fas fa-comments text-4xl mb-2 text-gray-300"></i> | |
| <p>No messages yet. Start the conversation!</p> | |
| </div> | |
| </div> | |
| <!-- Message Input --> | |
| <div class="p-4 border-t border-gray-200 bg-white"> | |
| <div class="flex space-x-2"> | |
| <input type="text" id="message-input" placeholder="Type your message here..." | |
| class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition"> | |
| <button id="send-btn" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Rooms Section --> | |
| <div id="room-management-section" class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <!-- Available Rooms --> | |
| <div class="bg-white rounded-xl shadow p-4"> | |
| <div class="flex justify-between items-center mb-3"> | |
| <h3 class="font-semibold text-blue-600 flex items-center"> | |
| <i class="fas fa-hashtag mr-2"></i> Available Rooms | |
| </h3> | |
| <span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full" id="available-room-count">0</span> | |
| </div> | |
| <div id="available-rooms" class="space-y-2 max-h-40 overflow-y-auto custom-scrollbar"> | |
| <p class="text-gray-500 text-sm italic">Loading rooms...</p> | |
| </div> | |
| </div> | |
| <!-- Add Room Section --> | |
| <div class="bg-white rounded-xl shadow p-4"> | |
| <h3 class="font-semibold text-purple-600 flex items-center mb-3"> | |
| <i class="fas fa-plus-circle mr-2"></i> Create New Room | |
| </h3> | |
| <div class="flex space-x-2"> | |
| <input type="text" id="new-room-input" placeholder="Enter room name" | |
| class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition"> | |
| <button id="add-room-btn" class="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 transition"> | |
| <i class="fas fa-check"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Join Room Modal --> | |
| <div id="join-room-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
| <div class="bg-white rounded-xl shadow-xl p-6 max-w-sm w-full mx-4 transform transition-all duration-300 scale-95 opacity-0"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h3 class="font-bold text-lg">Join Room</h3> | |
| <button id="close-modal-btn" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <p class="mb-4">Do you want to join <span id="room-name-display" class="font-semibold">General</span>?</p> | |
| <div class="flex justify-end space-x-3"> | |
| <button id="cancel-join-btn" class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-100 transition"> | |
| Cancel | |
| </button> | |
| <button id="confirm-join-btn" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition"> | |
| Join | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const loginSection = document.getElementById('login-section'); | |
| const chatInterface = document.getElementById('chat-interface'); | |
| const loginBtn = document.getElementById('login-btn'); | |
| const logoutBtn = document.getElementById('logout-btn'); | |
| const usernameInput = document.getElementById('username'); | |
| const usernameDisplay = document.getElementById('username-display'); | |
| const userAvatar = document.getElementById('user-avatar'); | |
| const messageInput = document.getElementById('message-input'); | |
| const sendBtn = document.getElementById('send-btn'); | |
| const messagesContainer = document.getElementById('messages-container'); | |
| const userRooms = document.getElementById('user-rooms'); | |
| const onlineUsers = document.getElementById('online-users'); | |
| const availableRooms = document.getElementById('available-rooms'); | |
| const newRoomInput = document.getElementById('new-room-input'); | |
| const addRoomBtn = document.getElementById('add-room-btn'); | |
| const joinRoomModal = document.getElementById('join-room-modal'); | |
| const closeModalBtn = document.getElementById('close-modal-btn'); | |
| const cancelJoinBtn = document.getElementById('cancel-join-btn'); | |
| const confirmJoinBtn = document.getElementById('confirm-join-btn'); | |
| const roomNameDisplay = document.getElementById('room-name-display'); | |
| const roomCount = document.getElementById('room-count'); | |
| const userCount = document.getElementById('user-count'); | |
| const availableRoomCount = document.getElementById('available-room-count'); | |
| const roomTab = document.getElementById('room-tab'); | |
| const privateTab = document.getElementById('private-tab'); | |
| const roomSection = document.getElementById('room-section'); | |
| const privateSection = document.getElementById('private-section'); | |
| const privateChats = document.getElementById('private-chats'); | |
| const chatHeader = document.getElementById('chat-header'); | |
| const chatTitle = document.getElementById('chat-title'); | |
| const chatTypeIcon = document.getElementById('chat-type-icon'); | |
| const roomManagementSection = document.getElementById('room-management-section'); | |
| const roomNotification = document.getElementById('room-notification'); | |
| const privateNotification = document.getElementById('private-notification'); | |
| const privateCount = document.getElementById('private-count'); | |
| // State | |
| let currentUser = ''; | |
| let currentRooms = []; | |
| let allUsers = []; | |
| let allRooms = ['General', 'Random', 'Help']; | |
| let selectedChat = null; | |
| let chatType = 'room'; // 'room' or 'private' | |
| let messages = {}; | |
| let privateMessages = {}; | |
| let unreadCounts = { rooms: {}, private: {} }; | |
| // Initialize with some sample data | |
| function initializeSampleData() { | |
| // Sample users | |
| allUsers = ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve']; | |
| // Sample messages for rooms | |
| messages = { | |
| 'General': [ | |
| { user: 'Alice', text: 'Hello everyone!', time: '10:30 AM' }, | |
| { user: 'Bob', text: 'Hi Alice! How are you?', time: '10:32 AM' } | |
| ], | |
| 'Random': [ | |
| { user: 'Charlie', text: 'Did you see that movie?', time: '9:15 AM' }, | |
| { user: 'Diana', text: 'Yes, it was amazing!', time: '9:20 AM' } | |
| ], | |
| 'Help': [ | |
| { user: 'Eve', text: 'Can someone help me with this?', time: '11:05 AM' } | |
| ] | |
| }; | |
| // Initialize private messages | |
| privateMessages = {}; | |
| allUsers.forEach(user => { | |
| if (user !== currentUser) { | |
| privateMessages[user] = []; | |
| } | |
| }); | |
| // Initialize unread counts | |
| unreadCounts = { | |
| rooms: {}, | |
| private: {} | |
| }; | |
| } | |
| // Login function | |
| function login() { | |
| const username = usernameInput.value.trim(); | |
| if (username) { | |
| currentUser = username; | |
| usernameDisplay.textContent = username; | |
| userAvatar.textContent = username.charAt(0).toUpperCase(); | |
| // Generate a random color for the avatar | |
| const colors = ['bg-indigo-500', 'bg-blue-500', 'bg-green-500', 'bg-purple-500', 'bg-pink-500', 'bg-red-500', 'bg-yellow-500']; | |
| const randomColor = colors[Math.floor(Math.random() * colors.length)]; | |
| userAvatar.className = `w-10 h-10 rounded-full ${randomColor} flex items-center justify-center text-white font-bold text-xl mr-3`; | |
| // Show chat interface and hide login | |
| loginSection.classList.add('hidden'); | |
| chatInterface.classList.remove('hidden'); | |
| // Initialize data and UI | |
| initializeSampleData(); | |
| updateUI(); | |
| // Add user to online users if not already there | |
| if (!allUsers.includes(username)) { | |
| allUsers.push(username); | |
| } | |
| // Focus on message input | |
| messageInput.focus(); | |
| } else { | |
| alert('Please enter a username'); | |
| } | |
| } | |
| // Logout function | |
| function logout() { | |
| currentUser = ''; | |
| chatInterface.classList.add('hidden'); | |
| loginSection.classList.remove('hidden'); | |
| usernameInput.value = ''; | |
| } | |
| // Switch between room and private chat tabs | |
| function switchTab(type) { | |
| if (type === chatType) return; | |
| chatType = type; | |
| // Update tab styling | |
| if (type === 'room') { | |
| roomTab.classList.add('active'); | |
| privateTab.classList.remove('active'); | |
| roomSection.classList.remove('hidden'); | |
| privateSection.classList.add('hidden'); | |
| roomManagementSection.classList.remove('hidden'); | |
| } else { | |
| roomTab.classList.remove('active'); | |
| privateTab.classList.add('active'); | |
| roomSection.classList.add('hidden'); | |
| privateSection.classList.remove('hidden'); | |
| roomManagementSection.classList.add('hidden'); | |
| } | |
| // Clear selected chat when switching tabs | |
| selectedChat = null; | |
| updateMessages(); | |
| updateChatHeader(); | |
| } | |
| // Update all UI elements | |
| function updateUI() { | |
| updateUserRooms(); | |
| updateOnlineUsers(); | |
| updateAvailableRooms(); | |
| updatePrivateChats(); | |
| updateMessages(); | |
| updateChatHeader(); | |
| updateNotifications(); | |
| } | |
| // Update the user's rooms list | |
| function updateUserRooms() { | |
| userRooms.innerHTML = ''; | |
| if (currentRooms.length === 0) { | |
| userRooms.innerHTML = '<p class="text-gray-500 text-sm italic">You haven\'t joined any rooms yet</p>'; | |
| roomCount.textContent = '0'; | |
| return; | |
| } | |
| roomCount.textContent = currentRooms.length; | |
| currentRooms.forEach(room => { | |
| const roomElement = document.createElement('div'); | |
| roomElement.className = 'flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg cursor-pointer transition'; | |
| roomElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <i class="fas fa-hashtag text-gray-400 mr-2"></i> | |
| <span>${room}</span> | |
| </div> | |
| <span class="text-xs text-gray-500">${messages[room] ? messages[room].length : 0} messages</span> | |
| `; | |
| roomElement.addEventListener('click', () => { | |
| chatType = 'room'; | |
| selectedChat = room; | |
| switchTab('room'); | |
| updateMessages(); | |
| // Mark as read | |
| if (unreadCounts.rooms[room]) { | |
| unreadCounts.rooms[room] = 0; | |
| updateNotifications(); | |
| } | |
| }); | |
| userRooms.appendChild(roomElement); | |
| }); | |
| } | |
| // Update online users list | |
| function updateOnlineUsers() { | |
| onlineUsers.innerHTML = ''; | |
| userCount.textContent = allUsers.length; | |
| if (allUsers.length === 0) { | |
| onlineUsers.innerHTML = '<p class="text-gray-500 text-sm italic">No users online</p>'; | |
| return; | |
| } | |
| allUsers.forEach(user => { | |
| if (user === currentUser) return; | |
| const userElement = document.createElement('div'); | |
| userElement.className = 'flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg cursor-pointer transition'; | |
| userElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <div class="w-6 h-6 rounded-full bg-green-500 flex items-center justify-center text-white text-xs mr-2"> | |
| ${user.charAt(0).toUpperCase()} | |
| </div> | |
| <span>${user}</span> | |
| </div> | |
| <span class="text-xs text-gray-500">Online</span> | |
| `; | |
| userElement.addEventListener('click', () => { | |
| // Start private chat with this user | |
| chatType = 'private'; | |
| selectedChat = user; | |
| switchTab('private'); | |
| updateMessages(); | |
| // Mark as read | |
| if (unreadCounts.private[user]) { | |
| unreadCounts.private[user] = 0; | |
| updateNotifications(); | |
| } | |
| }); | |
| onlineUsers.appendChild(userElement); | |
| }); | |
| } | |
| // Update available rooms list | |
| function updateAvailableRooms() { | |
| availableRooms.innerHTML = ''; | |
| availableRoomCount.textContent = allRooms.length; | |
| if (allRooms.length === 0) { | |
| availableRooms.innerHTML = '<p class="text-gray-500 text-sm italic">No rooms available</p>'; | |
| return; | |
| } | |
| allRooms.forEach(room => { | |
| // Skip rooms the user is already in | |
| if (currentRooms.includes(room)) return; | |
| const roomElement = document.createElement('div'); | |
| roomElement.className = 'flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg cursor-pointer transition'; | |
| roomElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <i class="fas fa-hashtag text-gray-400 mr-2"></i> | |
| <span>${room}</span> | |
| </div> | |
| <span class="text-xs text-gray-500">${messages[room] ? messages[room].length : 0} messages</span> | |
| `; | |
| roomElement.addEventListener('click', () => { | |
| showJoinRoomModal(room); | |
| }); | |
| availableRooms.appendChild(roomElement); | |
| }); | |
| } | |
| // Update private chats list | |
| function updatePrivateChats() { | |
| privateChats.innerHTML = ''; | |
| const privateChatUsers = Object.keys(privateMessages).filter(user => | |
| user !== currentUser && (privateMessages[user].length > 0 || allUsers.includes(user)) | |
| ); | |
| privateCount.textContent = privateChatUsers.length; | |
| if (privateChatUsers.length === 0) { | |
| privateChats.innerHTML = '<p class="text-gray-500 text-sm italic">No private conversations yet</p>'; | |
| return; | |
| } | |
| privateChatUsers.forEach(user => { | |
| const chatElement = document.createElement('div'); | |
| chatElement.className = 'flex items-center justify-between p-2 hover:bg-gray-100 rounded-lg cursor-pointer transition'; | |
| const lastMessage = privateMessages[user].length > 0 | |
| ? privateMessages[user][privateMessages[user].length - 1].text | |
| : 'No messages yet'; | |
| chatElement.innerHTML = ` | |
| <div class="flex items-center"> | |
| <div class="w-6 h-6 rounded-full bg-purple-500 flex items-center justify-center text-white text-xs mr-2"> | |
| ${user.charAt(0).toUpperCase()} | |
| </div> | |
| <div> | |
| <div class="font-medium">${user}</div> | |
| <div class="text-xs text-gray-500 truncate max-w-xs">${lastMessage}</div> | |
| </div> | |
| </div> | |
| ${unreadCounts.private[user] > 0 ? | |
| `<span class="bg-red-500 text-white text-xs px-1.5 py-0.5 rounded-full">${unreadCounts.private[user]}</span>` : | |
| ''} | |
| `; | |
| chatElement.addEventListener('click', () => { | |
| chatType = 'private'; | |
| selectedChat = user; | |
| switchTab('private'); | |
| updateMessages(); | |
| // Mark as read | |
| if (unreadCounts.private[user]) { | |
| unreadCounts.private[user] = 0; | |
| updateNotifications(); | |
| } | |
| }); | |
| privateChats.appendChild(chatElement); | |
| }); | |
| } | |
| // Update messages display | |
| function updateMessages() { | |
| messagesContainer.innerHTML = ''; | |
| if (!selectedChat) { | |
| messagesContainer.innerHTML = ` | |
| <div class="text-center text-gray-500 py-10"> | |
| <i class="fas fa-comments text-4xl mb-2 text-gray-300"></i> | |
| <p>Select a chat to start messaging</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| let messageList = []; | |
| if (chatType === 'room') { | |
| messageList = messages[selectedChat] || []; | |
| } else { | |
| messageList = privateMessages[selectedChat] || []; | |
| } | |
| if (messageList.length === 0) { | |
| messagesContainer.innerHTML = ` | |
| <div class="text-center text-gray-500 py-10"> | |
| <i class="fas fa-comments text-4xl mb-2 text-gray-300"></i> | |
| <p>No messages in this chat yet</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| messageList.forEach((msg, index) => { | |
| const isCurrentUser = msg.user === currentUser; | |
| const messageElement = document.createElement('div'); | |
| messageElement.className = `mb-4 message-animation ${isCurrentUser ? 'pl-8' : 'pr-8'}`; | |
| messageElement.innerHTML = ` | |
| <div class="flex ${isCurrentUser ? 'justify-end' : ''}"> | |
| <div class="max-w-xs md:max-w-md lg:max-w-lg ${isCurrentUser ? 'bg-indigo-100' : 'bg-white'} p-3 rounded-lg shadow-sm border border-gray-200"> | |
| ${!isCurrentUser ? `<div class="font-medium text-sm mb-1">${msg.user}</div>` : ''} | |
| <div class="text-gray-800">${msg.text}</div> | |
| <div class="text-xs text-gray-500 mt-1 text-right">${msg.time}</div> | |
| </div> | |
| </div> | |
| `; | |
| messagesContainer.appendChild(messageElement); | |
| }); | |
| // Scroll to bottom | |
| messagesContainer.scrollTop = messagesContainer.scrollHeight; | |
| } | |
| // Update chat header | |
| function updateChatHeader() { | |
| if (!selectedChat) { | |
| chatTitle.textContent = 'Select a chat'; | |
| chatTypeIcon.innerHTML = '<i class="fas fa-comments"></i>'; | |
| return; | |
| } | |
| if (chatType === 'room') { | |
| chatTitle.textContent = selectedChat; | |
| chatTypeIcon.innerHTML = '<i class="fas fa-hashtag"></i>'; | |
| } else { | |
| chatTitle.textContent = selectedChat; | |
| chatTypeIcon.innerHTML = '<i class="fas fa-user-friends"></i>'; | |
| } | |
| } | |
| // Update notification badges | |
| function updateNotifications() { | |
| // Room notifications | |
| let totalRoomUnread = 0; | |
| Object.keys(unreadCounts.rooms).forEach(room => { | |
| totalRoomUnread += unreadCounts.rooms[room]; | |
| }); | |
| if (totalRoomUnread > 0) { | |
| roomNotification.textContent = totalRoomUnread; | |
| roomNotification.classList.remove('hidden'); | |
| } else { | |
| roomNotification.classList.add('hidden'); | |
| } | |
| // Private notifications | |
| let totalPrivateUnread = 0; | |
| Object.keys(unreadCounts.private).forEach(user => { | |
| totalPrivateUnread += unreadCounts.private[user]; | |
| }); | |
| if (totalPrivateUnread > 0) { | |
| privateNotification.textContent = totalPrivateUnread; | |
| privateNotification.classList.remove('hidden'); | |
| } else { | |
| privateNotification.classList.add('hidden'); | |
| } | |
| } | |
| // Send message | |
| function sendMessage() { | |
| const messageText = messageInput.value.trim(); | |
| if (!messageText || !selectedChat) return; | |
| // Create new message | |
| const now = new Date(); | |
| const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | |
| const newMessage = { | |
| user: currentUser, | |
| text: messageText, | |
| time: timeString | |
| }; | |
| // Add to messages | |
| if (chatType === 'room') { | |
| if (!messages[selectedChat]) { | |
| messages[selectedChat] = []; | |
| } | |
| messages[selectedChat].push(newMessage); | |
| // Update unread count for other users in the room | |
| // (In a real app, this would be handled by the server) | |
| } else { | |
| if (!privateMessages[selectedChat]) { | |
| privateMessages[selectedChat] = []; | |
| } | |
| privateMessages[selectedChat].push(newMessage); | |
| // Update unread count for the recipient | |
| if (chatType !== 'private' || selectedChat !== currentUser) { | |
| if (!unreadCounts.private[selectedChat]) { | |
| unreadCounts.private[selectedChat] = 0; | |
| } | |
| unreadCounts.private[selectedChat]++; | |
| updateNotifications(); | |
| } | |
| } | |
| // Clear input | |
| messageInput.value = ''; | |
| // Update UI | |
| updateMessages(); | |
| if (chatType === 'private') { | |
| updatePrivateChats(); | |
| } | |
| } | |
| // Show join room modal | |
| function showJoinRoomModal(roomName) { | |
| roomNameDisplay.textContent = roomName; | |
| selectedChat = roomName; | |
| chatType = 'room'; | |
| // Show modal with animation | |
| joinRoomModal.classList.remove('hidden'); | |
| setTimeout(() => { | |
| const modalContent = joinRoomModal.querySelector('div > div'); | |
| modalContent.classList.remove('scale-95', 'opacity-0'); | |
| modalContent.classList.add('scale-100', 'opacity-100'); | |
| }, 10); | |
| } | |
| // Hide join room modal | |
| function hideJoinRoomModal() { | |
| const modalContent = joinRoomModal.querySelector('div > div'); | |
| modalContent.classList.remove('scale-100', 'opacity-100'); | |
| modalContent.classList.add('scale-95', 'opacity-0'); | |
| setTimeout(() => { | |
| joinRoomModal.classList.add('hidden'); | |
| }, 300); | |
| } | |
| // Join room | |
| function joinRoom() { | |
| if (!selectedChat || currentRooms.includes(selectedChat)) { | |
| hideJoinRoomModal(); | |
| return; | |
| } | |
| currentRooms.push(selectedChat); | |
| updateUI(); | |
| hideJoinRoomModal(); | |
| } | |
| // Add new room | |
| function addRoom() { | |
| const roomName = newRoomInput.value.trim(); | |
| if (!roomName) return; | |
| // Check if room already exists | |
| if (allRooms.includes(roomName)) { | |
| alert('Room already exists!'); | |
| return; | |
| } | |
| // Add new room | |
| allRooms.push(roomName); | |
| newRoomInput.value = ''; | |
| // Update UI | |
| updateAvailableRooms(); | |
| // Show join modal for the new room | |
| showJoinRoomModal(roomName); | |
| } | |
| // Event Listeners | |
| loginBtn.addEventListener('click', login); | |
| logoutBtn.addEventListener('click', logout); | |
| // Allow login with Enter key | |
| usernameInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') login(); | |
| }); | |
| sendBtn.addEventListener('click', sendMessage); | |
| // Allow sending with Enter key | |
| messageInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') sendMessage(); | |
| }); | |
| addRoomBtn.addEventListener('click', addRoom); | |
| // Allow adding room with Enter key | |
| newRoomInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') addRoom(); | |
| }); | |
| closeModalBtn.addEventListener('click', hideJoinRoomModal); | |
| cancelJoinBtn.addEventListener('click', hideJoinRoomModal); | |
| confirmJoinBtn.addEventListener('click', joinRoom); | |
| // Close modal when clicking outside | |
| joinRoomModal.addEventListener('click', (e) => { | |
| if (e.target === joinRoomModal) { | |
| hideJoinRoomModal(); | |
| } | |
| }); | |
| // Tab switching | |
| roomTab.addEventListener('click', () => switchTab('room')); | |
| privateTab.addEventListener('click', () => switchTab('private')); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=swordmeister-zoro/modern-chat-interface" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |