RC2
This commit is contained in:
101
static/app.js
101
static/app.js
@ -505,10 +505,15 @@ window.clearTimeout = (id) => {
|
||||
originalClearTimeout(id);
|
||||
};
|
||||
|
||||
// Track auth check calls
|
||||
// Track auth check calls and cache state
|
||||
let lastAuthCheckTime = 0;
|
||||
let authCheckCounter = 0;
|
||||
const AUTH_CHECK_DEBOUNCE = 1000; // 1 second
|
||||
let authStateCache = {
|
||||
timestamp: 0,
|
||||
value: null,
|
||||
ttl: 5000 // Cache TTL in milliseconds
|
||||
};
|
||||
|
||||
// Override console.log to capture all logs
|
||||
const originalConsoleLog = console.log;
|
||||
@ -822,33 +827,46 @@ function updateAccountDeletionVisibility(isAuthenticated) {
|
||||
});
|
||||
}
|
||||
|
||||
// Check authentication state and update UI
|
||||
function checkAuthState() {
|
||||
// Debounce rapid calls
|
||||
// Check authentication state and update UI with caching and debouncing
|
||||
function checkAuthState(force = false) {
|
||||
const now = Date.now();
|
||||
if (now - lastAuthCheckTime < AUTH_CHECK_DEBOUNCE) {
|
||||
|
||||
// Return cached value if still valid and not forcing a refresh
|
||||
if (!force && now - authStateCache.timestamp < authStateCache.ttl && authStateCache.value !== null) {
|
||||
return authStateCache.value;
|
||||
}
|
||||
|
||||
// Debounce rapid calls
|
||||
if (now - lastAuthCheckTime < AUTH_CHECK_DEBOUNCE && !force) {
|
||||
return wasAuthenticated === true;
|
||||
}
|
||||
|
||||
lastAuthCheckTime = now;
|
||||
authCheckCounter++;
|
||||
|
||||
// Check various authentication indicators
|
||||
const hasAuthCookie = document.cookie.includes('isAuthenticated=true');
|
||||
const hasUidCookie = document.cookie.includes('uid=');
|
||||
const hasLocalStorageAuth = localStorage.getItem('isAuthenticated') === 'true';
|
||||
const hasAuthToken = !!localStorage.getItem('authToken');
|
||||
|
||||
// User is considered authenticated if any of these are true
|
||||
const isAuthenticated = hasAuthCookie || hasUidCookie || hasLocalStorageAuth || hasAuthToken;
|
||||
// Use a single check for authentication state
|
||||
let isAuthenticated = false;
|
||||
|
||||
// Check the most likely indicators first for better performance
|
||||
isAuthenticated =
|
||||
document.cookie.includes('isAuthenticated=') ||
|
||||
document.cookie.includes('uid=') ||
|
||||
localStorage.getItem('isAuthenticated') === 'true' ||
|
||||
!!localStorage.getItem('authToken');
|
||||
|
||||
// Update cache
|
||||
authStateCache = {
|
||||
timestamp: now,
|
||||
value: isAuthenticated,
|
||||
ttl: isAuthenticated ? 30000 : 5000 // Longer TTL for authenticated users
|
||||
};
|
||||
|
||||
if (DEBUG_AUTH_STATE || isAuthenticated !== wasAuthenticated) {
|
||||
if (DEBUG_AUTH_STATE && isAuthenticated !== wasAuthenticated) {
|
||||
console.log('Auth State Check:', {
|
||||
hasAuthCookie,
|
||||
hasUidCookie,
|
||||
hasLocalStorageAuth,
|
||||
hasAuthToken,
|
||||
isAuthenticated,
|
||||
wasAuthenticated
|
||||
wasAuthenticated,
|
||||
cacheHit: !force && now - authStateCache.timestamp < authStateCache.ttl,
|
||||
cacheAge: now - authStateCache.timestamp
|
||||
});
|
||||
}
|
||||
|
||||
@ -887,19 +905,44 @@ function checkAuthState() {
|
||||
return isAuthenticated;
|
||||
}
|
||||
|
||||
// Periodically check authentication state
|
||||
// Periodically check authentication state with optimized polling
|
||||
function setupAuthStatePolling() {
|
||||
// Initial check
|
||||
checkAuthState();
|
||||
// Initial check with force to ensure we get the latest state
|
||||
checkAuthState(true);
|
||||
|
||||
// Check every 30 seconds instead of 2 to reduce load
|
||||
setInterval(checkAuthState, 30000);
|
||||
// Use a single interval for all checks
|
||||
const checkAndUpdate = () => {
|
||||
// Only force check if the page is visible
|
||||
checkAuthState(!document.hidden);
|
||||
};
|
||||
|
||||
// Also check after certain events that might affect auth state
|
||||
window.addEventListener('storage', checkAuthState);
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) checkAuthState();
|
||||
});
|
||||
// Check every 30 seconds (reduced from previous implementation)
|
||||
const AUTH_CHECK_INTERVAL = 30000;
|
||||
setInterval(checkAndUpdate, AUTH_CHECK_INTERVAL);
|
||||
|
||||
// Listen for storage events (like login/logout from other tabs)
|
||||
const handleStorageEvent = (e) => {
|
||||
if (['isAuthenticated', 'authToken', 'uid'].includes(e.key)) {
|
||||
checkAuthState(true); // Force check on relevant storage changes
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageEvent);
|
||||
|
||||
// Check auth state when page becomes visible
|
||||
const handleVisibilityChange = () => {
|
||||
if (!document.hidden) {
|
||||
checkAuthState(true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleStorageEvent);
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,50 +1,69 @@
|
||||
// Force hide guest navigation for authenticated users
|
||||
function fixMobileNavigation() {
|
||||
console.log('[FIX-NAV] Running navigation fix...');
|
||||
// Debounce helper function
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
// Throttle helper function
|
||||
function throttle(func, limit) {
|
||||
let inThrottle;
|
||||
return function() {
|
||||
const args = arguments;
|
||||
const context = this;
|
||||
if (!inThrottle) {
|
||||
func.apply(context, args);
|
||||
inThrottle = true;
|
||||
setTimeout(() => inThrottle = false, limit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Check authentication state once and cache it
|
||||
function getAuthState() {
|
||||
return (
|
||||
document.cookie.includes('isAuthenticated=') ||
|
||||
document.cookie.includes('uid=') ||
|
||||
localStorage.getItem('isAuthenticated') === 'true' ||
|
||||
!!localStorage.getItem('authToken')
|
||||
);
|
||||
}
|
||||
|
||||
// Update navigation based on authentication state
|
||||
function updateNavigation() {
|
||||
const isAuthenticated = getAuthState();
|
||||
|
||||
// Check if user is authenticated
|
||||
const hasAuthCookie = document.cookie.includes('isAuthenticated=true');
|
||||
const hasUidCookie = document.cookie.includes('uid=');
|
||||
const hasLocalStorageAuth = localStorage.getItem('isAuthenticated') === 'true';
|
||||
const hasAuthToken = localStorage.getItem('authToken') !== null;
|
||||
const isAuthenticated = hasAuthCookie || hasUidCookie || hasLocalStorageAuth || hasAuthToken;
|
||||
|
||||
console.log('[FIX-NAV] Authentication state:', {
|
||||
isAuthenticated,
|
||||
hasAuthCookie,
|
||||
hasUidCookie,
|
||||
hasLocalStorageAuth,
|
||||
hasAuthToken
|
||||
});
|
||||
// Only proceed if the authentication state has changed
|
||||
if (isAuthenticated === updateNavigation.lastState) {
|
||||
return;
|
||||
}
|
||||
updateNavigation.lastState = isAuthenticated;
|
||||
|
||||
if (isAuthenticated) {
|
||||
// Force hide guest navigation with !important styles
|
||||
// Hide guest navigation for authenticated users
|
||||
const guestNav = document.getElementById('guest-dashboard');
|
||||
if (guestNav) {
|
||||
console.log('[FIX-NAV] Hiding guest navigation');
|
||||
guestNav.style.cssText = `
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
opacity: 0 !important;
|
||||
height: 0 !important;
|
||||
width: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
border: none !important;
|
||||
position: absolute !important;
|
||||
overflow: hidden !important;
|
||||
position: absolute !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
pointer-events: none !important;
|
||||
`;
|
||||
guestNav.classList.add('force-hidden');
|
||||
}
|
||||
|
||||
// Ensure user navigation is visible with !important styles
|
||||
// Show user navigation if it exists
|
||||
const userNav = document.getElementById('user-dashboard');
|
||||
if (userNav) {
|
||||
console.log('[FIX-NAV] Showing user navigation');
|
||||
userNav.style.cssText = `
|
||||
display: flex !important;
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
height: auto !important;
|
||||
@ -55,25 +74,9 @@ function fixMobileNavigation() {
|
||||
userNav.classList.add('force-visible');
|
||||
}
|
||||
|
||||
// Add authenticated class to body
|
||||
// Update body classes
|
||||
document.body.classList.add('authenticated');
|
||||
document.body.classList.remove('guest-mode');
|
||||
|
||||
// Prevent default behavior of nav links that might cause page reloads
|
||||
document.querySelectorAll('a[href^="#"]').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const targetId = link.getAttribute('href');
|
||||
if (targetId && targetId !== '#') {
|
||||
// Use history API to update URL without full page reload
|
||||
history.pushState(null, '', targetId);
|
||||
// Dispatch a custom event that other scripts can listen for
|
||||
window.dispatchEvent(new CustomEvent('hashchange'));
|
||||
// Force re-apply our navigation fix
|
||||
setTimeout(fixMobileNavigation, 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// User is not authenticated - ensure guest nav is visible
|
||||
const guestNav = document.getElementById('guest-dashboard');
|
||||
@ -85,50 +88,53 @@ function fixMobileNavigation() {
|
||||
}
|
||||
}
|
||||
|
||||
// Run on page load
|
||||
document.addEventListener('DOMContentLoaded', fixMobileNavigation);
|
||||
// Initialize the navigation state
|
||||
updateNavigation.lastState = null;
|
||||
|
||||
// Also run after a short delay to catch any dynamic content
|
||||
setTimeout(fixMobileNavigation, 100);
|
||||
setTimeout(fixMobileNavigation, 300);
|
||||
setTimeout(fixMobileNavigation, 1000);
|
||||
|
||||
// Listen for hash changes (navigation)
|
||||
window.addEventListener('hashchange', fixMobileNavigation);
|
||||
|
||||
// Listen for pushState/replaceState (SPA navigation)
|
||||
const originalPushState = history.pushState;
|
||||
const originalReplaceState = history.replaceState;
|
||||
|
||||
history.pushState = function() {
|
||||
originalPushState.apply(this, arguments);
|
||||
setTimeout(fixMobileNavigation, 0);
|
||||
};
|
||||
|
||||
history.replaceState = function() {
|
||||
originalReplaceState.apply(this, arguments);
|
||||
setTimeout(fixMobileNavigation, 0);
|
||||
};
|
||||
|
||||
// Run on any DOM mutations (for dynamically loaded content)
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
let shouldFix = false;
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.addedNodes.length || mutation.removedNodes.length) {
|
||||
shouldFix = true;
|
||||
}
|
||||
});
|
||||
if (shouldFix) {
|
||||
setTimeout(fixMobileNavigation, 0);
|
||||
// Handle navigation link clicks
|
||||
function handleNavLinkClick(e) {
|
||||
const link = e.target.closest('a[href^="#"]');
|
||||
if (!link) return;
|
||||
|
||||
e.preventDefault();
|
||||
const targetId = link.getAttribute('href');
|
||||
if (targetId && targetId !== '#') {
|
||||
// Update URL without triggering full page reload
|
||||
history.pushState(null, '', targetId);
|
||||
// Dispatch a custom event for other scripts
|
||||
window.dispatchEvent(new CustomEvent('hashchange'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['class', 'style', 'id']
|
||||
});
|
||||
// Initialize the navigation system
|
||||
function initNavigation() {
|
||||
// Set up event delegation for navigation links
|
||||
document.body.addEventListener('click', handleNavLinkClick);
|
||||
|
||||
// Listen for hash changes (throttled)
|
||||
window.addEventListener('hashchange', throttle(updateNavigation, 100));
|
||||
|
||||
// Listen for storage changes (like login/logout from other tabs)
|
||||
window.addEventListener('storage', debounce(updateNavigation, 100));
|
||||
|
||||
// Check for authentication changes periodically (every 30 seconds)
|
||||
setInterval(updateNavigation, 30000);
|
||||
|
||||
// Initial update
|
||||
updateNavigation();
|
||||
}
|
||||
|
||||
// Export for debugging
|
||||
window.fixMobileNavigation = fixMobileNavigation;
|
||||
// Run initialization when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initNavigation);
|
||||
} else {
|
||||
// DOMContentLoaded has already fired
|
||||
initNavigation();
|
||||
}
|
||||
|
||||
// Export for testing if needed
|
||||
window.navigationUtils = {
|
||||
updateNavigation,
|
||||
getAuthState,
|
||||
initNavigation
|
||||
};
|
||||
|
@ -8,5 +8,7 @@
|
||||
<a href="#" data-target="privacy-page">Privacy</a>
|
||||
<span class="separator">•</span>
|
||||
<a href="#" data-target="imprint-page">Imprint</a>
|
||||
<span class="separator auth-only" style="display: none;">•</span>
|
||||
<a href="#" data-target="your-stream" class="auth-only" style="display: none;">Your Stream</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -258,8 +258,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
e.preventDefault();
|
||||
const target = link.dataset.target;
|
||||
if (target) {
|
||||
// Show the target section without updating URL hash
|
||||
showSection(target);
|
||||
// Update URL hash to maintain proper history state
|
||||
window.location.hash = target;
|
||||
// Use the router to handle the navigation
|
||||
if (router && typeof router.showOnly === 'function') {
|
||||
router.showOnly(target);
|
||||
} else {
|
||||
// Fallback to showSection if router is not available
|
||||
showSection(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user