|
1 | 1 | import { useCallback, useEffect, useRef, useState } from 'react'
|
2 | 2 |
|
| 3 | +interface DocumentWithFullscreen extends Document { |
| 4 | + webkitFullscreenElement?: Element | null |
| 5 | + mozFullScreenElement?: Element | null |
| 6 | + msFullscreenElement?: Element | null |
| 7 | + webkitExitFullscreen?: () => Promise<void> |
| 8 | + mozCancelFullScreen?: () => Promise<void> |
| 9 | + msExitFullscreen?: () => Promise<void> |
| 10 | +} |
| 11 | + |
| 12 | +interface HTMLElementWithFullscreen extends HTMLElement { |
| 13 | + webkitRequestFullscreen?: () => Promise<void> |
| 14 | + mozRequestFullScreen?: () => Promise<void> |
| 15 | + msRequestFullscreen?: () => Promise<void> |
| 16 | +} |
| 17 | + |
| 18 | +interface HTMLVideoElementWithFullscreen extends HTMLVideoElement { |
| 19 | + webkitEnterFullscreen?: () => void |
| 20 | + webkitExitFullscreen?: () => void |
| 21 | +} |
| 22 | + |
3 | 23 | export function useVideoPlayer(videoRef: React.RefObject<HTMLVideoElement>) {
|
4 | 24 | // Core state
|
5 | 25 | const [status, setStatus] = useState<'loading' | 'playing' | 'playing-with-sound'>('loading')
|
@@ -65,27 +85,59 @@ export function useVideoPlayer(videoRef: React.RefObject<HTMLVideoElement>) {
|
65 | 85 |
|
66 | 86 | // Toggle fullscreen
|
67 | 87 | const toggleFullscreen = useCallback(() => {
|
68 |
| - const container = videoRef.current?.closest('.video-inner-container') as HTMLElement |
| 88 | + const container = videoRef.current?.closest('.video-inner-container') as HTMLElementWithFullscreen |
69 | 89 | if (!container) return
|
70 | 90 |
|
71 |
| - if (!document.fullscreenElement) { |
72 |
| - container |
73 |
| - .requestFullscreen() |
74 |
| - .then(() => { |
75 |
| - setIsFullscreen(true) |
76 |
| - }) |
77 |
| - .catch(err => { |
78 |
| - console.log(`Error attempting to enable fullscreen: ${err.message}`) |
79 |
| - }) |
| 91 | + // Check if we're already in fullscreen mode |
| 92 | + const isFullscreen = |
| 93 | + document.fullscreenElement || |
| 94 | + (document as DocumentWithFullscreen).webkitFullscreenElement || |
| 95 | + (document as DocumentWithFullscreen).mozFullScreenElement || |
| 96 | + (document as DocumentWithFullscreen).msFullscreenElement |
| 97 | + |
| 98 | + if (!isFullscreen) { |
| 99 | + // Try different fullscreen methods |
| 100 | + if (container.requestFullscreen) { |
| 101 | + container.requestFullscreen() |
| 102 | + } else if (container.webkitRequestFullscreen) { |
| 103 | + container.webkitRequestFullscreen() |
| 104 | + } else if (container.mozRequestFullScreen) { |
| 105 | + container.mozRequestFullScreen() |
| 106 | + } else if (container.msRequestFullscreen) { |
| 107 | + container.msRequestFullscreen() |
| 108 | + } else if ((videoRef.current as HTMLVideoElementWithFullscreen)?.webkitEnterFullscreen) { |
| 109 | + // iOS Safari specific method |
| 110 | + const video = videoRef.current as HTMLVideoElementWithFullscreen |
| 111 | + if (video.webkitEnterFullscreen) { |
| 112 | + video.webkitEnterFullscreen() |
| 113 | + } |
| 114 | + } |
80 | 115 | } else {
|
81 |
| - document |
82 |
| - .exitFullscreen() |
83 |
| - .then(() => { |
84 |
| - setIsFullscreen(false) |
85 |
| - }) |
86 |
| - .catch(err => { |
87 |
| - console.log(`Error attempting to exit fullscreen: ${err.message}`) |
88 |
| - }) |
| 116 | + // Exit fullscreen |
| 117 | + if (document.exitFullscreen) { |
| 118 | + document.exitFullscreen() |
| 119 | + } else if ((document as DocumentWithFullscreen).webkitExitFullscreen) { |
| 120 | + const doc = document as DocumentWithFullscreen |
| 121 | + if (doc.webkitExitFullscreen) { |
| 122 | + doc.webkitExitFullscreen() |
| 123 | + } |
| 124 | + } else if ((document as DocumentWithFullscreen).mozCancelFullScreen) { |
| 125 | + const doc = document as DocumentWithFullscreen |
| 126 | + if (doc.mozCancelFullScreen) { |
| 127 | + doc.mozCancelFullScreen() |
| 128 | + } |
| 129 | + } else if ((document as DocumentWithFullscreen).msExitFullscreen) { |
| 130 | + const doc = document as DocumentWithFullscreen |
| 131 | + if (doc.msExitFullscreen) { |
| 132 | + doc.msExitFullscreen() |
| 133 | + } |
| 134 | + } else if ((videoRef.current as HTMLVideoElementWithFullscreen)?.webkitExitFullscreen) { |
| 135 | + // iOS Safari specific method |
| 136 | + const video = videoRef.current as HTMLVideoElementWithFullscreen |
| 137 | + if (video.webkitExitFullscreen) { |
| 138 | + video.webkitExitFullscreen() |
| 139 | + } |
| 140 | + } |
89 | 141 | }
|
90 | 142 | }, [videoRef])
|
91 | 143 |
|
@@ -193,6 +245,30 @@ export function useVideoPlayer(videoRef: React.RefObject<HTMLVideoElement>) {
|
193 | 245 | }
|
194 | 246 | }, [status, videoRef])
|
195 | 247 |
|
| 248 | + // Add fullscreen change event listeners |
| 249 | + useEffect(() => { |
| 250 | + const handleFullscreenChange = () => { |
| 251 | + const isFullscreen = |
| 252 | + document.fullscreenElement || |
| 253 | + (document as DocumentWithFullscreen).webkitFullscreenElement || |
| 254 | + (document as DocumentWithFullscreen).mozFullScreenElement || |
| 255 | + (document as DocumentWithFullscreen).msFullscreenElement |
| 256 | + setIsFullscreen(!!isFullscreen) |
| 257 | + } |
| 258 | + |
| 259 | + document.addEventListener('fullscreenchange', handleFullscreenChange) |
| 260 | + document.addEventListener('webkitfullscreenchange', handleFullscreenChange) |
| 261 | + document.addEventListener('mozfullscreenchange', handleFullscreenChange) |
| 262 | + document.addEventListener('MSFullscreenChange', handleFullscreenChange) |
| 263 | + |
| 264 | + return () => { |
| 265 | + document.removeEventListener('fullscreenchange', handleFullscreenChange) |
| 266 | + document.removeEventListener('webkitfullscreenchange', handleFullscreenChange) |
| 267 | + document.removeEventListener('mozfullscreenchange', handleFullscreenChange) |
| 268 | + document.removeEventListener('MSFullscreenChange', handleFullscreenChange) |
| 269 | + } |
| 270 | + }, []) |
| 271 | + |
196 | 272 | // Return the state and handlers
|
197 | 273 | return {
|
198 | 274 | status,
|
|
0 commit comments