import { Portal } from '@gorhom/portal' import React, { useCallback, useEffect, useRef, useState } from 'react' import { Pressable, Text, View } from 'react-native' import { resolveMediaUri } from '../utils/resolveMediaUri' const CLOSE_THRESHOLD_SECONDS = 0.35 const CLOSE_POLL_INTERVAL_MS = 500 const overlayStyle = { position: 'fixed', top: 0, right: 0, bottom: 0, left: 0, backgroundColor: 'black', justifyContent: 'center', alignItems: 'center', zIndex: 9999, } const videoStyle = { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', objectFit: 'cover', } const closeButtonStyle = { position: 'absolute', top: 50, right: 20, backgroundColor: '#00000080', paddingVertical: 10, paddingHorizontal: 14, borderRadius: 20, borderWidth: 1, borderColor: '#FFFFFF55', cursor: 'pointer', } const closeTextStyle = { color: '#FFF', fontSize: 14, } const playButtonStyle = { backgroundColor: '#000000CC', paddingVertical: 14, paddingHorizontal: 20, borderRadius: 24, borderWidth: 1, borderColor: '#FFFFFF88', cursor: 'pointer', zIndex: 1, } const FullscreenIntroVideo = ({ url, visible = true, onClose, contentFit = 'cover', allowSkip = true, requireAudio = false, offerSoundActivation = false, skipLabel = 'Passer la vidéo', playLabel = 'Lire la vidéo avec le son', }) => { const videoRef = useRef(null) const [uri, setUri] = useState(null) const [muted, setMuted] = useState(false) const [awaitingInteraction, setAwaitingInteraction] = useState(false) const hasClosedRef = useRef(false) const handleClose = useCallback(() => { if (hasClosedRef.current) return hasClosedRef.current = true onClose?.() }, [onClose]) const evaluateShouldClose = useCallback(() => { const video = videoRef.current if (!video || hasClosedRef.current) { return } if (video.ended) { handleClose() return } const remaining = video.duration - video.currentTime if (Number.isFinite(remaining) && remaining <= CLOSE_THRESHOLD_SECONDS) { handleClose() } }, [handleClose]) useEffect(() => { let isMounted = true const assignUri = (nextUri) => { if (isMounted) { setMuted(false) setAwaitingInteraction(false) setUri(nextUri) } } const load = async () => { try { const nextUri = await resolveMediaUri(url) if (!nextUri) { if (isMounted) { setUri(null) handleClose() } return } assignUri(nextUri) } catch { if (isMounted) { setUri(null) handleClose() } } } load() return () => { isMounted = false } }, [handleClose, url]) useEffect(() => { if (!visible || !uri) { return undefined } hasClosedRef.current = false let rafId const attemptPlay = () => { const video = videoRef.current if (!video) { rafId = requestAnimationFrame(attemptPlay) return } video.currentTime = 0 const result = video.play() if (result?.catch) { result.catch((error) => { if (error?.name === 'NotAllowedError' && requireAudio) { setAwaitingInteraction(true) } else if (error?.name === 'NotAllowedError' && !muted) { video.muted = true setMuted(true) setAwaitingInteraction(offerSoundActivation) const mutedResult = video.play() mutedResult?.catch?.(() => {}) } }) } } attemptPlay() const pollId = setInterval(evaluateShouldClose, CLOSE_POLL_INTERVAL_MS) return () => { if (rafId) { cancelAnimationFrame(rafId) } clearInterval(pollId) const video = videoRef.current video?.pause() } }, [evaluateShouldClose, muted, offerSoundActivation, requireAudio, uri, visible]) const handleManualPlay = useCallback(() => { const video = videoRef.current if (!video) return setMuted(false) video.muted = false video.currentTime = 0 const result = video.play() if (result?.then) { result.then(() => setAwaitingInteraction(false)).catch(() => {}) } }, []) useEffect(() => { const video = videoRef.current if (!video || !muted) { return } const result = video.play() if (result?.catch) { result.catch(() => {}) } }, [muted]) if (!visible) { return null } return ( {uri ? ( ) } export default FullscreenIntroVideo