Files
msgs/web/index.html
T
2025-11-23 19:01:04 +07:00

877 lines
40 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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-container { width: 100%; height: 100%; display: none; }
.chats-panel { width: 350px; background: #2c3e50; color: white; display: flex; flex-direction: column; }
.chats-header { padding: 20px; border-bottom: 1px solid #34495e; }
.chats-tabs { display: flex; border-bottom: 1px solid #34495e; }
.chat-tab { flex: 1; padding: 15px; text-align: center; cursor: pointer; border-bottom: 3px solid transparent; transition: all 0.3s ease; }
.chat-tab.active { border-bottom-color: #3498db; color: #3498db; font-weight: 600; }
.search-container { padding: 15px; border-bottom: 1px solid #34495e; display: none; }
.search-input { width: 100%; padding: 12px 15px; border: 1px solid #34495e; border-radius: 20px; background: #34495e; color: white; outline: none; }
.search-input::placeholder { color: #95a5a6; }
.search-results { max-height: 300px; overflow-y: auto; }
.chat-list { list-style: none; flex: 1; overflow-y: auto; }
.chat-item { padding: 15px; border-bottom: 1px solid #34495e; cursor: pointer; transition: all 0.3s ease; display: flex; justify-content: space-between; align-items: center; }
.chat-item:hover { background: #34495e; }
.chat-item.active { background: #3498db; }
.chat-info { flex: 1; }
.chat-username { font-weight: 600; margin-bottom: 5px; }
.chat-phone { font-size: 12px; color: #bdc3c7; margin-bottom: 5px; }
.chat-last-message { font-size: 13px; color: #ecf0f1; opacity: 0.8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
.chat-time { font-size: 11px; color: #95a5a6; }
.unread-badge { background: #e74c3c; color: white; border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; }
.chat-panel { flex: 1; display: flex; flex-direction: column; background: #f8f9fa; }
.chat-header { padding: 20px; background: white; border-bottom: 1px solid #e9ecef; 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; }
.empty-state { text-align: center; color: #7f8c8d; padding: 40px 20px; }
.empty-state h3 { margin-bottom: 10px; font-weight: 300; }
/* Стили для кнопки удаления сообщения */
.message-actions { position: absolute; top: 5px; right: 10px; opacity: 0; transition: opacity 0.3s ease; }
.message:hover .message-actions { opacity: 1; }
.delete-btn { background: none; border: none; color: #95a5a6; cursor: pointer; font-size: 14px; padding: 5px; border-radius: 3px; }
.delete-btn:hover { background: rgba(0, 0, 0, 0.1); color: #e74c3c; }
/* Стили для модального окна удаления */
.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 1000; }
.modal { background: white; border-radius: 10px; padding: 20px; max-width: 400px; width: 90%; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); }
.modal h3 { margin-bottom: 15px; color: #2c3e50; }
.modal p { margin-bottom: 20px; color: #7f8c8d; }
.modal-buttons { display: flex; gap: 10px; justify-content: flex-end; }
.modal-btn { padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; }
.modal-btn-cancel { background: #95a5a6; color: white; }
.modal-btn-cancel:hover { background: #7f8c8d; }
.modal-btn-delete { background: #e74c3c; color: white; }
.modal-btn-delete:hover { background: #c0392b; }
.modal-btn-delete-for-me { background: #3498db; color: white; }
.modal-btn-delete-for-me:hover { background: #2980b9; }
::-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; }
.chats-panel ::-webkit-scrollbar-track { background: #34495e; }
.chats-panel ::-webkit-scrollbar-thumb { background: #2c3e50; }
.chats-panel ::-webkit-scrollbar-thumb:hover { background: #1a252f; }
</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="chats-panel">
<div class="chats-header">
<h2>Chats</h2>
</div>
<div class="chats-tabs">
<div class="chat-tab active" onclick="showChatTab('existing')">My Chats</div>
<div class="chat-tab" onclick="showChatTab('search')">New Chat</div>
</div>
<!-- Search Container -->
<div class="search-container" id="searchContainer">
<input type="text" id="searchInput" class="search-input" placeholder="Search by phone or username...">
<div class="search-results" id="searchResults"></div>
</div>
<!-- Existing Chats List -->
<ul class="chat-list" id="chatsList">
<!-- Chats will be populated here -->
</ul>
</div>
<div class="chat-panel">
<div class="chat-header">
<span id="chatHeader">Select a chat to start messaging</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>
<!-- Modal for delete confirmation -->
<div id="deleteModal" class="modal-overlay" style="display: none;">
<div class="modal">
<h3>Delete Message</h3>
<p>How would you like to delete this message?</p>
<div class="modal-buttons">
<button class="modal-btn modal-btn-cancel" onclick="closeDeleteModal()">Cancel</button>
<button class="modal-btn modal-btn-delete-for-me" onclick="deleteMessageForMe()">Delete for me</button>
<button class="modal-btn modal-btn-delete" onclick="deleteMessageForEveryone()">Delete for everyone</button>
</div>
</div>
</div>
<script>
let ws = null;
let currentUser = '';
let currentToken = '';
let currentRecipient = '';
let chats = [];
let searchResults = [];
let messageHistory = {};
let currentChatTab = 'existing';
let messageToDelete = null;
// 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) {
document.querySelectorAll('.auth-tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.auth-form').forEach(form => form.classList.remove('active'));
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');
}
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;
localStorage.setItem('chatToken', token);
localStorage.setItem('chatUsername', data.username);
document.getElementById('authContainer').style.display = 'none';
document.getElementById('chatContainer').style.display = 'flex';
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
loadChats();
connectWebSocket();
} else {
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 loadChats() {
try {
console.log('Loading chats for user:', currentUser);
const response = await fetch('/api/chats', {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
console.log('Chats response status:', response.status);
if (response.ok) {
chats = await response.json();
console.log('Loaded chats:', chats);
renderChatsList();
} else {
console.error('Failed to load chats, status:', response.status);
}
} catch (error) {
console.error('Error loading chats:', error);
}
}
// Function to mark messages as read
async function markMessagesAsRead(fromUser) {
try {
const response = await fetch('/api/mark-read', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${currentToken}`
},
body: JSON.stringify({
from_user: fromUser,
to_user: currentUser
})
});
const data = await response.json();
if (data.success) {
console.log(`✅ Marked messages as read from ${fromUser}`);
// Обновляем список чатов чтобы убрать красный badge
loadChats();
} else {
console.error('❌ Failed to mark messages as read:', data.error);
}
} catch (error) {
console.error('❌ Error marking messages as read:', error);
}
}
// Function to delete message
async function deleteMessage(messageId, forEveryone) {
try {
const response = await fetch('/api/delete-message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${currentToken}`
},
body: JSON.stringify({
message_id: messageId,
for_everyone: forEveryone
})
});
const data = await response.json();
if (data.success) {
console.log(`✅ Message deleted: ${data.message}`);
showAlert(data.message, 'success');
// Remove message from UI
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
if (messageElement) {
messageElement.remove();
}
// Update chat history
if (messageHistory[currentRecipient]) {
messageHistory[currentRecipient] = messageHistory[currentRecipient].filter(
msg => msg.id !== messageId
);
}
// Reload chats to update last message
loadChats();
} else {
console.error('❌ Failed to delete message:', data.message);
showAlert(data.message, 'error');
}
} catch (error) {
console.error('❌ Error deleting message:', error);
showAlert('Error deleting message', 'error');
}
}
// Modal functions
function showDeleteModal(messageId) {
messageToDelete = messageId;
document.getElementById('deleteModal').style.display = 'flex';
}
function closeDeleteModal() {
messageToDelete = null;
document.getElementById('deleteModal').style.display = 'none';
}
function deleteMessageForMe() {
if (messageToDelete) {
deleteMessage(messageToDelete, false);
closeDeleteModal();
}
}
function deleteMessageForEveryone() {
if (messageToDelete) {
deleteMessage(messageToDelete, true);
closeDeleteModal();
}
}
function renderChatsList() {
const chatsList = document.getElementById('chatsList');
chatsList.innerHTML = '';
console.log('Rendering chats:', chats);
if (chats.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.innerHTML = `
<h3>No chats yet</h3>
<p>Start a new conversation by clicking "New Chat"</p>
`;
chatsList.appendChild(emptyState);
return;
}
chats.forEach(chat => {
const li = document.createElement('li');
li.className = 'chat-item';
if (chat.username === currentRecipient) {
li.classList.add('active');
}
const lastMessageTime = chat.last_message_time ?
formatTime(chat.last_message_time) : '';
const lastMessage = chat.last_message || 'No messages yet';
li.innerHTML = `
<div class="chat-info">
<div class="chat-username">${chat.username}</div>
<div class="chat-phone">${chat.phone}</div>
<div class="chat-last-message">${lastMessage}</div>
</div>
<div class="chat-meta">
<div class="chat-time">${lastMessageTime}</div>
${chat.unread_count > 0 ? `<div class="unread-badge">${chat.unread_count}</div>` : ''}
</div>
`;
li.onclick = () => selectChat(chat.username, chat.phone);
chatsList.appendChild(li);
});
}
function showChatTab(tab) {
currentChatTab = tab;
document.querySelectorAll('.chat-tab').forEach(tab => tab.classList.remove('active'));
if (tab === 'existing') {
document.querySelector('.chat-tab:nth-child(1)').classList.add('active');
document.getElementById('searchContainer').style.display = 'none';
renderChatsList();
} else {
document.querySelector('.chat-tab:nth-child(2)').classList.add('active');
document.getElementById('searchContainer').style.display = 'block';
document.getElementById('searchResults').innerHTML = '<div class="empty-state"><p>Search for users by phone or username</p></div>';
document.getElementById('searchInput').value = '';
}
}
let searchTimeout;
document.getElementById('searchInput').addEventListener('input', function(e) {
clearTimeout(searchTimeout);
const query = e.target.value.trim();
if (query.length < 2) {
document.getElementById('searchResults').innerHTML = '<div class="empty-state"><p>Enter at least 2 characters to search</p></div>';
return;
}
searchTimeout = setTimeout(() => searchUsers(query), 300);
});
async function searchUsers(query) {
try {
const response = await fetch(`/api/search-users?q=${encodeURIComponent(query)}`, {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
if (response.ok) {
searchResults = await response.json();
renderSearchResults();
} else {
console.error('Search failed');
}
} catch (error) {
console.error('Error searching users:', error);
}
}
function renderSearchResults() {
const searchResultsContainer = document.getElementById('searchResults');
searchResultsContainer.innerHTML = '';
if (searchResults.length === 0) {
searchResultsContainer.innerHTML = '<div class="empty-state"><p>No users found</p></div>';
return;
}
searchResults.forEach(user => {
const div = document.createElement('div');
div.className = 'chat-item';
div.innerHTML = `
<div class="chat-info">
<div class="chat-username">${user.username}</div>
<div class="chat-phone">${user.phone}</div>
<div class="chat-last-message">Click to start chatting</div>
</div>
`;
div.onclick = () => {
selectChat(user.username, user.phone);
showChatTab('existing');
document.getElementById('searchInput').value = '';
};
searchResultsContainer.appendChild(div);
});
}
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) {
localStorage.setItem('chatToken', data.token);
localStorage.setItem('chatUsername', data.username);
currentUser = data.username;
currentToken = data.token;
document.getElementById('authContainer').style.display = 'none';
document.getElementById('chatContainer').style.display = 'flex';
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
loadChats();
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) {
localStorage.setItem('chatToken', data.token);
localStorage.setItem('chatUsername', data.username);
currentUser = data.username;
currentToken = data.token;
document.getElementById('authContainer').style.display = 'none';
document.getElementById('chatContainer').style.display = 'flex';
document.getElementById('chatHeader').textContent = `Welcome, ${currentUser}`;
loadChats();
connectWebSocket();
} else {
showAlert(data.message, 'error');
}
} catch (error) {
showAlert('Login failed. Please try again.', 'error');
}
}
function logout() {
localStorage.removeItem('chatToken');
localStorage.removeItem('chatUsername');
if (ws) ws.close();
currentUser = '';
currentToken = '';
currentRecipient = '';
chats = [];
searchResults = [];
messageHistory = {};
document.getElementById('authContainer').style.display = 'block';
document.getElementById('chatContainer').style.display = 'none';
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');
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 === 'error') {
showAlert(data.content, 'error');
if (data.content.includes('Invalid') || data.content.includes('expired') || data.content.includes('auth')) {
logout();
}
} else if (data.type === 'system') {
addSystemMessage(data.content);
} else if (data.type === 'message_deleted') {
// Handle message deletion from server
const messageElement = document.querySelector(`[data-message-id="${data.message_id}"]`);
if (messageElement) {
messageElement.remove();
}
// Update chat history
if (messageHistory[currentRecipient]) {
messageHistory[currentRecipient] = messageHistory[currentRecipient].filter(
msg => msg.id !== data.message_id
);
}
// Reload chats to update last message
loadChats();
} else {
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');
};
}
async function selectChat(username, phone) {
currentRecipient = username;
// Помечаем сообщения как прочитанные при открытии чата
await markMessagesAsRead(username);
document.querySelectorAll('.chat-item').forEach(item => item.classList.remove('active'));
document.getElementById('chatHeader').textContent = `Chat with ${username} (${phone})`;
document.getElementById('sendButton').disabled = false;
document.getElementById('messageInput').disabled = false;
document.getElementById('messageInput').focus();
displayChatHistory(username);
}
function displayChatHistory(username) {
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML = '';
if (messageHistory[username]) {
messageHistory[username].forEach(msg => displayMessage(msg, false));
} else {
addSystemMessage('No message history found');
}
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] = [];
}
const messageExists = messageHistory[otherUser].some(
existingMsg => existingMsg.id === msg.id
);
if (!messageExists) {
messageHistory[otherUser].push(msg);
}
loadChats();
if (otherUser === currentRecipient) {
displayMessage(msg, true);
// Если сообщение пришло в открытый чат, сразу помечаем как прочитанное
if (msg.from_user === currentRecipient) {
markMessagesAsRead(currentRecipient);
}
}
}
function displayMessage(msg, scrollToBottom) {
const chatMessages = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${msg.from_user === currentUser ? 'outgoing' : 'incoming'}`;
messageDiv.setAttribute('data-message-id', msg.id);
if (msg.message_type === 'system') {
messageDiv.className = 'system-message';
messageDiv.textContent = msg.content;
} else {
const isOutgoing = msg.from_user === currentUser;
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);
// Add delete button for user's own messages or all messages
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.innerHTML = '×';
deleteBtn.title = 'Delete message';
deleteBtn.onclick = (e) => {
e.stopPropagation();
showDeleteModal(msg.id);
};
actionsDiv.appendChild(deleteBtn);
messageDiv.appendChild(actionsDiv);
}
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,
to_user: currentRecipient,
content: content,
timestamp: Date.now(),
message_type: 'text'
};
console.log('Sending message:', message);
ws.send(JSON.stringify(message));
input.value = '';
setTimeout(() => loadChats(), 100);
}
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
function formatTime(timestamp) {
if (!timestamp) return '';
const date = new Date(timestamp);
const now = new Date();
const diff = now - date;
if (diff < 24 * 60 * 60 * 1000) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
} else {
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
}
}
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();
});
// Close modal when clicking outside
document.getElementById('deleteModal').addEventListener('click', function(e) {
if (e.target === this) {
closeDeleteModal();
}
});
document.getElementById('loginPhone').focus();
</script>
</body>
</html>