import { FC, RefObject, useEffect, useRef } from 'react';
import useIntersection from 'react-use/lib/useIntersection';
import { shallow } from 'zustand/shallow';
import { useMediaStore } from '../../context/MediaProvider';
import { useMediaListener } from '../../hooks/use-media-listener';
interface UsePipHookProps {
isPlayerReady: boolean;
}
const ROOT_MARGIN = '-48px 0px -0px 0px';
/** Bind Picture-in-Picture logic to the ``. */
export const UsePipHook: FC = ({ isPlayerReady }) => {
const [
getListener,
isPlaying,
setCurrentTime,
hasPipTriggeredByClick,
setHasPipTriggeredByClick,
mediaContainerRef,
reactPlayerRef,
isPip,
exitPip,
requestPip,
] = useMediaStore(
state => [
state.getListener,
state.isPlaying,
state.setCurrentTime,
state.hasPipTriggeredByClick,
state.setHasPipTriggeredByClick,
state.mediaContainerRef,
state.reactPlayerRef,
state.isPip,
state.exitPip,
state.requestPip,
],
shallow,
);
const listener = getListener();
// Store currentTime into a ref, to avoid rerenders
const currentTimeRef = useRef(0);
useMediaListener(
'timeupdate',
e => (currentTimeRef.current = e.seconds),
listener,
);
// Checks if media container is in viewport when scrolling bottom
const intersectionObservable = useIntersection(
mediaContainerRef as unknown as RefObject,
{
rootMargin: ROOT_MARGIN,
threshold: 0,
},
);
const isVisible = intersectionObservable?.isIntersecting;
// If we have called PIP events via click, refresh hasPipTriggeredByClick
// eg: triggering PIP by click, being in viewport wont overlap events
useEffect(() => {
if (isVisible !== undefined) {
setHasPipTriggeredByClick(isVisible);
}
}, [isVisible, setHasPipTriggeredByClick]);
// If the player is mounted, ready and isPlaying then display/hide pip player
useEffect(() => {
// Allow to enter PIP mode, except clicks on pip icon button
if (hasPipTriggeredByClick) {
return;
}
const mediaEl = reactPlayerRef?.current?.getInternalPlayer();
if (!isPlaying || !isPlayerReady || !mediaEl) {
return;
}
if (!isPip && !isVisible) {
requestPip();
}
if (isPip && isVisible) {
exitPip();
}
}, [
isPlayerReady,
isPlaying,
isVisible,
reactPlayerRef,
isPip,
hasPipTriggeredByClick,
requestPip,
exitPip,
]);
// When entering for the first time in PIP, react player got unmounted
useMediaListener(
'pipEnter',
async () => {
const mediaEl = reactPlayerRef?.current?.getInternalPlayer();
if (mediaEl) {
mediaEl.currentTime = currentTimeRef.current;
setCurrentTime?.(currentTimeRef.current);
}
},
listener,
);
return null;
};