414 lines
14 KiB
JavaScript
414 lines
14 KiB
JavaScript
// Chat System Module
|
|
// Handles chat functionality, message handling, and user authentication
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
AppModules.require('api', 'ui-controls');
|
|
AppModules.register('chat');
|
|
|
|
// Initialize chat system
|
|
function initializeChat() {
|
|
AppLogger.log('Initializing chat system...');
|
|
|
|
// Get user ID first
|
|
API.getUserId()
|
|
.then(data => {
|
|
AppLogger.log('Chat initialized with user:', AppState.userId, AppState.isAdmin ? '(admin)' : '');
|
|
|
|
// Show admin toast if applicable
|
|
if (AppState.isAdmin) {
|
|
UIControls.showToast('Admin mode activated');
|
|
}
|
|
|
|
// Set up nickname handling
|
|
const nicknameInput = document.getElementById('nickname');
|
|
if (nicknameInput) {
|
|
// Load saved nickname
|
|
const savedNickname = localStorage.getItem('chatNickname');
|
|
if (savedNickname) {
|
|
nicknameInput.value = savedNickname;
|
|
}
|
|
|
|
// Handle nickname changes
|
|
UIControls.DOMUtils.addEvent(nicknameInput, 'change', function() {
|
|
const nickname = this.value.trim();
|
|
localStorage.setItem('chatNickname', nickname);
|
|
AppState.nickname = nickname;
|
|
});
|
|
}
|
|
|
|
// Set up message input handling
|
|
const messageInput = document.getElementById('messageInput');
|
|
if (messageInput) {
|
|
// Enter key to send message
|
|
UIControls.DOMUtils.addEvent(messageInput, 'keypress', function(e) {
|
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
e.preventDefault();
|
|
sendMessage();
|
|
}
|
|
});
|
|
|
|
// Typing indicator
|
|
UIControls.DOMUtils.addEvent(messageInput, 'input', function() {
|
|
handleTypingIndicator();
|
|
});
|
|
}
|
|
|
|
// Set up send button
|
|
const sendButton = document.querySelector('.chat-input button') ||
|
|
document.querySelector('#messageInput').nextElementSibling;
|
|
if (sendButton && sendButton.tagName === 'BUTTON') {
|
|
UIControls.DOMUtils.addEvent(sendButton, 'click', sendMessage);
|
|
}
|
|
|
|
// Start polling for messages
|
|
startMessagePolling();
|
|
|
|
// Send initial heartbeat
|
|
API.sendHeartbeat();
|
|
|
|
AppLogger.log('Chat system initialized successfully');
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Failed to initialize chat:', error);
|
|
UIControls.showToast('Failed to initialize chat');
|
|
});
|
|
}
|
|
|
|
// Send message functionality
|
|
function sendMessage() {
|
|
const messageInput = document.getElementById('messageInput');
|
|
const nicknameInput = document.getElementById('nickname');
|
|
const message = messageInput?.value?.trim() || '';
|
|
const nickname = nicknameInput?.value?.trim() || '';
|
|
|
|
if (!nickname) {
|
|
if (nicknameInput) {
|
|
nicknameInput.style.borderColor = 'var(--dodgers-red)';
|
|
nicknameInput.focus();
|
|
}
|
|
UIControls.showToast('Please enter a nickname');
|
|
setTimeout(() => {
|
|
if (nicknameInput) nicknameInput.style.borderColor = '#e0e0e0';
|
|
}, 2000);
|
|
return;
|
|
}
|
|
|
|
if (!message) {
|
|
return; // Just return quietly for empty message
|
|
}
|
|
|
|
// Send message via API
|
|
API.sendMessage(nickname, message)
|
|
.then(data => {
|
|
if (data.success) {
|
|
if (messageInput) messageInput.value = '';
|
|
AppState.nickname = nickname;
|
|
// Message will be added automatically by polling
|
|
} else if (data.error) {
|
|
UIControls.showToast(data.error);
|
|
UIControls.updateConnectionStatus(false);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Error sending message:', error);
|
|
UIControls.showToast('Failed to send message');
|
|
UIControls.updateConnectionStatus(false);
|
|
});
|
|
}
|
|
|
|
// Delete message (admin only)
|
|
function deleteMessage(messageId) {
|
|
if (!AppState.isAdmin) return;
|
|
|
|
if (!confirm('Delete this message?')) return;
|
|
|
|
API.deleteMessage(messageId)
|
|
.then(data => {
|
|
if (data.success) {
|
|
removeMessageFromUI(messageId);
|
|
UIControls.showToast('Message deleted');
|
|
} else {
|
|
UIControls.showToast('Failed to delete message');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Error deleting message:', error);
|
|
UIControls.showToast('Failed to delete message');
|
|
});
|
|
}
|
|
|
|
// Ban user (admin only)
|
|
function banUser(userId) {
|
|
if (!AppState.isAdmin) return;
|
|
|
|
if (!confirm('Ban this user from chat?')) return;
|
|
|
|
API.banUser(userId)
|
|
.then(data => {
|
|
if (data.success) {
|
|
UIControls.showToast('User banned');
|
|
} else {
|
|
UIControls.showToast('Failed to ban user');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Error banning user:', error);
|
|
UIControls.showToast('Failed to ban user');
|
|
});
|
|
}
|
|
|
|
// Clear chat (admin only)
|
|
function clearChat() {
|
|
if (!AppState.isAdmin) return;
|
|
|
|
if (!confirm('Clear all chat messages?')) return;
|
|
|
|
API.clearChat()
|
|
.then(data => {
|
|
if (data.success) {
|
|
clearMessagesUI();
|
|
UIControls.showToast('Chat cleared');
|
|
AppState.lastMessageId = '';
|
|
API.sendHeartbeat(); // Force refresh all users
|
|
} else {
|
|
UIControls.showToast('Failed to clear chat');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Error clearing chat:', error);
|
|
UIControls.showToast('Failed to clear chat');
|
|
});
|
|
}
|
|
|
|
// Message polling system
|
|
function startMessagePolling() {
|
|
// Poll for new messages
|
|
setInterval(fetchMessages, AppConfig.api.chatPollInterval);
|
|
|
|
// Send heartbeat periodically
|
|
setInterval(API.sendHeartbeat, AppConfig.api.heartbeatInterval);
|
|
|
|
// Initial fetch
|
|
fetchMessages();
|
|
}
|
|
|
|
// Fetch and display messages
|
|
function fetchMessages() {
|
|
API.fetchMessages()
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Handle initial load or message updates
|
|
if (data.all_messages !== null) {
|
|
// Initial load or forced refresh (like after admin clear)
|
|
handleInitialMessageLoad(data);
|
|
} else {
|
|
// Incremental updates
|
|
handleNewMessages(data.messages);
|
|
}
|
|
|
|
// Check for admin clear
|
|
if (data.message_count === 0 && AppState.allMessages.length > 0) {
|
|
handleChatCleared();
|
|
}
|
|
|
|
// Update last message ID
|
|
if (AppState.allMessages.length > 0) {
|
|
AppState.lastMessageId = AppState.allMessages[AppState.allMessages.length - 1].id;
|
|
}
|
|
|
|
UIControls.updateConnectionStatus(true);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
AppLogger.error('Error fetching messages:', error);
|
|
UIControls.updateConnectionStatus(false);
|
|
});
|
|
}
|
|
|
|
// Handle initial message load
|
|
function handleInitialMessageLoad(data) {
|
|
AppState.allMessages = data.all_messages || [];
|
|
displayAllMessages();
|
|
}
|
|
|
|
// Handle new messages
|
|
function handleNewMessages(messages) {
|
|
if (!messages || messages.length === 0) return;
|
|
|
|
messages.forEach(msg => {
|
|
appendMessage(msg);
|
|
|
|
// Show notification if chat is collapsed and message is not from current user
|
|
if (AppState.chatCollapsed && msg.user_id !== AppState.userId) {
|
|
AppState.unreadCount++;
|
|
UIControls.updateNotificationBadge();
|
|
UIControls.playNotificationSound();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Handle chat cleared by admin
|
|
function handleChatCleared() {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
if (chatMessages) {
|
|
chatMessages.innerHTML = '<div class="system-message">Chat was cleared by admin</div>';
|
|
}
|
|
AppState.allMessages = [];
|
|
AppState.lastMessageId = '';
|
|
}
|
|
|
|
// Typing indicator system
|
|
function handleTypingIndicator() {
|
|
const messageInput = document.getElementById('messageInput');
|
|
if (!messageInput) return;
|
|
|
|
const hasContent = messageInput.value.length > 0;
|
|
|
|
if (hasContent && !AppState.isTyping) {
|
|
AppState.isTyping = true;
|
|
}
|
|
|
|
clearTimeout(AppState.typingTimer);
|
|
AppState.typingTimer = setTimeout(() => {
|
|
AppState.isTyping = false;
|
|
}, AppConfig.chat.typingTimeout);
|
|
}
|
|
|
|
// UI manipulation functions
|
|
function displayAllMessages() {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
|
|
if (!chatMessages) return;
|
|
|
|
if (AppState.allMessages.length === 0) {
|
|
chatMessages.innerHTML = '<div class="empty-chat">No messages yet. Be the first to say hello! 👋</div>';
|
|
AppState.lastMessageCount = 0;
|
|
return;
|
|
}
|
|
|
|
chatMessages.innerHTML = '';
|
|
|
|
AppState.allMessages.forEach(msg => {
|
|
appendMessageElement(msg, false);
|
|
});
|
|
|
|
// Scroll to bottom
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
AppState.lastMessageCount = AppState.allMessages.length;
|
|
}
|
|
|
|
function appendMessage(msg) {
|
|
AppState.allMessages.push(msg);
|
|
appendMessageElement(msg, true);
|
|
AppState.lastMessageCount = AppState.allMessages.length;
|
|
}
|
|
|
|
function appendMessageElement(msg, animate) {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
|
|
if (!chatMessages) return;
|
|
|
|
// Remove empty chat message if it exists
|
|
const emptyChat = chatMessages.querySelector('.empty-chat');
|
|
if (emptyChat) {
|
|
emptyChat.remove();
|
|
}
|
|
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = 'message';
|
|
messageDiv.setAttribute('data-message-id', msg.id);
|
|
|
|
// Own message styling
|
|
if (msg.user_id === AppState.userId) {
|
|
messageDiv.className += ' own-message';
|
|
}
|
|
|
|
// Admin message styling
|
|
if (msg.is_admin) {
|
|
messageDiv.className += ' admin-message';
|
|
}
|
|
|
|
// Disable animation for initial load
|
|
if (!animate) {
|
|
messageDiv.style.animation = 'none';
|
|
}
|
|
|
|
// Create admin actions if user is admin
|
|
const adminActions = (AppState.isAdmin && msg.user_id !== AppState.userId) ?
|
|
`<div class="message-actions">
|
|
<button class="delete-btn" onclick="Chat.deleteMessage('${msg.id}')">Delete</button>
|
|
<button class="ban-btn" onclick="Chat.banUser('${msg.user_id}')">Ban</button>
|
|
</div>` : '';
|
|
|
|
// Create admin badge
|
|
const adminBadge = msg.is_admin ? '👑 ' : '';
|
|
|
|
messageDiv.innerHTML = `
|
|
<div class="message-header">
|
|
<span class="message-nickname${msg.is_admin ? ' admin' : ''}">${adminBadge}${msg.nickname}</span>
|
|
<span class="message-id">#${msg.user_id}</span>
|
|
<span class="message-time">${msg.time}</span>
|
|
</div>
|
|
<div class="message-text">${escapeHtml(msg.message)}</div>
|
|
${adminActions}
|
|
`;
|
|
|
|
chatMessages.appendChild(messageDiv);
|
|
|
|
// Auto-scroll to bottom on new messages
|
|
if (animate) {
|
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
}
|
|
}
|
|
|
|
function removeMessageFromUI(messageId) {
|
|
const messageElement = document.querySelector(`[data-message-id="${messageId}"]`);
|
|
if (messageElement) {
|
|
messageElement.style.animation = 'slideOut 0.3s';
|
|
setTimeout(() => {
|
|
messageElement.remove();
|
|
}, 300);
|
|
}
|
|
|
|
// Remove from internal array
|
|
AppState.allMessages = AppState.allMessages.filter(msg => msg.id !== messageId);
|
|
}
|
|
|
|
function clearMessagesUI() {
|
|
const chatMessages = document.getElementById('chatMessages');
|
|
if (chatMessages) {
|
|
chatMessages.innerHTML = '<div class="system-message">Chat cleared by admin</div>';
|
|
}
|
|
AppState.allMessages = [];
|
|
AppState.lastMessageId = '';
|
|
}
|
|
|
|
// Utility function for HTML escaping
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
// Public API
|
|
window.Chat = {
|
|
initializeChat: initializeChat,
|
|
sendMessage: sendMessage,
|
|
deleteMessage: deleteMessage,
|
|
banUser: banUser,
|
|
clearChat: clearChat,
|
|
displayAllMessages: displayAllMessages,
|
|
appendMessage: appendMessage
|
|
};
|
|
|
|
// Initialize when DOM is ready and modules are loaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Small delay to ensure all modules are loaded
|
|
setTimeout(initializeChat, 100);
|
|
});
|
|
|
|
AppLogger.log('Chat module loaded');
|
|
|
|
})();
|