293 lines
9.6 KiB
JavaScript
293 lines
9.6 KiB
JavaScript
// UI Controls Module
|
|
// Handles UI toggle functions, keyboard shortcuts, notifications, and DOM utilities
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
AppModules.require('api');
|
|
AppModules.register('ui-controls');
|
|
|
|
// Toast notification system
|
|
function showToast(message, duration = AppConfig.ui.toastDuration) {
|
|
const toast = document.getElementById('toast');
|
|
if (!toast) return;
|
|
|
|
toast.textContent = message;
|
|
toast.classList.add('show');
|
|
|
|
if (duration > 0) {
|
|
setTimeout(() => {
|
|
toast.classList.remove('show');
|
|
}, duration);
|
|
}
|
|
}
|
|
|
|
function hideToast() {
|
|
const toast = document.getElementById('toast');
|
|
if (toast) {
|
|
toast.classList.remove('show');
|
|
}
|
|
}
|
|
|
|
// Notification badge management
|
|
function updateNotificationBadge() {
|
|
const badge = document.getElementById('notificationBadge');
|
|
|
|
if (badge) {
|
|
if (AppState.unreadCount > 0) {
|
|
badge.textContent = AppState.unreadCount > 99 ? '99+' : AppState.unreadCount;
|
|
badge.classList.add('show');
|
|
} else {
|
|
badge.classList.remove('show');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Chat toggle functionality
|
|
function toggleChat() {
|
|
const chatSection = document.getElementById('chatSection');
|
|
const videoSection = document.getElementById('videoSection');
|
|
const toggleBtn = document.getElementById('chatToggleText');
|
|
|
|
if (!chatSection || !videoSection || !toggleBtn) return;
|
|
|
|
const isMobile = window.innerWidth <= AppConfig.ui.mobileBreakpoint;
|
|
AppState.chatCollapsed = !AppState.chatCollapsed;
|
|
|
|
if (AppState.chatCollapsed) {
|
|
if (isMobile) {
|
|
chatSection.classList.remove('mobile-visible');
|
|
toggleBtn.textContent = 'Show Chat';
|
|
} else {
|
|
chatSection.classList.add('collapsed');
|
|
videoSection.classList.add('expanded');
|
|
toggleBtn.textContent = 'Show Chat';
|
|
}
|
|
} else {
|
|
if (isMobile) {
|
|
chatSection.classList.add('mobile-visible');
|
|
toggleBtn.textContent = 'Hide Chat';
|
|
} else {
|
|
chatSection.classList.remove('collapsed');
|
|
videoSection.classList.remove('expanded');
|
|
toggleBtn.textContent = 'Hide Chat';
|
|
}
|
|
// Clear unread count when reopening
|
|
AppState.unreadCount = 0;
|
|
updateNotificationBadge();
|
|
}
|
|
}
|
|
|
|
// Viewer count update
|
|
function updateViewerCount(count) {
|
|
const viewerElement = document.getElementById('viewerCount');
|
|
if (viewerElement) {
|
|
viewerElement.textContent = count + (count === 1 ? ' viewer' : ' viewers');
|
|
}
|
|
}
|
|
|
|
// Connection status dot
|
|
function updateConnectionStatus(online) {
|
|
const statusDot = document.getElementById('statusDot');
|
|
const statusText = document.getElementById('statusText');
|
|
|
|
if (statusDot && statusText) {
|
|
if (online) {
|
|
statusDot.classList.remove('offline');
|
|
statusText.textContent = 'Connected';
|
|
} else {
|
|
statusDot.classList.add('offline');
|
|
statusText.textContent = 'Reconnecting...';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Keyboard shortcuts handler
|
|
function handleKeyboardShortcuts(event) {
|
|
// Skip if typing in input field
|
|
if (event.target.matches('input, textarea')) {
|
|
return;
|
|
}
|
|
|
|
switch (event.key) {
|
|
case 'c':
|
|
case 'C':
|
|
// Toggle chat
|
|
event.preventDefault();
|
|
toggleChat();
|
|
break;
|
|
case 'f':
|
|
case 'F':
|
|
// Toggle fullscreen
|
|
event.preventDefault();
|
|
toggleFullscreen();
|
|
break;
|
|
case '/':
|
|
// Focus chat input
|
|
event.preventDefault();
|
|
const messageInput = document.getElementById('messageInput');
|
|
if (messageInput) {
|
|
messageInput.focus();
|
|
}
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
// Toggle picture-in-picture
|
|
event.preventDefault();
|
|
togglePictureInPicture();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fullscreen functionality
|
|
function toggleFullscreen() {
|
|
if (!AppState.player) return;
|
|
|
|
if (AppState.player.isFullscreen()) {
|
|
AppState.player.exitFullscreen();
|
|
} else {
|
|
AppState.player.requestFullscreen();
|
|
}
|
|
}
|
|
|
|
// Picture-in-picture functionality
|
|
function togglePictureInPicture() {
|
|
const video = document.getElementById('video-player');
|
|
if (!video || !video.requestPictureInPicture) return;
|
|
|
|
if (document.pictureInPictureElement) {
|
|
document.exitPictureInPicture();
|
|
} else {
|
|
video.requestPictureInPicture();
|
|
}
|
|
}
|
|
|
|
// Sound notification for new messages
|
|
function playNotificationSound() {
|
|
if (!AppState.soundEnabled || !document.hidden) return;
|
|
|
|
try {
|
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
const oscillator = audioContext.createOscillator();
|
|
const gainNode = audioContext.createGain();
|
|
|
|
oscillator.connect(gainNode);
|
|
gainNode.connect(audioContext.destination);
|
|
|
|
oscillator.frequency.value = 800;
|
|
oscillator.type = 'sine';
|
|
gainNode.gain.value = 0.1;
|
|
|
|
oscillator.start(audioContext.currentTime);
|
|
oscillator.stop(audioContext.currentTime + 0.1);
|
|
} catch(e) {
|
|
AppLogger.log('Audio notification failed:', e);
|
|
}
|
|
}
|
|
|
|
// Mobile responsive behavior
|
|
function handleWindowResize() {
|
|
const isMobile = window.innerWidth <= AppConfig.ui.mobileBreakpoint;
|
|
|
|
if (isMobile && !AppState.chatCollapsed) {
|
|
// Auto-collapse chat on mobile for better viewing
|
|
toggleChat();
|
|
}
|
|
}
|
|
|
|
// Page visibility API handler
|
|
function handleVisibilityChange() {
|
|
if (!document.hidden && !AppState.chatCollapsed) {
|
|
// Clear unread count when page becomes visible
|
|
AppState.unreadCount = 0;
|
|
updateNotificationBadge();
|
|
}
|
|
}
|
|
|
|
// DOM utility functions
|
|
const DOMUtils = {
|
|
// Add event listener with proper cleanup tracking
|
|
addEvent: function(element, event, handler) {
|
|
if (!element) return;
|
|
|
|
element.addEventListener(event, handler);
|
|
|
|
// Store handler for potential cleanup
|
|
if (!element._handlers) {
|
|
element._handlers = new Map();
|
|
}
|
|
if (!element._handlers.has(event)) {
|
|
element._handlers.set(event, []);
|
|
}
|
|
element._handlers.get(event).push(handler);
|
|
},
|
|
|
|
// Remove event listeners (for cleanup)
|
|
removeEvent: function(element, event, handler) {
|
|
if (!element || !element._handlers || !element._handlers.has(event)) return;
|
|
|
|
const handlers = element._handlers.get(event);
|
|
const index = handlers.indexOf(handler);
|
|
if (index > -1) {
|
|
handlers.splice(index, 1);
|
|
element.removeEventListener(event, handler);
|
|
}
|
|
},
|
|
|
|
// Get element with optional error logging
|
|
getElement: function(id, required = false) {
|
|
const element = document.getElementById(id);
|
|
if (required && !element) {
|
|
AppLogger.error(`Required element with id '${id}' not found`);
|
|
}
|
|
return element;
|
|
},
|
|
|
|
// Add/remove classes safely
|
|
addClass: function(element, className) {
|
|
if (element) element.classList.add(className);
|
|
},
|
|
|
|
removeClass: function(element, className) {
|
|
if (element) element.classList.remove(className);
|
|
},
|
|
|
|
toggleClass: function(element, className) {
|
|
if (element) element.classList.toggle(className);
|
|
}
|
|
};
|
|
|
|
// Initialize event listeners
|
|
function initializeEventListeners() {
|
|
// Keyboard shortcuts
|
|
DOMUtils.addEvent(document, 'keydown', handleKeyboardShortcuts);
|
|
|
|
// Window resize for mobile responsiveness
|
|
DOMUtils.addEvent(window, 'resize', handleWindowResize);
|
|
|
|
// Page visibility for notification clearing
|
|
DOMUtils.addEvent(document, 'visibilitychange', handleVisibilityChange);
|
|
|
|
AppLogger.log('UI controls event listeners initialized');
|
|
}
|
|
|
|
// Public API
|
|
window.UIControls = {
|
|
showToast: showToast,
|
|
hideToast: hideToast,
|
|
toggleChat: toggleChat,
|
|
toggleFullscreen: toggleFullscreen,
|
|
togglePictureInPicture: togglePictureInPicture,
|
|
updateViewerCount: updateViewerCount,
|
|
updateConnectionStatus: updateConnectionStatus,
|
|
updateNotificationBadge: updateNotificationBadge,
|
|
playNotificationSound: playNotificationSound,
|
|
DOMUtils: DOMUtils
|
|
};
|
|
|
|
// Initialize when DOM is ready
|
|
document.addEventListener('DOMContentLoaded', initializeEventListeners);
|
|
|
|
AppLogger.log('UI Controls module loaded');
|
|
|
|
})();
|