import { useEffect, useRef } from 'react'; import { usePlaybackContext } from '../context/PlaybackContext/PlaybackContext'; export const PageRefreshPrevention = () => { const playback = usePlaybackContext(); const preventRefreshRef = useRef(true); const lastInteractionRef = useRef(Date.now()); // Simple Media Session API (like jelly-app approach) useEffect(() => { if ('mediaSession' in navigator && playback.currentTrack) { navigator.mediaSession.metadata = new MediaMetadata({ title: playback.currentTrack.Name || 'Unknown Track', artist: playback.currentTrack.AlbumArtist || playback.currentTrack.Artists?.[0] || 'Unknown Artist', album: playback.currentTrack.Album || 'Unknown Album', artwork: playback.currentTrack.ImageTags?.Primary ? [{ src: `${playback.currentTrack.ServerId}/Items/${playback.currentTrack.Id}/Images/Primary?maxHeight=512&maxWidth=512`, sizes: '512x512', type: 'image/webp' }] : [] }); // Set playback state navigator.mediaSession.playbackState = playback.isPlaying ? 'playing' : 'paused'; } }, [playback.currentTrack, playback.isPlaying]); useEffect(() => { // Enhanced refresh prevention const handleBeforeUnload = (e: BeforeUnloadEvent) => { // Always prevent if music is playing if (playback.isPlaying && playback.currentTrack) { e.preventDefault(); e.returnValue = 'Music is currently playing. Are you sure you want to leave?'; return 'Music is currently playing. Are you sure you want to leave?'; } // Also prevent if user hasn't interacted recently (likely accidental) const timeSinceInteraction = Date.now() - lastInteractionRef.current; if (timeSinceInteraction < 2000 && preventRefreshRef.current) { e.preventDefault(); e.returnValue = 'Are you sure you want to refresh?'; return 'Are you sure you want to refresh?'; } }; // Track user interactions to detect intentional vs accidental refreshes const trackInteraction = () => { lastInteractionRef.current = Date.now(); }; // Track touch interactions but don't prevent pull-to-refresh const handleTouchStart = (e: TouchEvent) => { trackInteraction(); }; // Prevent accidental navigation away from the page const handleUnload = () => { if (playback.isPlaying && playback.currentTrack) { // Save current playback state before unload localStorage.setItem('playbackStateBeforeUnload', JSON.stringify({ currentTrack: playback.currentTrack, currentTime: playback.audioRef.current?.currentTime || 0, isPlaying: playback.isPlaying, volume: playback.volume, timestamp: Date.now() })); } }; // Simple visibility change handling (background playback is handled by Media Session API) const handleVisibilityChange = () => { console.log('📱 Visibility change:', { hidden: document.hidden, isPlaying: playback.isPlaying, currentTrack: playback.currentTrack?.Name }); }; // Prevent keyboard shortcuts that might refresh const handleKeyDown = (e: KeyboardEvent) => { trackInteraction(); // Prevent Cmd+R / Ctrl+R (refresh) if ((e.metaKey || e.ctrlKey) && e.key === 'r') { if (playback.isPlaying && playback.currentTrack) { e.preventDefault(); console.log('Prevented refresh while music is playing'); return false; } } // Prevent F5 (refresh) if (e.key === 'F5') { if (playback.isPlaying && playback.currentTrack) { e.preventDefault(); console.log('Prevented F5 refresh while music is playing'); return false; } } }; // Add event listeners window.addEventListener('beforeunload', handleBeforeUnload); window.addEventListener('unload', handleUnload); document.addEventListener('visibilitychange', handleVisibilityChange); document.addEventListener('keydown', handleKeyDown); document.addEventListener('touchstart', handleTouchStart, { passive: true }); document.addEventListener('click', trackInteraction); document.addEventListener('scroll', trackInteraction); // Cleanup return () => { window.removeEventListener('beforeunload', handleBeforeUnload); window.removeEventListener('unload', handleUnload); document.removeEventListener('visibilitychange', handleVisibilityChange); document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('touchstart', handleTouchStart); document.removeEventListener('click', trackInteraction); document.removeEventListener('scroll', trackInteraction); }; }, [playback.isPlaying, playback.currentTrack, playback.audioRef, playback.volume]); // Prevent mobile browser from sleeping during playback useEffect(() => { let wakeLock: any = null; const requestWakeLock = async () => { if ('wakeLock' in navigator && playback.isPlaying) { try { wakeLock = await (navigator as any).wakeLock.request('screen'); console.log('Wake lock acquired'); } catch (err) { console.log('Wake lock failed:', err); } } }; const releaseWakeLock = () => { if (wakeLock) { wakeLock.release(); wakeLock = null; console.log('Wake lock released'); } }; if (playback.isPlaying) { requestWakeLock(); } else { releaseWakeLock(); } return () => { releaseWakeLock(); }; }, [playback.isPlaying]); return null; // This component doesn't render anything };