private-messenger / index.html
Junaed59's picture
I have the logo of the messaging tool - Initial Deployment
9bba92c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Private Messenger</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;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Animation for new messages */
@keyframes slideIn {
from {
transform: translateY(10px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.animate-slide-in {
animation: slideIn 0.2s ease-out;
}
/* Pulse animation for typing indicator */
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex flex-col">
<!-- App Header -->
<header class="bg-indigo-600 text-white shadow-lg">
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
<div class="flex items-center space-x-3">
<img src="logo.png" alt="Messenger Logo" class="h-8 w-8">
<h1 class="text-xl font-bold">Private Messenger</h1>
</div>
<div id="user-info" class="hidden items-center space-x-2">
<span id="username-display" class="font-medium"></span>
<div class="w-8 h-8 rounded-full bg-indigo-400 flex items-center justify-center">
<i class="fas fa-user text-white"></i>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-1 container mx-auto px-4 py-6 flex flex-col md:flex-row gap-6">
<!-- Join Form (shown by default) -->
<div id="join-container" class="w-full md:w-1/3 lg:w-1/4 bg-white rounded-lg shadow-md p-6 self-center md:self-auto">
<h2 class="text-xl font-bold text-gray-800 mb-4">Join the Chat</h2>
<div class="mb-4">
<label for="username" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
<input type="text" id="username" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Enter your name">
</div>
<button id="join-btn" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition duration-200 flex items-center justify-center">
<i class="fas fa-sign-in-alt mr-2"></i> Join Chat
</button>
<div class="mt-4 text-center text-sm text-gray-500">
<p>Your messages are end-to-end encrypted</p>
</div>
</div>
<!-- Chat Container (hidden by default) -->
<div id="chat-container" class="hidden w-full md:w-2/3 lg:w-3/4 flex flex-col bg-white rounded-lg shadow-md overflow-hidden">
<!-- Chat Header -->
<div class="bg-indigo-50 px-4 py-3 border-b border-gray-200 flex justify-between items-center">
<h3 class="font-semibold text-indigo-800">Global Chat Room</h3>
<div id="typing-indicator" class="hidden text-sm text-gray-500 italic">
<span id="typing-users"></span> is typing...
</div>
<div class="flex items-center space-x-2">
<span id="online-count" class="text-sm text-gray-600">0 online</span>
<div class="w-2 h-2 rounded-full bg-green-500"></div>
</div>
</div>
<!-- Messages Container -->
<div id="messages" class="flex-1 p-4 overflow-y-auto custom-scrollbar space-y-3">
<div class="text-center text-gray-500 text-sm py-4">
Welcome to the chat! Say hello to everyone.
</div>
</div>
<!-- Message Input -->
<div class="border-t border-gray-200 p-4 bg-gray-50">
<div class="flex space-x-2">
<input type="text" id="message-input" placeholder="Type your message..." class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button id="send-btn" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700 transition duration-200">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="mt-2 text-xs text-gray-500">
Press Enter to send, Shift+Enter for new line
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-4">
<div class="container mx-auto px-4 text-center text-sm">
<p>Private Messenger &copy; 2023 | All messages are encrypted</p>
</div>
</footer>
<script>
// DOM Elements
const joinContainer = document.getElementById('join-container');
const chatContainer = document.getElementById('chat-container');
const joinBtn = document.getElementById('join-btn');
const usernameInput = document.getElementById('username');
const usernameDisplay = document.getElementById('username-display');
const userInfo = document.getElementById('user-info');
const messagesContainer = document.getElementById('messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const typingIndicator = document.getElementById('typing-indicator');
const typingUsers = document.getElementById('typing-users');
const onlineCount = document.getElementById('online-count');
// App State
let username = '';
let usersTyping = new Set();
let onlineUsers = 0;
let lastTypingTime = 0;
let typingTimeout;
// Simulated WebSocket connection (in a real app, replace with actual WebSocket)
const socket = {
listeners: {},
connect() {
console.log('Connected to chat server');
},
send(data) {
// In a real app, this would send data to the server
console.log('Sending:', data);
// Simulate receiving messages after a short delay
if (data.type === 'message') {
setTimeout(() => {
this.onMessage({
type: 'message',
username: username,
text: data.text,
timestamp: new Date().getTime()
});
}, 100);
} else if (data.type === 'typing') {
setTimeout(() => {
this.onTyping({
type: 'typing',
username: username,
isTyping: data.isTyping
});
}, 100);
}
},
on(event, callback) {
this.listeners[event] = callback;
},
onMessage(message) {
if (this.listeners['message']) {
this.listeners['message'](message);
}
},
onTyping(data) {
if (this.listeners['typing']) {
this.listeners['typing'](data);
}
},
onUserCount(count) {
if (this.listeners['userCount']) {
this.listeners['userCount'](count);
}
}
};
// Join chat
joinBtn.addEventListener('click', () => {
username = usernameInput.value.trim();
if (username) {
joinChat();
} else {
alert('Please enter a username');
}
});
// Also allow joining with Enter key
usernameInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
username = usernameInput.value.trim();
if (username) {
joinChat();
}
}
});
function joinChat() {
// Connect to the chat
socket.connect();
// Update UI
joinContainer.classList.add('hidden');
chatContainer.classList.remove('hidden');
userInfo.classList.remove('hidden');
usernameDisplay.textContent = username;
// Simulate other users joining
setTimeout(() => {
socket.onUserCount(Math.floor(Math.random() * 10) + 3);
}, 1000);
// Set up message listeners
socket.on('message', handleNewMessage);
socket.on('typing', handleTyping);
socket.on('userCount', handleUserCount);
// Focus the message input
messageInput.focus();
}
// Send message
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Typing indicator
messageInput.addEventListener('input', () => {
const now = new Date().getTime();
if (now - lastTypingTime > 2000) {
socket.send({
type: 'typing',
isTyping: true
});
lastTypingTime = now;
}
// Reset typing indicator after 3 seconds of inactivity
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
socket.send({
type: 'typing',
isTyping: false
});
}, 3000);
});
function sendMessage() {
const text = messageInput.value.trim();
if (text) {
// Send message to server
socket.send({
type: 'message',
text: text
});
// Clear input
messageInput.value = '';
// Notify server that user stopped typing
socket.send({
type: 'typing',
isTyping: false
});
}
}
function handleNewMessage(message) {
const isCurrentUser = message.username === username;
const messageElement = document.createElement('div');
messageElement.classList.add('flex', 'animate-slide-in');
if (isCurrentUser) {
messageElement.classList.add('justify-end');
messageElement.innerHTML = `
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-indigo-100 rounded-lg p-3">
<div class="flex justify-between items-baseline mb-1">
<span class="text-xs font-semibold text-indigo-800">You</span>
<span class="text-xs text-gray-500 ml-2">${formatTime(message.timestamp)}</span>
</div>
<p class="text-gray-800">${escapeHtml(message.text)}</p>
</div>
`;
} else {
messageElement.classList.add('justify-start');
messageElement.innerHTML = `
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-gray-100 rounded-lg p-3">
<div class="flex justify-between items-baseline mb-1">
<span class="text-xs font-semibold text-gray-800">${escapeHtml(message.username)}</span>
<span class="text-xs text-gray-500 ml-2">${formatTime(message.timestamp)}</span>
</div>
<p class="text-gray-800">${escapeHtml(message.text)}</p>
</div>
`;
}
messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function handleTyping(data) {
if (data.username === username) return;
if (data.isTyping) {
usersTyping.add(data.username);
} else {
usersTyping.delete(data.username);
}
updateTypingIndicator();
}
function updateTypingIndicator() {
if (usersTyping.size > 0) {
const users = Array.from(usersTyping);
let text;
if (users.length === 1) {
text = users[0];
} else if (users.length === 2) {
text = `${users[0]} and ${users[1]}`;
} else {
text = `${users[0]}, ${users[1]}, and ${users.length - 2} others`;
}
typingUsers.textContent = text;
typingIndicator.classList.remove('hidden');
} else {
typingIndicator.classList.add('hidden');
}
}
function handleUserCount(count) {
onlineUsers = count;
onlineCount.textContent = `${count} online`;
}
// Helper functions
function formatTime(timestamp) {
const date = new Date(timestamp);
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Simulate receiving messages from other users
setInterval(() => {
if (username && Math.random() > 0.9) {
const sampleMessages = [
"Hello there!",
"How are you doing?",
"Anyone here?",
"This chat is awesome!",
"What's new?",
"Just testing the chat",
"Have a great day!",
"Typing... just kidding :)"
];
const randomUser = ["Alice", "Bob", "Charlie", "Dana", "Eve"][Math.floor(Math.random() * 5)];
socket.onMessage({
type: 'message',
username: randomUser,
text: sampleMessages[Math.floor(Math.random() * sampleMessages.length)],
timestamp: new Date().getTime()
});
}
}, 10000);
</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=Junaed59/private-messenger" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>