// Smooth transitions and audio player enhancements document.addEventListener('DOMContentLoaded', () => { const cards = document.querySelectorAll('.episode-card'); // Find project root from script src const scripts = document.getElementsByTagName('script'); let projectRoot = ''; for(let s of scripts) { if(s.src.includes('main.js')) { projectRoot = s.src.split('/assets/js/main.js')[0]; } } if (!projectRoot) projectRoot = window.location.origin; // Intersection Observer for fade-in effect const observerOptions = { threshold: 0.1 }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); cards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'all 0.6s cubic-bezier(0.4, 0, 0.2, 1)'; observer.observe(card); }); // Handle audio player play states and tracking const audios = document.querySelectorAll('audio'); audios.forEach(audio => { let startTime = 0; let sessionId = null; const episodeCard = audio.closest('.episode-card'); const episodeId = episodeCard ? episodeCard.id.replace('episode-', '') : null; const generateSessionId = () => { return Date.now() + '-' + Math.random().toString(36).substr(2, 9); }; audio.addEventListener('play', () => { if (!sessionId) sessionId = generateSessionId(); startTime = Date.now(); // Pause other players audios.forEach(otherAudio => { if (otherAudio !== audio) { otherAudio.pause(); } }); }); const logPlay = (isHeartbeat = false) => { if (!episodeId || startTime === 0) return; const duration = Math.round((Date.now() - startTime) / 1000); if (duration < 1 && !isHeartbeat) { console.log('Skipping log: duration too short', duration); return; } console.log(`Tracking play for episode ${episodeId}: ${duration}s (Session: ${sessionId}, Heartbeat: ${isHeartbeat})`); fetch(projectRoot + '/includes/track_play.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `episode_id=${episodeId}&duration=${duration}&session_id=${sessionId}` }).then(response => response.json()) .then(data => console.log('Tracking response:', data)) .catch(err => console.error('Tracking fetch error:', err)); if (!isHeartbeat) startTime = 0; // Reset after logging else startTime = Date.now(); // Reset for next heartbeat }; // Log when paused or finished audio.addEventListener('pause', () => logPlay()); audio.addEventListener('ended', () => logPlay()); // Heartbeat every 30 seconds setInterval(() => { if (!audio.paused) logPlay(true); }, 30000); }); // --- Push Notifications Logic --- const notifyBtn = document.getElementById('notify-btn'); if (notifyBtn) { if (Notification.permission === 'granted') { notifyBtn.classList.add('active'); notifyBtn.textContent = '🔔 Notifications On'; } notifyBtn.addEventListener('click', async () => { console.log('Notify button clicked'); if (!('Notification' in window)) { alert('This browser does not support desktop notifications.'); return; } if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') { alert('Notifications require a secure connection (HTTPS). Please ensure your site has an SSL certificate.'); return; } try { const permission = await Notification.requestPermission(); console.log('Permission status:', permission); if (permission === 'granted') { notifyBtn.classList.add('active'); notifyBtn.textContent = '🔔 Notifications On'; new Notification('Notifications Enabled!', { body: 'You will now receive updates from our podcast.', icon: projectRoot + '/favicon.ico' }); if ('serviceWorker' in navigator) { navigator.serviceWorker.register(projectRoot + '/sw.js') .then(async reg => { console.log('SW Registered'); try { const subscription = await reg.pushManager.subscribe({ userVisibleOnly: true, // Note: In a real app, you'd generate VAPID keys. // For now, we use a placeholder or skip if browser allows. applicationServerKey: 'BM-YOUR-PUBLIC-VAPID-KEY-HERE' }); await fetch(projectRoot + '/includes/subscribe_push.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); console.log('Subscription saved to server'); } catch (e) { console.warn('Push subscription failed:', e); } }) .catch(err => console.error('SW Error:', err)); } } else if (permission === 'denied') { alert('Notifications were denied. Please enable them in your browser settings if you wish to receive updates.'); } } catch (err) { console.error('Error requesting notification permission:', err); alert('An error occurred while requesting permission. Check the console for details.'); } }); } }); /** * Copy text to clipboard with UI feedback */ function copyToClipboard(text, btnId) { const btn = document.getElementById(btnId); navigator.clipboard.writeText(text).then(() => { if (btn) { btn.classList.add('copy-success'); const originalHtml = btn.innerHTML; btn.innerHTML = ''; setTimeout(() => { btn.classList.remove('copy-success'); btn.innerHTML = originalHtml; }, 2000); } }); }