101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
/**
|
|
* useVideoSoundControl Hook
|
|
*
|
|
* Manages video sound state with iOS autoplay compatibility.
|
|
* Videos start muted to ensure autoplay works on iOS WebKit browsers,
|
|
* then can be unmuted by user interaction.
|
|
*
|
|
* iOS WebKit autoplay policy:
|
|
* - Videos CAN autoplay if they have `playsinline` AND `muted` attributes
|
|
* - Unmuted videos require user interaction → native play button appears
|
|
* - By forcing muted start + custom sound button, we avoid native controls
|
|
*/
|
|
|
|
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
import { logger } from '../lib/logger';
|
|
|
|
export interface UseVideoSoundControlOptions {
|
|
/** Whether page settings allow sound (background_video_muted === false) */
|
|
pageHasSound: boolean;
|
|
/** Whether page has a background video */
|
|
hasBackgroundVideo: boolean;
|
|
/** Current video URL - used to detect page changes (optional) */
|
|
videoUrl?: string;
|
|
}
|
|
|
|
export interface UseVideoSoundControlResult {
|
|
/** Current muted state (always starts true for iOS compatibility) */
|
|
isMuted: boolean;
|
|
/** Whether to show sound toggle button */
|
|
showSoundButton: boolean;
|
|
/** Toggle muted state */
|
|
toggleSound: () => void;
|
|
/** Force muted state (for page changes) */
|
|
setMuted: (muted: boolean) => void;
|
|
}
|
|
|
|
/**
|
|
* Hook for managing video sound state with iOS autoplay compatibility.
|
|
*
|
|
* @example
|
|
* const { isMuted, showSoundButton, toggleSound } = useVideoSoundControl({
|
|
* pageHasSound: selectedPage?.background_video_muted === false,
|
|
* hasBackgroundVideo: Boolean(backgroundVideoUrl),
|
|
* });
|
|
*
|
|
* // Pass to RuntimeControls
|
|
* <RuntimeControls
|
|
* showSoundButton={showSoundButton}
|
|
* isMuted={isMuted}
|
|
* onSoundToggle={toggleSound}
|
|
* />
|
|
*
|
|
* // Pass to CanvasBackground (always muted for autoplay)
|
|
* <CanvasBackground videoMuted={isMuted} />
|
|
*/
|
|
export function useVideoSoundControl({
|
|
pageHasSound,
|
|
hasBackgroundVideo,
|
|
videoUrl,
|
|
}: UseVideoSoundControlOptions): UseVideoSoundControlResult {
|
|
// Always start muted for iOS autoplay compatibility
|
|
const [isMuted, setIsMuted] = useState(true);
|
|
|
|
// Track previous video URL to detect page changes
|
|
const prevVideoUrl = useRef(videoUrl);
|
|
|
|
// Reset to muted when video changes (page navigation)
|
|
// This ensures iOS autoplay works on every page - new videos always start muted
|
|
useEffect(() => {
|
|
// Only reset if there's a new video (URL changed and we have a video)
|
|
if (prevVideoUrl.current !== videoUrl && videoUrl) {
|
|
setIsMuted(true);
|
|
logger.debug('[useVideoSoundControl] Video changed, resetting to muted', {
|
|
from: prevVideoUrl.current?.slice(-20),
|
|
to: videoUrl?.slice(-20),
|
|
});
|
|
}
|
|
prevVideoUrl.current = videoUrl;
|
|
}, [videoUrl]);
|
|
|
|
const toggleSound = useCallback(() => {
|
|
setIsMuted((prev) => {
|
|
logger.debug('[useVideoSoundControl] Toggle sound:', {
|
|
from: prev,
|
|
to: !prev,
|
|
});
|
|
return !prev;
|
|
});
|
|
}, []);
|
|
|
|
return {
|
|
isMuted,
|
|
// Show button only if page allows sound AND has a background video
|
|
showSoundButton: pageHasSound && hasBackgroundVideo,
|
|
toggleSound,
|
|
setMuted: setIsMuted,
|
|
};
|
|
}
|
|
|
|
export default useVideoSoundControl;
|