chat-app / index.html
Fdgg55's picture
Add 3 files
9f1ba64 verified
Raw
History Blame Contribute Delete
20.9 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Real-Time Chat App</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-database.js"></script>
<style>
.message-container {
scrollbar-width: thin;
scrollbar-color: #4B5563 #1F2937;
}
.message-container::-webkit-scrollbar {
width: 8px;
}
.message-container::-webkit-scrollbar-track {
background: #1F2937;
}
.message-container::-webkit-scrollbar-thumb {
background-color: #4B5563;
border-radius: 4px;
}
.typing-indicator::after {
content: "...";
animation: typing 1.5s infinite;
}
@keyframes typing {
0% { content: "."; }
33% { content: ".."; }
66% { content: "..."; }
}
.fade-in {
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col">
<!-- Firebase Configuration -->
<script>
// Your Firebase configuration
const firebaseConfig = {
apiKey: "AIzaSyB5XQ7j5X5X5X5X5X5X5X5X5X5X5X5X5X5",
authDomain: "realtime-chat-app-12345.firebaseapp.com",
databaseURL: "https://realtime-chat-app-12345.firebaseio.com",
projectId: "realtime-chat-app-12345",
storageBucket: "realtime-chat-app-12345.appspot.com",
messagingSenderId: "123456789012",
appId: "1:123456789012:web:5X5X5X5X5X5X5X5X5X5X5X"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const database = firebase.database();
</script>
<header class="bg-gray-800 py-4 px-6 shadow-lg">
<div class="container mx-auto flex justify-between items-center">
<h1 class="text-2xl font-bold text-blue-400">Real-Time Chat</h1>
<div id="connection-status" class="flex items-center">
<span class="h-3 w-3 rounded-full bg-red-500 mr-2"></span>
<span>Disconnected</span>
</div>
</div>
</header>
<main class="flex-1 container mx-auto p-4 flex flex-col md:flex-row gap-6">
<!-- Connection Panel -->
<div class="bg-gray-800 rounded-lg p-6 w-full md:w-1/3 lg:w-1/4 shadow-lg">
<div class="mb-6">
<h2 class="text-xl font-semibold mb-4">Your Profile</h2>
<div class="space-y-4">
<div>
<label for="user-id" class="block text-sm font-medium mb-1">Your Unique ID</label>
<div class="flex">
<input type="text" id="user-id" readonly class="bg-gray-700 text-gray-200 rounded-l-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500">
<button onclick="copyUserId()" class="bg-blue-600 hover:bg-blue-700 px-3 rounded-r-md flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
</button>
</div>
</div>
<div>
<label for="display-name" class="block text-sm font-medium mb-1">Display Name</label>
<input type="text" id="display-name" placeholder="Enter your name" class="bg-gray-700 text-gray-200 rounded-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<button onclick="generateUserId()" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md transition duration-200">
Generate New ID
</button>
</div>
</div>
<div>
<h2 class="text-xl font-semibold mb-4">Connect to Friend</h2>
<div class="space-y-4">
<div>
<label for="friend-id" class="block text-sm font-medium mb-1">Friend's ID</label>
<input type="text" id="friend-id" placeholder="Enter friend's ID" class="bg-gray-700 text-gray-200 rounded-md px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<button onclick="connectToFriend()" id="connect-button" class="w-full bg-green-600 hover:bg-green-700 text-white py-2 px-4 rounded-md transition duration-200">
Connect
</button>
<button onclick="disconnectFromFriend()" id="disconnect-button" class="w-full bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded-md transition duration-200 hidden">
Disconnect
</button>
</div>
</div>
</div>
<!-- Chat Panel -->
<div class="bg-gray-800 rounded-lg p-6 flex-1 shadow-lg flex flex-col">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Chat</h2>
<div id="chat-status" class="text-sm text-gray-400">
Not connected to anyone
</div>
</div>
<div id="message-container" class="message-container flex-1 overflow-y-auto mb-4 space-y-3 p-2 bg-gray-700 rounded-lg">
<div class="text-center text-gray-400 py-10">
Connect with a friend to start chatting
</div>
</div>
<div id="typing-indicator" class="typing-indicator text-sm text-gray-400 mb-2 h-5 hidden">
Friend is typing
</div>
<div class="flex gap-2">
<input type="text" id="message-input" placeholder="Type a message..." disabled class="flex-1 bg-gray-700 text-gray-200 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
<button onclick="sendMessage()" id="send-button" disabled class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md transition duration-200 disabled:opacity-50">
Send
</button>
</div>
</div>
</main>
<script>
// Global variables
let userId = '';
let friendId = '';
let displayName = 'Anonymous';
let isConnected = false;
let typingTimeout;
let chatRef;
let typingRef;
let userRef;
// Initialize the app
function init() {
// Check if user ID exists in localStorage
if (localStorage.getItem('chatUserId')) {
userId = localStorage.getItem('chatUserId');
document.getElementById('user-id').value = userId;
} else {
generateUserId();
}
// Check if display name exists
if (localStorage.getItem('chatDisplayName')) {
displayName = localStorage.getItem('chatDisplayName');
document.getElementById('display-name').value = displayName;
}
// Set up event listeners
document.getElementById('display-name').addEventListener('input', function() {
displayName = this.value || 'Anonymous';
localStorage.setItem('chatDisplayName', displayName);
if (isConnected) {
// Update display name in the database
userRef.update({
name: displayName
});
}
});
document.getElementById('message-input').addEventListener('input', function() {
if (isConnected) {
// User is typing
typingRef.set(true);
// Clear previous timeout if it exists
if (typingTimeout) clearTimeout(typingTimeout);
// Set timeout to indicate typing stopped
typingTimeout = setTimeout(() => {
typingRef.set(false);
}, 1000);
}
});
document.getElementById('message-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
}
// Generate a new user ID
function generateUserId() {
userId = 'user-' + Math.random().toString(36).substr(2, 9);
document.getElementById('user-id').value = userId;
localStorage.setItem('chatUserId', userId);
// Disconnect if currently connected
if (isConnected) {
disconnectFromFriend();
}
}
// Copy user ID to clipboard
function copyUserId() {
const userIdInput = document.getElementById('user-id');
userIdInput.select();
document.execCommand('copy');
// Show feedback
const copyButton = userIdInput.nextElementSibling;
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
`;
setTimeout(() => {
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
`;
}, 2000);
}
// Connect to a friend
function connectToFriend() {
friendId = document.getElementById('friend-id').value.trim();
if (!friendId) {
alert('Please enter your friend\'s ID');
return;
}
if (friendId === userId) {
alert('You cannot connect to yourself');
return;
}
// Check if friend exists
const friendRef = database.ref('users/' + friendId);
friendRef.once('value').then((snapshot) => {
if (snapshot.exists()) {
// Friend exists, establish connection
isConnected = true;
// Update UI
document.getElementById('connect-button').classList.add('hidden');
document.getElementById('disconnect-button').classList.remove('hidden');
document.getElementById('message-input').disabled = false;
document.getElementById('send-button').disabled = false;
document.getElementById('friend-id').disabled = true;
// Update status
document.getElementById('connection-status').innerHTML = `
<span class="h-3 w-3 rounded-full bg-green-500 mr-2"></span>
<span>Connected to ${snapshot.val().name || 'Friend'}</span>
`;
document.getElementById('chat-status').textContent = `Chatting with ${snapshot.val().name || 'Friend'}`;
// Clear message container
document.getElementById('message-container').innerHTML = '';
// Create chat reference
const chatId = [userId, friendId].sort().join('-');
chatRef = database.ref('chats/' + chatId);
// Create typing reference
typingRef = database.ref('typing/' + chatId + '/' + userId);
// Create user reference
userRef = database.ref('users/' + userId);
// Set user data
userRef.set({
name: displayName,
connectedTo: friendId,
lastSeen: firebase.database.ServerValue.TIMESTAMP
});
// Listen for messages
chatRef.limitToLast(100).on('child_added', (snapshot) => {
const message = snapshot.val();
addMessageToChat(message);
});
// Listen for typing indicators from friend
database.ref('typing/' + chatId + '/' + friendId).on('value', (snapshot) => {
const isTyping = snapshot.val();
const typingIndicator = document.getElementById('typing-indicator');
if (isTyping) {
typingIndicator.classList.remove('hidden');
} else {
typingIndicator.classList.add('hidden');
}
});
// Listen for friend disconnection
database.ref('users/' + friendId + '/connectedTo').on('value', (snapshot) => {
if (snapshot.val() !== userId) {
// Friend disconnected
disconnectFromFriend();
alert('Your friend has disconnected');
}
});
} else {
alert('Friend not found. Please check the ID and try again.');
}
}).catch((error) => {
console.error('Error connecting to friend:', error);
alert('Error connecting to friend. Please try again.');
});
}
// Disconnect from friend
function disconnectFromFriend() {
if (!isConnected) return;
isConnected = false;
// Update UI
document.getElementById('connect-button').classList.remove('hidden');
document.getElementById('disconnect-button').classList.add('hidden');
document.getElementById('message-input').disabled = true;
document.getElementById('send-button').disabled = true;
document.getElementById('friend-id').disabled = false;
// Update status
document.getElementById('connection-status').innerHTML = `
<span class="h-3 w-3 rounded-full bg-red-500 mr-2"></span>
<span>Disconnected</span>
`;
document.getElementById('chat-status').textContent = 'Not connected to anyone';
document.getElementById('typing-indicator').classList.add('hidden');
// Add disconnect message to chat
addSystemMessage('Disconnected from chat');
// Clean up Firebase references
if (chatRef) chatRef.off();
if (typingRef) typingRef.off();
if (userRef) {
userRef.update({
connectedTo: null,
lastSeen: firebase.database.ServerValue.TIMESTAMP
});
}
// Clear typing status
if (typingRef) typingRef.set(false);
}
// Send a message
function sendMessage() {
const messageInput = document.getElementById('message-input');
const messageText = messageInput.value.trim();
if (!messageText || !isConnected) return;
// Create message object
const message = {
sender: userId,
senderName: displayName,
text: messageText,
timestamp: firebase.database.ServerValue.TIMESTAMP
};
// Push message to database
chatRef.push(message);
// Clear input
messageInput.value = '';
// Stop typing indicator
typingRef.set(false);
}
// Add message to chat UI
function addMessageToChat(message) {
const messageContainer = document.getElementById('message-container');
// Remove "not connected" message if it exists
if (messageContainer.children.length === 1 && messageContainer.children[0].classList.contains('text-center')) {
messageContainer.innerHTML = '';
}
const messageElement = document.createElement('div');
messageElement.classList.add('fade-in');
if (message.sender === userId) {
// Your message
messageElement.innerHTML = `
<div class="flex justify-end">
<div class="bg-blue-600 text-white rounded-l-lg rounded-br-lg px-4 py-2 max-w-xs md:max-w-md lg:max-w-lg">
<div class="text-xs text-blue-200 mb-1">You</div>
<div>${message.text}</div>
</div>
</div>
`;
} else if (message.sender === 'system') {
// System message
messageElement.innerHTML = `
<div class="text-center text-gray-400 text-sm py-2">
${message.text}
</div>
`;
} else {
// Friend's message
messageElement.innerHTML = `
<div class="flex justify-start">
<div class="bg-gray-700 text-white rounded-r-lg rounded-bl-lg px-4 py-2 max-w-xs md:max-w-md lg:max-w-lg">
<div class="text-xs text-gray-300 mb-1">${message.senderName || 'Friend'}</div>
<div>${message.text}</div>
</div>
</div>
`;
}
messageContainer.appendChild(messageElement);
messageContainer.scrollTop = messageContainer.scrollHeight;
}
// Add system message
function addSystemMessage(text) {
addMessageToChat({
sender: 'system',
text: text,
timestamp: firebase.database.ServerValue.TIMESTAMP
});
}
// Initialize the app when the page loads
window.onload = init;
</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=Fdgg55/chat-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>