Fix double audio playback and add UID handling for personal stream
- Fixed double playback issue on stream page by properly scoping event delegation in streams-ui.js - Added init-personal-stream.js to handle UID for personal stream playback - Improved error handling and logging for audio playback - Added proper event propagation control to prevent duplicate event handling
This commit is contained in:
@ -1,136 +1,55 @@
|
||||
// inject-nav.js - Handles dynamic injection and management of navigation elements
|
||||
import { showOnly } from './router.js';
|
||||
|
||||
// Menu state
|
||||
let isMenuOpen = false;
|
||||
|
||||
// Export the injectNavigation function
|
||||
export function injectNavigation(isAuthenticated = false) {
|
||||
console.log('injectNavigation called with isAuthenticated:', isAuthenticated);
|
||||
const navContainer = document.getElementById('main-navigation');
|
||||
if (!navContainer) {
|
||||
console.error('Navigation container not found. Looking for #main-navigation');
|
||||
console.log('Available elements with id:', document.querySelectorAll('[id]'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing content
|
||||
navContainer.innerHTML = '';
|
||||
// Function to set up guest navigation links
|
||||
function setupGuestNav() {
|
||||
const guestDashboard = document.getElementById('guest-dashboard');
|
||||
if (!guestDashboard) return;
|
||||
|
||||
console.log('Creating navigation...');
|
||||
try {
|
||||
// Create the navigation wrapper
|
||||
const navWrapper = document.createElement('nav');
|
||||
navWrapper.className = 'nav-wrapper';
|
||||
|
||||
// Create the navigation content
|
||||
const nav = isAuthenticated ? createUserNav() : createGuestNav();
|
||||
console.log('Navigation HTML created:', nav.outerHTML);
|
||||
|
||||
// Append navigation to wrapper
|
||||
navWrapper.appendChild(nav);
|
||||
|
||||
// Append to container
|
||||
navContainer.appendChild(navWrapper);
|
||||
|
||||
console.log('Navigation appended to container');
|
||||
|
||||
// Initialize menu toggle after navigation is injected
|
||||
setupMenuToggle();
|
||||
|
||||
// Set up menu links
|
||||
setupMenuLinks();
|
||||
|
||||
// Add click handler for the logo to navigate home
|
||||
const logo = document.querySelector('.logo');
|
||||
if (logo) {
|
||||
logo.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
showOnly('welcome');
|
||||
closeMenu();
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating navigation:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up menu toggle for mobile
|
||||
setupMenuToggle();
|
||||
|
||||
// Set up menu links
|
||||
setupMenuLinks();
|
||||
|
||||
// Close menu when clicking on a nav link on mobile
|
||||
const navLinks = navContainer.querySelectorAll('.nav-link');
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
if (window.innerWidth < 768) { // Mobile breakpoint
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Add click handler for the logo to navigate home
|
||||
const logo = document.querySelector('.logo');
|
||||
if (logo) {
|
||||
logo.addEventListener('click', (e) => {
|
||||
const links = guestDashboard.querySelectorAll('a');
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
showOnly('welcome');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Function to create the guest navigation
|
||||
function createGuestNav() {
|
||||
const nav = document.createElement('div');
|
||||
nav.className = 'dashboard-nav';
|
||||
nav.setAttribute('role', 'navigation');
|
||||
nav.setAttribute('aria-label', 'Main navigation');
|
||||
|
||||
const navList = document.createElement('ul');
|
||||
navList.className = 'nav-list';
|
||||
|
||||
const links = [
|
||||
{ id: 'nav-login', target: 'login', text: 'Login / Register' },
|
||||
{ id: 'nav-streams', target: 'streams', text: 'Streams' },
|
||||
{ id: 'nav-welcome', target: 'welcome', text: 'Welcome' }
|
||||
];
|
||||
|
||||
// Create and append links
|
||||
links.forEach((link) => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'nav-item';
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.id = link.id;
|
||||
a.href = `#${link.target}`;
|
||||
a.className = 'nav-link';
|
||||
a.setAttribute('data-target', link.target);
|
||||
a.textContent = link.text;
|
||||
|
||||
// Add click handler for navigation
|
||||
a.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const target = e.currentTarget.getAttribute('data-target');
|
||||
const target = link.getAttribute('href')?.substring(1); // Remove '#'
|
||||
if (target) {
|
||||
window.location.hash = target;
|
||||
if (window.router && typeof window.router.showOnly === 'function') {
|
||||
window.router.showOnly(target);
|
||||
}
|
||||
// Close menu on mobile after clicking a link
|
||||
if (window.innerWidth < 768) {
|
||||
closeMenu();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
li.appendChild(a);
|
||||
navList.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to set up user navigation links
|
||||
function setupUserNav() {
|
||||
const userDashboard = document.getElementById('user-dashboard');
|
||||
if (!userDashboard) return;
|
||||
|
||||
nav.appendChild(navList);
|
||||
return nav;
|
||||
const links = userDashboard.querySelectorAll('a');
|
||||
links.forEach(link => {
|
||||
// Handle logout specially
|
||||
if (link.getAttribute('href') === '#logout') {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
if (window.handleLogout) {
|
||||
window.handleLogout();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Handle regular navigation
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const target = link.getAttribute('href')?.substring(1); // Remove '#'
|
||||
if (target) {
|
||||
window.location.hash = target;
|
||||
if (window.router && typeof window.router.showOnly === 'function') {
|
||||
window.router.showOnly(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createUserNav() {
|
||||
@ -150,7 +69,7 @@ function createUserNav() {
|
||||
];
|
||||
|
||||
// Create and append links
|
||||
links.forEach((link, index) => {
|
||||
links.forEach((link) => {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'nav-item';
|
||||
|
||||
@ -158,35 +77,24 @@ function createUserNav() {
|
||||
a.id = link.id;
|
||||
a.href = '#';
|
||||
a.className = 'nav-link';
|
||||
|
||||
// Special handling for logout
|
||||
if (link.target === 'logout') {
|
||||
a.href = '#';
|
||||
a.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
closeMenu();
|
||||
// Use the handleLogout function from dashboard.js if available
|
||||
if (typeof handleLogout === 'function') {
|
||||
await handleLogout();
|
||||
} else {
|
||||
// Fallback in case handleLogout is not available
|
||||
localStorage.removeItem('user');
|
||||
localStorage.removeItem('uid');
|
||||
localStorage.removeItem('uid_time');
|
||||
localStorage.removeItem('confirmed_uid');
|
||||
document.cookie = 'uid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
||||
window.location.href = '/';
|
||||
}
|
||||
window.location.href = '#';
|
||||
// Force reload to reset the app state
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
a.setAttribute('data-target', link.target);
|
||||
}
|
||||
|
||||
a.setAttribute('data-target', link.target);
|
||||
a.textContent = link.text;
|
||||
|
||||
a.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const target = e.currentTarget.getAttribute('data-target');
|
||||
if (target === 'logout') {
|
||||
if (window.handleLogout) {
|
||||
window.handleLogout();
|
||||
}
|
||||
} else if (target) {
|
||||
window.location.hash = target;
|
||||
if (window.router && typeof window.router.showOnly === 'function') {
|
||||
window.router.showOnly(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
li.appendChild(a);
|
||||
navList.appendChild(li);
|
||||
});
|
||||
@ -195,243 +103,82 @@ function createUserNav() {
|
||||
return nav;
|
||||
}
|
||||
|
||||
// Set up menu toggle functionality
|
||||
function setupMenuToggle() {
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
const navWrapper = document.querySelector('.nav-wrapper');
|
||||
|
||||
if (!menuToggle || !navWrapper) return;
|
||||
|
||||
menuToggle.addEventListener('click', toggleMenu);
|
||||
|
||||
// Close menu when clicking outside
|
||||
document.addEventListener('click', (e) => {
|
||||
if (isMenuOpen && !navWrapper.contains(e.target) && !menuToggle.contains(e.target)) {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// Close menu on escape key
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && isMenuOpen) {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// Close menu when resizing to desktop
|
||||
let resizeTimer;
|
||||
window.addEventListener('resize', () => {
|
||||
if (window.innerWidth >= 768) {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Toggle mobile menu
|
||||
function toggleMenu(event) {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
const navWrapper = document.querySelector('.nav-wrapper');
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
|
||||
if (!navWrapper || !menuToggle) return;
|
||||
|
||||
isMenuOpen = !isMenuOpen;
|
||||
|
||||
if (isMenuOpen) {
|
||||
// Open menu
|
||||
navWrapper.classList.add('active');
|
||||
menuToggle.setAttribute('aria-expanded', 'true');
|
||||
menuToggle.innerHTML = '✕';
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// Focus the first link in the menu for better keyboard navigation
|
||||
const firstLink = navWrapper.querySelector('a');
|
||||
if (firstLink) firstLink.focus();
|
||||
|
||||
// Add click outside handler
|
||||
document._handleClickOutside = (e) => {
|
||||
if (!navWrapper.contains(e.target) && e.target !== menuToggle) {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', document._handleClickOutside);
|
||||
|
||||
// Add escape key handler
|
||||
document._handleEscape = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
closeMenu();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', document._handleEscape);
|
||||
} else {
|
||||
closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
// Close menu function
|
||||
function closeMenu() {
|
||||
const navWrapper = document.querySelector('.nav-wrapper');
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
|
||||
if (!navWrapper || !menuToggle) return;
|
||||
|
||||
isMenuOpen = false;
|
||||
navWrapper.classList.remove('active');
|
||||
menuToggle.setAttribute('aria-expanded', 'false');
|
||||
menuToggle.innerHTML = '☰';
|
||||
document.body.style.overflow = '';
|
||||
|
||||
// Remove event listeners
|
||||
if (document._handleClickOutside) {
|
||||
document.removeEventListener('click', document._handleClickOutside);
|
||||
delete document._handleClickOutside;
|
||||
}
|
||||
|
||||
if (document._handleEscape) {
|
||||
document.removeEventListener('keydown', document._handleEscape);
|
||||
delete document._handleEscape;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize menu toggle on page load
|
||||
function initializeMenuToggle() {
|
||||
console.log('Initializing menu toggle...');
|
||||
const menuToggle = document.getElementById('menu-toggle');
|
||||
|
||||
if (!menuToggle) {
|
||||
console.error('Main menu toggle button not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Menu toggle button found:', menuToggle);
|
||||
|
||||
// Remove any existing click listeners
|
||||
const newToggle = menuToggle.cloneNode(true);
|
||||
if (menuToggle.parentNode) {
|
||||
menuToggle.parentNode.replaceChild(newToggle, menuToggle);
|
||||
console.log('Replaced menu toggle button');
|
||||
} else {
|
||||
console.error('Menu toggle has no parent node!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add click handler to the new toggle
|
||||
newToggle.addEventListener('click', function(event) {
|
||||
console.log('Menu toggle clicked!', event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleMenu(event);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Also handle the header menu toggle if it exists
|
||||
const headerMenuToggle = document.getElementById('header-menu-toggle');
|
||||
if (headerMenuToggle) {
|
||||
console.log('Header menu toggle found, syncing with main menu');
|
||||
headerMenuToggle.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
newToggle.click(); // Trigger the main menu toggle
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('DOM fully loaded and parsed');
|
||||
|
||||
// Initialize navigation based on authentication state
|
||||
// This will be set by the main app after checking auth status
|
||||
if (window.initializeNavigation) {
|
||||
window.initializeNavigation();
|
||||
}
|
||||
|
||||
// Initialize menu toggle
|
||||
initializeMenuToggle();
|
||||
|
||||
// Also try to initialize after a short delay in case the DOM changes
|
||||
setTimeout(initializeMenuToggle, 500);
|
||||
});
|
||||
|
||||
// Navigation injection function
|
||||
export function injectNavigation(isAuthenticated = false) {
|
||||
console.log('Injecting navigation, isAuthenticated:', isAuthenticated);
|
||||
const container = document.getElementById('main-navigation');
|
||||
const navWrapper = document.querySelector('.nav-wrapper');
|
||||
// Get the appropriate dashboard element based on auth state
|
||||
const guestDashboard = document.getElementById('guest-dashboard');
|
||||
const userDashboard = document.getElementById('user-dashboard');
|
||||
|
||||
if (!container || !navWrapper) {
|
||||
console.error('Navigation elements not found. Looking for #main-navigation and .nav-wrapper');
|
||||
return null;
|
||||
if (isAuthenticated) {
|
||||
// Show user dashboard, hide guest dashboard
|
||||
if (guestDashboard) guestDashboard.style.display = 'none';
|
||||
if (userDashboard) userDashboard.style.display = 'block';
|
||||
document.body.classList.add('authenticated');
|
||||
document.body.classList.remove('guest-mode');
|
||||
} else {
|
||||
// Show guest dashboard, hide user dashboard
|
||||
if (guestDashboard) guestDashboard.style.display = 'block';
|
||||
if (userDashboard) userDashboard.style.display = 'none';
|
||||
document.body.classList.add('guest-mode');
|
||||
document.body.classList.remove('authenticated');
|
||||
}
|
||||
|
||||
try {
|
||||
// Store scroll position
|
||||
const scrollPosition = window.scrollY;
|
||||
|
||||
// Clear existing navigation
|
||||
container.innerHTML = '';
|
||||
|
||||
// Create the appropriate navigation based on authentication status
|
||||
const nav = isAuthenticated ? createUserNav() : createGuestNav();
|
||||
|
||||
// Append the navigation to the container
|
||||
container.appendChild(nav);
|
||||
|
||||
// Set up menu toggle functionality
|
||||
setupMenuToggle();
|
||||
|
||||
// Set up navigation links
|
||||
setupMenuLinks();
|
||||
|
||||
// Show the appropriate page based on URL
|
||||
if (window.location.hash === '#streams' || window.location.pathname === '/streams') {
|
||||
showOnly('stream-page');
|
||||
if (typeof window.maybeLoadStreamsOnShow === 'function') {
|
||||
window.maybeLoadStreamsOnShow();
|
||||
}
|
||||
} else if (!window.location.hash || window.location.hash === '#') {
|
||||
// Show welcome page by default if no hash
|
||||
showOnly('welcome-page');
|
||||
}
|
||||
|
||||
// Restore scroll position
|
||||
window.scrollTo(0, scrollPosition);
|
||||
|
||||
return nav;
|
||||
} catch (error) {
|
||||
console.error('Error injecting navigation:', error);
|
||||
return null;
|
||||
}
|
||||
// Set up menu links and active state
|
||||
setupMenuLinks();
|
||||
updateActiveNav();
|
||||
|
||||
return isAuthenticated ? userDashboard : guestDashboard;
|
||||
}
|
||||
|
||||
// Set up menu links with click handlers
|
||||
function setupMenuLinks() {
|
||||
// Handle navigation link clicks
|
||||
document.addEventListener('click', function(e) {
|
||||
// Check if click is on a nav link or its children
|
||||
let link = e.target.closest('.nav-link');
|
||||
if (!link) return;
|
||||
|
||||
const target = link.getAttribute('data-target');
|
||||
if (target) {
|
||||
e.preventDefault();
|
||||
console.log('Navigation link clicked:', target);
|
||||
showOnly(target);
|
||||
closeMenu();
|
||||
|
||||
// Update active state
|
||||
document.querySelectorAll('.nav-link').forEach(l => {
|
||||
l.classList.remove('active');
|
||||
});
|
||||
// Set up guest and user navigation links
|
||||
setupGuestNav();
|
||||
setupUserNav();
|
||||
|
||||
// Handle hash changes for SPA navigation
|
||||
window.addEventListener('hashchange', updateActiveNav);
|
||||
}
|
||||
|
||||
// Update active navigation link
|
||||
function updateActiveNav() {
|
||||
const currentHash = window.location.hash.substring(1) || 'welcome';
|
||||
|
||||
// Remove active class from all links in both dashboards
|
||||
document.querySelectorAll('#guest-dashboard a, #user-dashboard a').forEach(link => {
|
||||
link.classList.remove('active');
|
||||
// Check if this link's href matches the current hash
|
||||
const linkTarget = link.getAttribute('href')?.substring(1); // Remove '#'
|
||||
if (linkTarget === currentHash) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check authentication state and initialize navigation
|
||||
const isAuthenticated = document.cookie.includes('sessionid=') ||
|
||||
localStorage.getItem('isAuthenticated') === 'true';
|
||||
|
||||
// Initialize navigation based on authentication state
|
||||
injectNavigation(isAuthenticated);
|
||||
|
||||
// Set up menu links and active navigation
|
||||
setupMenuLinks();
|
||||
updateActiveNav();
|
||||
|
||||
// Update body classes based on authentication state
|
||||
if (isAuthenticated) {
|
||||
document.body.classList.add('authenticated');
|
||||
document.body.classList.remove('guest-mode');
|
||||
} else {
|
||||
document.body.classList.add('guest-mode');
|
||||
document.body.classList.remove('authenticated');
|
||||
}
|
||||
|
||||
console.log('[NAV] Navigation initialized', { isAuthenticated });
|
||||
});
|
||||
|
||||
// Make the function available globally for debugging
|
||||
window.injectNavigation = injectNavigation;
|
||||
|
Reference in New Issue
Block a user