From 8dc10fd4b774b2cc91bf75f4f1680b63411b8559 Mon Sep 17 00:00:00 2001 From: YangJH Date: Sat, 18 Jan 2025 23:19:55 +0900 Subject: [PATCH] feat(web): implement web pip method and event (#4370) Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> --- docs/pages/component/events.mdx | 2 +- docs/pages/component/methods.mdx | 4 +-- src/Video.web.tsx | 53 ++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/docs/pages/component/events.mdx b/docs/pages/component/events.mdx index 47f90204..f59d35f2 100644 --- a/docs/pages/component/events.mdx +++ b/docs/pages/component/events.mdx @@ -317,7 +317,7 @@ Example: ### `onPictureInPictureStatusChanged` - + Callback function that is called when picture in picture becomes active or inactive. diff --git a/docs/pages/component/methods.mdx b/docs/pages/component/methods.mdx index 5c1f39a2..223fbd2f 100644 --- a/docs/pages/component/methods.mdx +++ b/docs/pages/component/methods.mdx @@ -80,7 +80,7 @@ Future: ### `enterPictureInPicture` - + `enterPictureInPicture()` @@ -114,7 +114,7 @@ NOTE: Video ads cannot start when you are using the PIP on iOS (more info availa ### `exitPictureInPicture` - + `exitPictureInPicture()` diff --git a/src/Video.web.tsx b/src/Video.web.tsx index 920e4065..1dcc6843 100644 --- a/src/Video.web.tsx +++ b/src/Video.web.tsx @@ -50,6 +50,7 @@ const Video = forwardRef( onVolumeChange, onEnd, onPlaybackStateChanged, + onPictureInPictureStatusChanged, }, ref, ) => { @@ -180,6 +181,31 @@ const Video = forwardRef( [setFullScreen], ); + const enterPictureInPicture = useCallback(() => { + try { + if (!nativeRef.current) { + console.error('Video Component is not mounted'); + } else { + nativeRef.current.requestPictureInPicture(); + } + } catch (e) { + console.error(e); + } + }, []); + + const exitPictureInPicture = useCallback(() => { + if ( + nativeRef.current && + nativeRef.current === document.pictureInPictureElement + ) { + try { + document.exitPictureInPicture(); + } catch (e) { + console.error(e); + } + } + }, []); + useImperativeHandle( ref, () => ({ @@ -193,8 +219,8 @@ const Video = forwardRef( dismissFullscreenPlayer, setFullScreen, save: unsupported, - enterPictureInPicture: unsupported, - exitPictureInPicture: unsupported, + enterPictureInPicture, + exitPictureInPicture, restoreUserInterfaceForPictureInPictureStopCompleted: unsupported, nativeHtmlVideoRef: nativeRef, }), @@ -210,6 +236,8 @@ const Video = forwardRef( presentFullscreenPlayer, dismissFullscreenPlayer, setFullScreen, + enterPictureInPicture, + exitPictureInPicture, ], ); @@ -254,6 +282,27 @@ const Video = forwardRef( nativeRef.current.playbackRate = rate; }, [rate]); + useEffect(() => { + if ( + typeof onPictureInPictureStatusChanged !== 'function' || + !nativeRef.current + ) { + return; + } + const onEnterPip = () => + onPictureInPictureStatusChanged({isActive: true}); + const onLeavePip = () => + onPictureInPictureStatusChanged({isActive: false}); + + const video = nativeRef.current; + video.addEventListener('enterpictureinpicture', onEnterPip); + video.addEventListener('leavepictureinpicture', onLeavePip); + return () => { + video.removeEventListener('enterpictureinpicture', onEnterPip); + video.removeEventListener('leavepictureinpicture', onLeavePip); + }; + }, [onPictureInPictureStatusChanged]); + useMediaSession(src?.metadata, nativeRef, showNotificationControls); return (