document.addEventListener('DOMContentLoaded', (event) => { const socket = io(); const md = window.markdownit(); function requestNotificationPermission() { if (Notification.permission !== "granted") { Notification.requestPermission(); } } function showNotification(title, body) { if (Notification.permission === "granted") { new Notification(title, { body }); } } requestNotificationPermission(); socket.emit('join', { username: username }); document.title = `Chatting with ${friendUsername}`; const messagesList = document.getElementById('messages'); const imageOverlay = document.getElementById('image-overlay'); const overlayImage = document.getElementById('overlay-image'); const contextMenu = document.getElementById('context-menu'); const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toast-message'); let currentMessageId = null; let currentMessageText = null; function showToast(message) { toastMessage.textContent = message; toast.classList.add('show'); setTimeout(() => { toast.classList.remove('show'); }, 3000); } function scrollToBottom() { messagesList.scrollTop = messagesList.scrollHeight; } function shouldShowUsername(previousTimestamp, currentTimestamp) { const tenMinutes = 10 * 60 * 1000; return (currentTimestamp - previousTimestamp) > tenMinutes; } function formatLocalTime(utcTimestamp) { const date = new Date(utcTimestamp); const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000); const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); const timeFormatter = { hour: '2-digit', minute: '2-digit' }; const dateFormatter = { year: 'numeric', month: '2-digit', day: '2-digit' }; let formattedTime; if (localDate >= today) { formattedTime = `Today at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`; } else if (localDate >= yesterday) { formattedTime = `Yesterday at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`; } else { formattedTime = `${localDate.toLocaleDateString(undefined, dateFormatter)} at ${localDate.toLocaleTimeString(undefined, timeFormatter)}`; } return formattedTime; } function isImageUrl(url) { return /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(url); } function appendMessage(msg, showUsername, senderProfilePicture) { const currentTimestamp = new Date(msg.timestamp).getTime(); previousSender = msg.sender; previousTimestamp = currentTimestamp; const newMessage = document.createElement('div'); newMessage.classList.add('message'); if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); if (msg.sender === username) { usernameElement.classList.add('message-myself-sender'); } usernameElement.innerHTML = ` /cdn/${msg.sender} ${msg.sender}
${formatLocalTime(msg.timestamp)}
`; messagesList.appendChild(usernameElement); } if (msg.content_type === 'text') { if (isImageUrl(msg.content)) { newMessage.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; } else { newMessage.innerHTML = `${md.render(msg.content)}
${formatLocalTime(msg.timestamp)}
`; } } else if (msg.content_type === 'file') { const isImage = isImageUrl(msg.content); const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content); if (isImage) { newMessage.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; } else if (isVideo) { newMessage.innerHTML = `
${formatLocalTime(msg.timestamp)}
`; } else { newMessage.innerHTML = `Download File
${formatLocalTime(msg.timestamp)}
`; } } messagesList.appendChild(newMessage); scrollToBottom(); } let previousSender = null; let previousTimestamp = 0; socket.on('new_message', function(data) { // Check if the message is for the current chat if (friendUsername !== data.sender && friendId != data.sender_id) { return; } showNotification('New Message', `You have received a new message from ${data.sender}`); const currentTimestamp = new Date(data.timestamp).getTime(); const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp); previousSender = data.sender; previousTimestamp = currentTimestamp; const messageElement = document.createElement('div'); messageElement.classList.add('message'); const newMessage = document.createElement('div'); newMessage.classList.add('message'); newMessage.dataset.messageId = data.id; newMessage.dataset.messageText = data.content; if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); const timestampFormatted = formatLocalTime(data.timestamp); // Include the profile picture if available const profilePicture = data.sender_profile_picture ? `${data.sender}` : ''; usernameElement.innerHTML = `${profilePicture} ${data.sender}:
${timestampFormatted}
`; messagesList.appendChild(usernameElement); } if (data.content_type === 'text') { if (isImageUrl(data.content)) { newMessage.innerHTML = `Image
${formatLocalTime(data.timestamp)}
`; } else { newMessage.innerHTML = `${md.render(data.content)}
${formatLocalTime(data.timestamp)}
`; } } else if (data.content_type === 'file') { const isImage = isImageUrl(data.content); const isVideo = /\.(mp4|webm|ogg)$/i.test(data.content); if (isImage) { newMessage.innerHTML = `Image
${formatLocalTime(data.timestamp)}
`; } else if (isVideo) { newMessage.innerHTML = `
${formatLocalTime(data.timestamp)}
`; } else { newMessage.innerHTML = `Download File
${formatLocalTime(data.timestamp)}
`; } } messagesList.appendChild(newMessage); scrollToBottom(); }); if (friendId) { fetch(`/get_messages/${friendId}`) .then(response => response.json()) .then(data => { messagesList.innerHTML = ''; document.getElementById('chat-with').textContent = `Chatting with ${friendUsername}`; document.getElementById('send-message-form').dataset.receiver = friendId; data.messages.forEach((msg, index, messages) => { const currentTimestamp = new Date(msg.timestamp).getTime(); const previousMessage = messages[index - 1]; const previousTimestamp = previousMessage ? new Date(previousMessage.timestamp).getTime() : 0; const showUsername = index === 0 || msg.sender !== previousMessage.sender || shouldShowUsername(previousTimestamp, currentTimestamp); if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); if (msg.sender === username) { usernameElement.classList.add('message-myself-sender'); } // Include the profile picture if available const profilePicture = msg.sender_profile_picture ? `${msg.sender}` : ''; usernameElement.innerHTML = `${profilePicture} ${msg.sender}
${formatLocalTime(msg.timestamp)}
`; messagesList.appendChild(usernameElement); } const messageElement = document.createElement('div'); messageElement.classList.add('message'); messageElement.dataset.messageId = msg.id; messageElement.dataset.messageText = msg.content; if (msg.content_type === 'text') { if (isImageUrl(msg.content)) { messageElement.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; } else { messageElement.innerHTML = `${md.render(msg.content)}
${formatLocalTime(msg.timestamp)}
`; } } else if (msg.content_type === 'file') { const isImage = isImageUrl(msg.content); const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content); if (isImage) { messageElement.innerHTML = `Image
${formatLocalTime(msg.timestamp)}
`; } else if (isVideo) { messageElement.innerHTML = `
${formatLocalTime(msg.timestamp)}
`; } else { messageElement.innerHTML = `Download File
${formatLocalTime(msg.timestamp)}
`; } } messagesList.appendChild(messageElement); }); scrollToBottom(); }) .catch(error => console.error('Error fetching messages:', error)); } const sendMessageForm = document.querySelector('.send-message-form'); if (sendMessageForm) { sendMessageForm.addEventListener('submit', function(event) { event.preventDefault(); const receiver = this.dataset.receiver; const contentInput = this.querySelector('input[name="content"]'); const fileInput = this.querySelector('input[name="file"]'); const content = contentInput.value; const timestamp = new Date().toISOString().slice(0, 19).replace('T', ' '); const imagePreview = document.querySelector('.file-preview-image'); const videoPreview = document.querySelector('.file-preview-video'); const filenamePreview = document.querySelector('.file-preview-filename'); if (content || fileInput.files.length > 0) { // Append the message locally const newMessage = document.createElement('div'); newMessage.classList.add('message'); newMessage.dataset.messageId = 'temp-id'; // Temporary ID for the new message const currentTimestamp = new Date(timestamp).getTime(); const showUsername = previousSender !== username || shouldShowUsername(previousTimestamp, currentTimestamp); previousSender = username; previousTimestamp = currentTimestamp; if (showUsername) { const usernameElement = document.createElement('div'); usernameElement.classList.add('message-sender'); const timestampFormatted = formatLocalTime(timestamp); const profilePictureUrl = `/cdn/${profilePicture}`; // Correct reference usernameElement.innerHTML = `${username} ${username}:
${timestampFormatted}
`; messagesList.appendChild(usernameElement); } if (content) { newMessage.dataset.messageText = content; if (isImageUrl(content)) { newMessage.innerHTML = `Image
${formatLocalTime(timestamp)}
`; } else { newMessage.innerHTML = `${md.render(content)}
${formatLocalTime(timestamp)}
`; } } if (fileInput.files.length > 0) { const file = fileInput.files[0]; const isImage = /\.(jpg|jpeg|png|gif|bmp|svg)$/i.test(file.name); const isVideo = /\.(mp4|webm|ogg)$/i.test(file.name); if (isImage) { newMessage.innerHTML = `Image
${formatLocalTime(timestamp)}
`; } else if (isVideo) { newMessage.innerHTML = `
${formatLocalTime(timestamp)}
`; } else { newMessage.innerHTML = `Download File
${formatLocalTime(timestamp)}
`; } } messagesList.appendChild(newMessage); scrollToBottom(); const formData = new FormData(); formData.append('content', content); formData.append('timestamp', timestamp); if (fileInput.files[0]) { formData.append('file', fileInput.files[0]); } fetch(`/send_message/${receiver}`, { method: 'POST', body: formData }).then(response => response.json()) .then(data => { if (data.status === 'Message sent') { contentInput.value = ''; fileInput.value = ''; document.querySelector('.file-preview').style.display = 'none'; document.querySelector('.file-preview img').src = ''; document.querySelector('.file-preview video').style.display = 'none'; document.querySelector('.file-preview video').src = ''; document.querySelector('.file-preview .file-preview-filename').style.display = 'none'; document.querySelector('.file-preview .file-preview-filename').textContent = ''; // Update the message ID with the real one from the server newMessage.dataset.messageId = data.message_id; } else { showToast('Message sending failed'); } }).catch(error => { showToast('Message sending failed'); console.error('Error sending message:', error); }); } }); document.querySelector('input[name="file"]').addEventListener('change', function(event) { const file = event.target.files[0]; if (file) { const reader = new FileReader(); const imagePreview = document.querySelector('.file-preview-image'); const videoPreview = document.querySelector('.file-preview-video'); const filenamePreview = document.querySelector('.file-preview-filename'); reader.onload = function(e) { document.querySelector('.file-preview').style.display = 'block'; if (file.type.startsWith('image/')) { imagePreview.style.display = 'block'; videoPreview.style.display = 'none'; filenamePreview.style.display = 'none'; imagePreview.src = e.target.result; } else if (file.type.startsWith('video/')) { videoPreview.style.display = 'block'; imagePreview.style.display = 'none'; filenamePreview.style.display = 'none'; videoPreview.querySelector('source').src = e.target.result; videoPreview.load(); } else { filenamePreview.style.display = 'block'; imagePreview.style.display = 'none'; videoPreview.style.display = 'none'; filenamePreview.textContent = file.name; } }; reader.readAsDataURL(file); } }); document.querySelector('.remove-file').addEventListener('click', function() { const fileInput = document.querySelector('input[name="file"]'); fileInput.value = ''; const filePreview = document.querySelector('.file-preview'); filePreview.style.display = 'none'; filePreview.querySelector('img').style.display = 'none'; filePreview.querySelector('img').src = ''; filePreview.querySelector('video').style.display = 'none'; filePreview.querySelector('video').src = ''; filePreview.querySelector('.file-preview-filename').style.display = 'none'; filePreview.querySelector('.file-preview-filename').textContent = ''; }); } function checkFriendRequests() { const friendRequestsList = document.getElementById('friend-requests'); const friendRequestsSection = document.getElementById('friend-requests-section'); if (friendRequestsList && friendRequestsSection) { if (friendRequestsList.children.length === 0) { friendRequestsSection.style.display = 'none'; } else { friendRequestsSection.style.display = 'block'; } } } checkFriendRequests(); // Image enhancer functionality document.addEventListener('click', function(event) { if (event.target.classList.contains('enhanceable-image')) { overlayImage.src = event.target.src; imageOverlay.style.display = 'flex'; } }); imageOverlay.addEventListener('click', function() { imageOverlay.style.display = 'none'; }); // Custom Context Menu Functionality document.addEventListener('contextmenu', function(event) { const messageElement = event.target.closest('.message'); if (messageElement) { event.preventDefault(); currentMessageId = messageElement.dataset.messageId; currentMessageText = messageElement.dataset.messageText; contextMenu.style.top = `${event.clientY}px`; contextMenu.style.left = `${event.clientX}px`; contextMenu.style.display = 'block'; } else { contextMenu.style.display = 'none'; } }); document.getElementById('copy-text').addEventListener('click', function() { if (currentMessageText) { navigator.clipboard.writeText(currentMessageText).then(() => { showToast('Text copied to clipboard'); }); } contextMenu.style.display = 'none'; }); document.getElementById('copy-id').addEventListener('click', function() { if (currentMessageId) { navigator.clipboard.writeText(currentMessageId).then(() => { showToast('Message ID copied to clipboard'); }); } contextMenu.style.display = 'none'; }); contextMenu.addEventListener('contextmenu', function(event) { event.preventDefault(); }); document.addEventListener('click', function() { contextMenu.style.display = 'none'; }); // Handle user online/offline status socket.on('user_online', function(data) { const statusIndicator = document.getElementById(`status-${data.username}`); if (statusIndicator) { statusIndicator.classList.remove('offline'); statusIndicator.classList.add('online'); } }); socket.on('user_offline', function(data) { const statusIndicator = document.getElementById(`status-${data.username}`); if (statusIndicator) { statusIndicator.classList.remove('online'); statusIndicator.classList.add('offline'); } }); });