speakmore-2.0/static/js/chat.js

332 lines
16 KiB
JavaScript

document.addEventListener('DOMContentLoaded', (event) => {
const socket = io();
const md = window.markdownit();
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;
}
let previousSender = null;
let previousTimestamp = 0;
socket.on('new_message', function(data) {
const currentTimestamp = new Date(data.timestamp).getTime();
const showUsername = previousSender !== data.sender || shouldShowUsername(previousTimestamp, currentTimestamp);
previousSender = data.sender;
previousTimestamp = currentTimestamp;
const newMessage = document.createElement('div');
newMessage.classList.add('message');
newMessage.dataset.messageId = data.id; // Ensure this is correct
newMessage.dataset.messageText = data.content;
if (showUsername) {
const usernameElement = document.createElement('div');
usernameElement.classList.add('message-sender');
usernameElement.innerHTML = `<strong>${data.sender}</strong>:`;
messagesList.appendChild(usernameElement);
}
if (data.content_type === 'text') {
newMessage.innerHTML = `${md.render(data.content)}<div class="timestamp">${data.timestamp}</div>`;
} else if (data.content_type === 'file') {
const isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(data.content);
const isVideo = /\.(mp4|webm|ogg)$/i.test(data.content);
if (isImage) {
newMessage.innerHTML = `<img src="/cdn/${data.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${data.timestamp}</div>`;
} else if (isVideo) {
newMessage.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="/cdn/${data.content}" type="video/mp4"></video><div class="timestamp">${data.timestamp}</div>`;
} else {
newMessage.innerHTML = `<a href="/cdn/${data.content}" target="_blank">Download File</a><div class="timestamp">${data.timestamp}</div>`;
}
}
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');
usernameElement.innerHTML = `<strong>${msg.sender}</strong>:`;
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') {
messageElement.innerHTML = `${md.render(msg.content)}<div class="timestamp">${msg.timestamp}</div>`;
} else if (msg.content_type === 'file') {
const isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(msg.content);
const isVideo = /\.(mp4|webm|ogg)$/i.test(msg.content);
if (isImage) {
messageElement.innerHTML = `<img src="/cdn/${msg.content}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${msg.timestamp}</div>`;
} else if (isVideo) {
messageElement.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="/cdn/${msg.content}" type="video/mp4"></video><div class="timestamp">${msg.timestamp}</div>`;
} else {
messageElement.innerHTML = `<a href="/cdn/${msg.content}" target="_blank">Download File</a><div class="timestamp">${msg.timestamp}</div>`;
}
}
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');
usernameElement.innerHTML = `<strong>${username}</strong>:`;
messagesList.appendChild(usernameElement);
}
if (content) {
newMessage.dataset.messageText = content;
newMessage.innerHTML = `${md.render(content)}<div class="timestamp">${timestamp}</div>`;
}
if (fileInput.files.length > 0) {
const file = fileInput.files[0];
const isImage = /\.(jpg|jpeg|png|gif|bmp)$/i.test(file.name);
const isVideo = /\.(mp4|webm|ogg)$/i.test(file.name);
if (isImage) {
newMessage.innerHTML = `<img src="${imagePreview.src}" alt="Image" class="enhanceable-image" style="max-width: 200px; max-height: 200px;" /><div class="timestamp">${timestamp}</div>`;
} else if (isVideo) {
newMessage.innerHTML = `<video controls style="max-width: 200px; max-height: 200px;"><source src="${videoPreview.src}" type="video/mp4"></video><div class="timestamp">${timestamp}</div>`;
} else {
newMessage.innerHTML = `<a href="${URL.createObjectURL(file)}" target="_blank">Download File</a><div class="timestamp">${timestamp}</div>`;
}
}
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');
}
});
});