868 lines
29 KiB
HTML
868 lines
29 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Secure Chat App</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
height: 100vh;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.container {
|
|
width: 90%;
|
|
max-width: 1200px;
|
|
height: 90vh;
|
|
background: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
display: flex;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.auth-container {
|
|
width: 400px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
padding: 40px;
|
|
border-radius: 15px;
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.auth-tabs {
|
|
display: flex;
|
|
margin-bottom: 30px;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.auth-tab {
|
|
flex: 1;
|
|
padding: 15px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
border-bottom: 3px solid transparent;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.auth-tab.active {
|
|
border-bottom-color: #3498db;
|
|
color: #3498db;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.auth-form {
|
|
display: none;
|
|
}
|
|
|
|
.auth-form.active {
|
|
display: block;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 500;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 15px 20px;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 10px;
|
|
font-size: 16px;
|
|
outline: none;
|
|
transition: border-color 0.3s ease;
|
|
}
|
|
|
|
.form-control:focus {
|
|
border-color: #3498db;
|
|
}
|
|
|
|
.btn {
|
|
width: 100%;
|
|
padding: 15px;
|
|
background: linear-gradient(135deg, #3498db, #2980b9);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
|
|
}
|
|
|
|
.alert {
|
|
padding: 12px 16px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
display: none;
|
|
}
|
|
|
|
.alert-success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
|
|
.alert-error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
|
|
/* Chat styles remain the same as before */
|
|
.chat-container {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: none;
|
|
}
|
|
|
|
.users-panel {
|
|
width: 300px;
|
|
background: #2c3e50;
|
|
color: white;
|
|
padding: 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.users-panel h2 {
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
font-weight: 300;
|
|
border-bottom: 1px solid #34495e;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.user-list {
|
|
list-style: none;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.user-item {
|
|
padding: 15px;
|
|
margin: 8px 0;
|
|
background: #34495e;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
border-left: 4px solid transparent;
|
|
}
|
|
|
|
.user-item:hover {
|
|
background: #3d566e;
|
|
transform: translateX(5px);
|
|
}
|
|
|
|
.user-item.active {
|
|
background: #3498db;
|
|
border-left-color: #2980b9;
|
|
}
|
|
|
|
.chat-panel {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.chat-header {
|
|
padding: 20px;
|
|
background: white;
|
|
border-bottom: 1px solid #e9ecef;
|
|
text-align: center;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.logout-btn {
|
|
padding: 8px 16px;
|
|
background: #e74c3c;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background: #c0392b;
|
|
}
|
|
|
|
.chat-messages {
|
|
flex: 1;
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.message {
|
|
max-width: 70%;
|
|
margin: 15px 0;
|
|
padding: 15px 20px;
|
|
border-radius: 20px;
|
|
position: relative;
|
|
word-wrap: break-word;
|
|
animation: fadeIn 0.3s ease;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.incoming {
|
|
background: white;
|
|
border: 1px solid #e9ecef;
|
|
align-self: flex-start;
|
|
border-bottom-left-radius: 5px;
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.outgoing {
|
|
background: linear-gradient(135deg, #3498db, #2980b9);
|
|
color: white;
|
|
align-self: flex-end;
|
|
margin-left: auto;
|
|
border-bottom-right-radius: 5px;
|
|
box-shadow: 0 2px 5px rgba(52, 152, 219, 0.3);
|
|
}
|
|
|
|
.message-sender {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.message-content {
|
|
margin: 8px 0;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.message-time {
|
|
font-size: 11px;
|
|
text-align: right;
|
|
opacity: 0.7;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.chat-input-area {
|
|
padding: 20px;
|
|
background: white;
|
|
border-top: 1px solid #e9ecef;
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: center;
|
|
}
|
|
|
|
.message-input {
|
|
flex: 1;
|
|
padding: 15px 20px;
|
|
border: 2px solid #e9ecef;
|
|
border-radius: 25px;
|
|
outline: none;
|
|
font-size: 14px;
|
|
transition: border-color 0.3s ease;
|
|
}
|
|
|
|
.message-input:focus {
|
|
border-color: #3498db;
|
|
}
|
|
|
|
.send-button {
|
|
padding: 15px 25px;
|
|
background: linear-gradient(135deg, #3498db, #2980b9);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 25px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.send-button:hover:not(:disabled) {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
|
|
}
|
|
|
|
.send-button:disabled {
|
|
background: #bdc3c7;
|
|
cursor: not-allowed;
|
|
transform: none;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.system-message {
|
|
text-align: center;
|
|
color: #7f8c8d;
|
|
font-style: italic;
|
|
margin: 15px 0;
|
|
font-size: 13px;
|
|
padding: 10px;
|
|
background: #f8f9fa;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #c1c1c1;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #a8a8a8;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Authentication Container -->
|
|
<div id="authContainer" class="auth-container">
|
|
<div class="auth-tabs">
|
|
<div class="auth-tab active" onclick="showTab('login')">Login</div>
|
|
<div class="auth-tab" onclick="showTab('register')">Register</div>
|
|
</div>
|
|
|
|
<div id="alert" class="alert" style="display: none;"></div>
|
|
|
|
<!-- Login Form -->
|
|
<div id="loginForm" class="auth-form active">
|
|
<div class="form-group">
|
|
<label for="loginPhone">Phone Number</label>
|
|
<input type="text" id="loginPhone" class="form-control" placeholder="+1234567890">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="loginPassword">Password</label>
|
|
<input type="password" id="loginPassword" class="form-control" placeholder="Enter your password">
|
|
</div>
|
|
<button class="btn" onclick="login()">Login</button>
|
|
</div>
|
|
|
|
<!-- Register Form -->
|
|
<div id="registerForm" class="auth-form">
|
|
<div class="form-group">
|
|
<label for="registerPhone">Phone Number</label>
|
|
<input type="text" id="registerPhone" class="form-control" placeholder="+1234567890">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="registerUsername">Username</label>
|
|
<input type="text" id="registerUsername" class="form-control" placeholder="Choose a username">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="registerPassword">Password</label>
|
|
<input type="password" id="registerPassword" class="form-control" placeholder="Choose a password (min 6 characters)">
|
|
</div>
|
|
<button class="btn" onclick="register()">Register</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Chat Container -->
|
|
<div class="container" style="display: none;" id="chatContainer">
|
|
<div class="users-panel">
|
|
<h2>Online Users</h2>
|
|
<ul class="user-list" id="userList"></ul>
|
|
</div>
|
|
|
|
<div class="chat-panel">
|
|
<div class="chat-header">
|
|
<span id="chatHeader">Select a user to start chatting</span>
|
|
<button class="logout-btn" onclick="logout()">Logout</button>
|
|
</div>
|
|
|
|
<div class="chat-messages" id="chatMessages"></div>
|
|
|
|
<div class="chat-input-area">
|
|
<input type="text" id="messageInput" class="message-input" placeholder="Type a message..." disabled>
|
|
<button onclick="sendMessage()" class="send-button" id="sendButton" disabled>Send</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let ws = null;
|
|
let currentUser = '';
|
|
let currentToken = '';
|
|
let currentRecipient = '';
|
|
let userList = [];
|
|
let messageHistory = {};
|
|
|
|
// Check for saved session on page load
|
|
window.addEventListener('DOMContentLoaded', function() {
|
|
const savedToken = localStorage.getItem('chatToken');
|
|
const savedUsername = localStorage.getItem('chatUsername');
|
|
|
|
if (savedToken && savedUsername) {
|
|
validateToken(savedToken, savedUsername);
|
|
}
|
|
});
|
|
|
|
function showTab(tabName) {
|
|
// Update tabs
|
|
document.querySelectorAll('.auth-tab').forEach(tab => {
|
|
tab.classList.remove('active');
|
|
});
|
|
document.querySelectorAll('.auth-form').forEach(form => {
|
|
form.classList.remove('active');
|
|
});
|
|
|
|
// Show selected tab
|
|
if (tabName === 'login') {
|
|
document.querySelector('.auth-tab:nth-child(1)').classList.add('active');
|
|
document.getElementById('loginForm').classList.add('active');
|
|
} else {
|
|
document.querySelector('.auth-tab:nth-child(2)').classList.add('active');
|
|
document.getElementById('registerForm').classList.add('active');
|
|
}
|
|
|
|
// Clear alerts
|
|
hideAlert();
|
|
}
|
|
|
|
function showAlert(message, type) {
|
|
const alert = document.getElementById('alert');
|
|
alert.textContent = message;
|
|
alert.className = `alert alert-${type}`;
|
|
alert.style.display = 'block';
|
|
}
|
|
|
|
function hideAlert() {
|
|
document.getElementById('alert').style.display = 'none';
|
|
}
|
|
|
|
async function validateToken(token, username) {
|
|
try {
|
|
const response = await fetch('/api/validate-token', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ token }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
currentUser = data.username;
|
|
currentToken = token;
|
|
|
|
// Save to localStorage
|
|
localStorage.setItem('chatToken', token);
|
|
localStorage.setItem('chatUsername', data.username);
|
|
|
|
// Hide auth, show chat
|
|
document.getElementById('authContainer').style.display = 'none';
|
|
document.getElementById('chatContainer').style.display = 'flex';
|
|
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
|
|
|
|
// Connect to WebSocket
|
|
connectWebSocket();
|
|
} else {
|
|
// Clear invalid token
|
|
localStorage.removeItem('chatToken');
|
|
localStorage.removeItem('chatUsername');
|
|
showAlert('Session expired. Please login again.', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('Token validation failed:', error);
|
|
localStorage.removeItem('chatToken');
|
|
localStorage.removeItem('chatUsername');
|
|
showAlert('Session validation failed. Please login again.', 'error');
|
|
}
|
|
}
|
|
|
|
async function register() {
|
|
const phone = document.getElementById('registerPhone').value.trim();
|
|
const username = document.getElementById('registerUsername').value.trim();
|
|
const password = document.getElementById('registerPassword').value;
|
|
|
|
if (!phone || !username || !password) {
|
|
showAlert('Please fill all fields', 'error');
|
|
return;
|
|
}
|
|
|
|
if (password.length < 6) {
|
|
showAlert('Password must be at least 6 characters', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/register', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ phone, username, password }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
// Save token and username to localStorage
|
|
localStorage.setItem('chatToken', data.token);
|
|
localStorage.setItem('chatUsername', data.username);
|
|
|
|
currentUser = data.username;
|
|
currentToken = data.token;
|
|
|
|
// Hide auth, show chat
|
|
document.getElementById('authContainer').style.display = 'none';
|
|
document.getElementById('chatContainer').style.display = 'flex';
|
|
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
|
|
|
|
// Connect to WebSocket
|
|
connectWebSocket();
|
|
|
|
showAlert('Registration successful!', 'success');
|
|
} else {
|
|
showAlert(data.message, 'error');
|
|
}
|
|
} catch (error) {
|
|
showAlert('Registration failed. Please try again.', 'error');
|
|
}
|
|
}
|
|
|
|
async function login() {
|
|
const phone = document.getElementById('loginPhone').value.trim();
|
|
const password = document.getElementById('loginPassword').value;
|
|
|
|
if (!phone || !password) {
|
|
showAlert('Please fill all fields', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/login', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ phone, password }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.success) {
|
|
// Save token and username to localStorage
|
|
localStorage.setItem('chatToken', data.token);
|
|
localStorage.setItem('chatUsername', data.username);
|
|
|
|
currentUser = data.username;
|
|
currentToken = data.token;
|
|
|
|
// Hide auth, show chat
|
|
document.getElementById('authContainer').style.display = 'none';
|
|
document.getElementById('chatContainer').style.display = 'flex';
|
|
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
|
|
|
|
// Connect to WebSocket
|
|
connectWebSocket();
|
|
} else {
|
|
showAlert(data.message, 'error');
|
|
}
|
|
} catch (error) {
|
|
showAlert('Login failed. Please try again.', 'error');
|
|
}
|
|
}
|
|
|
|
function logout() {
|
|
// Clear localStorage
|
|
localStorage.removeItem('chatToken');
|
|
localStorage.removeItem('chatUsername');
|
|
|
|
// Close WebSocket connection
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
|
|
// Reset state
|
|
currentUser = '';
|
|
currentToken = '';
|
|
currentRecipient = '';
|
|
userList = [];
|
|
messageHistory = {};
|
|
|
|
// Show auth, hide chat
|
|
document.getElementById('authContainer').style.display = 'block';
|
|
document.getElementById('chatContainer').style.display = 'none';
|
|
|
|
// Clear forms
|
|
document.getElementById('loginPhone').value = '';
|
|
document.getElementById('loginPassword').value = '';
|
|
document.getElementById('registerPhone').value = '';
|
|
document.getElementById('registerUsername').value = '';
|
|
document.getElementById('registerPassword').value = '';
|
|
|
|
showTab('login');
|
|
}
|
|
|
|
function connectWebSocket() {
|
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
|
|
|
ws = new WebSocket(wsUrl);
|
|
|
|
ws.onopen = () => {
|
|
console.log('WebSocket connected');
|
|
// Send authentication
|
|
ws.send(JSON.stringify({
|
|
type: 'auth',
|
|
token: currentToken
|
|
}));
|
|
|
|
addSystemMessage('Connected to chat server');
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
console.log('Received message:', event.data);
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
|
|
if (data.type === 'user_list') {
|
|
updateUserList(data.users);
|
|
} else if (data.type === 'error') {
|
|
showAlert(data.content, 'error');
|
|
// If authentication error, logout
|
|
if (data.content.includes('Invalid') || data.content.includes('expired') || data.content.includes('auth')) {
|
|
logout();
|
|
}
|
|
} else if (data.type === 'system') {
|
|
addSystemMessage(data.content);
|
|
} else {
|
|
// Regular chat message
|
|
handleChatMessage(data);
|
|
}
|
|
} catch (e) {
|
|
console.error('Error parsing message:', e, 'Raw data:', event.data);
|
|
}
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
console.log('WebSocket disconnected');
|
|
addSystemMessage('Disconnected from server');
|
|
document.getElementById('sendButton').disabled = true;
|
|
document.getElementById('messageInput').disabled = true;
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket error:', error);
|
|
addSystemMessage('Connection error');
|
|
};
|
|
}
|
|
|
|
function updateUserList(users) {
|
|
userList = users.filter(user => user !== currentUser);
|
|
const userListElement = document.getElementById('userList');
|
|
userListElement.innerHTML = '';
|
|
|
|
userList.forEach(user => {
|
|
const li = document.createElement('li');
|
|
li.className = 'user-item';
|
|
li.textContent = user;
|
|
li.onclick = () => selectUser(user);
|
|
userListElement.appendChild(li);
|
|
});
|
|
|
|
// Auto-select first user if none selected
|
|
if (!currentRecipient && userList.length > 0) {
|
|
selectUser(userList[0]);
|
|
}
|
|
}
|
|
|
|
function selectUser(user) {
|
|
currentRecipient = user;
|
|
|
|
// Update active state in user list
|
|
document.querySelectorAll('.user-item').forEach(item => {
|
|
item.classList.remove('active');
|
|
if (item.textContent === user) {
|
|
item.classList.add('active');
|
|
}
|
|
});
|
|
|
|
// Update chat header
|
|
document.getElementById('chatHeader').textContent = `Chat with ${user}`;
|
|
|
|
// Enable input
|
|
document.getElementById('sendButton').disabled = false;
|
|
document.getElementById('messageInput').disabled = false;
|
|
document.getElementById('messageInput').focus();
|
|
|
|
// Display chat history with this user
|
|
displayChatHistory(user);
|
|
}
|
|
|
|
function displayChatHistory(user) {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
chatMessages.innerHTML = '';
|
|
|
|
if (messageHistory[user]) {
|
|
messageHistory[user].forEach(msg => {
|
|
displayMessage(msg, false);
|
|
});
|
|
}
|
|
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
function handleChatMessage(msg) {
|
|
console.log('Handling chat message:', msg);
|
|
const otherUser = msg.from_user === currentUser ? msg.to_user : msg.from_user;
|
|
|
|
if (!messageHistory[otherUser]) {
|
|
messageHistory[otherUser] = [];
|
|
}
|
|
|
|
// Check if message already exists to avoid duplicates
|
|
const messageExists = messageHistory[otherUser].some(
|
|
existingMsg => existingMsg.id === msg.id
|
|
);
|
|
|
|
if (!messageExists) {
|
|
messageHistory[otherUser].push(msg);
|
|
}
|
|
|
|
if (otherUser === currentRecipient) {
|
|
displayMessage(msg, true);
|
|
}
|
|
}
|
|
|
|
function displayMessage(msg, scrollToBottom) {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
const messageDiv = document.createElement('div');
|
|
|
|
if (msg.message_type === 'system') {
|
|
messageDiv.className = 'system-message';
|
|
messageDiv.textContent = msg.content;
|
|
} else {
|
|
const isOutgoing = msg.from_user === currentUser;
|
|
messageDiv.className = `message ${isOutgoing ? 'outgoing' : 'incoming'}`;
|
|
|
|
if (!isOutgoing) {
|
|
const senderDiv = document.createElement('div');
|
|
senderDiv.className = 'message-sender';
|
|
senderDiv.textContent = msg.from_user;
|
|
messageDiv.appendChild(senderDiv);
|
|
}
|
|
|
|
const contentDiv = document.createElement('div');
|
|
contentDiv.className = 'message-content';
|
|
contentDiv.textContent = msg.content;
|
|
messageDiv.appendChild(contentDiv);
|
|
|
|
const timeDiv = document.createElement('div');
|
|
timeDiv.className = 'message-time';
|
|
const date = new Date(msg.timestamp);
|
|
timeDiv.textContent = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
messageDiv.appendChild(timeDiv);
|
|
}
|
|
|
|
chatMessages.appendChild(messageDiv);
|
|
|
|
if (scrollToBottom) {
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
}
|
|
|
|
function addSystemMessage(content) {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
const systemDiv = document.createElement('div');
|
|
systemDiv.className = 'system-message';
|
|
systemDiv.textContent = content;
|
|
chatMessages.appendChild(systemDiv);
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
|
|
function sendMessage() {
|
|
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
showAlert('Please connect to the server first', 'error');
|
|
return;
|
|
}
|
|
|
|
if (!currentRecipient) {
|
|
showAlert('Please select a user to chat with', 'error');
|
|
return;
|
|
}
|
|
|
|
const input = document.getElementById('messageInput');
|
|
const content = input.value.trim();
|
|
|
|
if (!content) {
|
|
return;
|
|
}
|
|
|
|
const message = {
|
|
id: generateId(),
|
|
from_user: currentUser, // This will be overwritten by server, but needed for structure
|
|
to_user: currentRecipient,
|
|
content: content,
|
|
timestamp: Date.now(),
|
|
message_type: 'text'
|
|
};
|
|
|
|
console.log('Sending message:', message);
|
|
ws.send(JSON.stringify(message));
|
|
input.value = '';
|
|
}
|
|
|
|
function generateId() {
|
|
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
}
|
|
|
|
// Event listeners for Enter key
|
|
document.getElementById('messageInput').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
sendMessage();
|
|
}
|
|
});
|
|
|
|
document.getElementById('loginPassword').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
login();
|
|
}
|
|
});
|
|
|
|
document.getElementById('registerPassword').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
register();
|
|
}
|
|
});
|
|
|
|
// Focus phone input on load
|
|
document.getElementById('loginPhone').focus();
|
|
</script>
|
|
</body>
|
|
</html> |