Podcast-server/assets/js/main.js

179 lines
7.3 KiB
JavaScript

// 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 = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>';
setTimeout(() => {
btn.classList.remove('copy-success');
btn.innerHTML = originalHtml;
}, 2000);
}
});
}