From 08ac6c47196bd0c149f17f58b5952ad633540911 Mon Sep 17 00:00:00 2001
From: Amar Bathwal <110378139+amar-1995@users.noreply.github.com>
Date: Tue, 19 Mar 2024 18:14:54 +0530
Subject: [PATCH] feat: add play pause state for desktop, fullscreen fixes
---
.../Prebuilt/components/Footer/ChatToggle.tsx | 4 +-
.../Prebuilt/components/HMSVideo/HMSVideo.jsx | 1 +
.../components/HMSVideo/PlayPauseButton.tsx | 4 +-
.../HMSVideo/PlayPauseSeekControls.tsx | 158 ++++++++++++++
.../{SeekControls.tsx => SeekControl.tsx} | 4 +-
.../components/HMSVideo/VideoProgress.tsx | 10 +-
.../src/Prebuilt/components/HMSVideo/index.ts | 9 +-
.../src/Prebuilt/layouts/HLSView.jsx | 193 +++++++++++-------
8 files changed, 292 insertions(+), 91 deletions(-)
create mode 100644 packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseSeekControls.tsx
rename packages/roomkit-react/src/Prebuilt/components/HMSVideo/{SeekControls.tsx => SeekControl.tsx} (91%)
diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/ChatToggle.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/ChatToggle.tsx
index 5ae0033156..8615b3bac4 100644
--- a/packages/roomkit-react/src/Prebuilt/components/Footer/ChatToggle.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/Footer/ChatToggle.tsx
@@ -9,7 +9,7 @@ import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane
// @ts-ignore: No implicit Any
import { SIDE_PANE_OPTIONS } from '../../common/constants';
-export const ChatToggle = () => {
+export const ChatToggle = ({ onClick }: { onClick?: () => void }) => {
const countUnreadMessages = useHMSStore(selectUnreadHMSMessagesCount);
const isChatOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.CHAT);
const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
@@ -21,7 +21,7 @@ export const ChatToggle = () => {
}}
>
-
+ (onClick ? onClick() : toggleChat())} active={!isChatOpen} data-testid="chat_btn">
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HMSVideo.jsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HMSVideo.jsx
index d022f05a23..a673afef4a 100644
--- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HMSVideo.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HMSVideo.jsx
@@ -9,6 +9,7 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
size: '100%',
position: 'relative',
justifyContent: 'center',
+ transition: 'all 0.3s ease-in-out',
'@md': {
height: 'auto',
'& video': {
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx
index 2c950ab34b..ef29a128ae 100644
--- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx
@@ -9,8 +9,8 @@ export const PlayPauseButton = ({
height = 20,
}: {
isPaused: boolean;
- width: number;
- height: number;
+ width?: number;
+ height?: number;
}) => {
const { hlsPlayer } = useHMSPlayerContext();
const onClick = async (event: MouseEvent) => {
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseSeekControls.tsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseSeekControls.tsx
new file mode 100644
index 0000000000..03267dbaca
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/PlayPauseSeekControls.tsx
@@ -0,0 +1,158 @@
+import React from 'react';
+import { useMedia } from 'react-use';
+import { BackwardArrowIcon, ForwardArrowIcon } from '@100mslive/react-icons';
+import { Box, Flex } from '../../../Layout';
+import { Text } from '../../../Text';
+import { config } from '../../../Theme';
+import { PlayPauseButton } from './PlayPauseButton';
+import { SeekControl } from './SeekControl';
+import { useIsLandscape } from '../../common/hooks';
+
+// desktop buttons
+export const PlayPauseSeekControls = ({
+ isPaused,
+ onSeekTo,
+}: {
+ isPaused: boolean;
+ onSeekTo: (value: number) => void;
+}) => {
+ return (
+ <>
+ {
+ e.stopPropagation();
+ onSeekTo(-10);
+ }}
+ title="backward"
+ >
+
+
+
+ {
+ e.stopPropagation();
+ onSeekTo(10);
+ }}
+ title="forward"
+ >
+
+
+ >
+ );
+};
+
+// overlay handlers
+export const PlayPauseSeekOverlayControls = ({
+ isPaused,
+ showControls,
+ hoverControlsVisible,
+}: {
+ isPaused: boolean;
+ showControls: boolean;
+ hoverControlsVisible: {
+ seekBackward: boolean;
+ seekForward: boolean;
+ pausePlay: boolean;
+ };
+}) => {
+ const isMobile = useMedia(config.media.md);
+ const isLandscape = useIsLandscape();
+
+ if (!isMobile && !isLandscape) {
+ // show desktopOverflow icons
+ return (
+ <>
+
+
+
+
+
+ 10 secs
+
+
+
+
+
+
+
+
+
+
+ 10 secs
+
+
+ >
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControls.tsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControl.tsx
similarity index 91%
rename from packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControls.tsx
rename to packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControl.tsx
index 830ed0a082..20ba919045 100644
--- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControls.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/SeekControl.tsx
@@ -1,7 +1,7 @@
import React, { MouseEventHandler } from 'react';
import { IconButton, Tooltip } from '../../..';
-export const SeekControls = ({
+export const SeekControl = ({
title,
onClick,
children,
@@ -9,7 +9,7 @@ export const SeekControls = ({
}: {
title: string;
onClick?: MouseEventHandler;
- css: any;
+ css?: any;
children: React.ReactNode;
}) => {
return (
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/VideoProgress.tsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/VideoProgress.tsx
index c2e637c10a..42f9eaafc4 100644
--- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/VideoProgress.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/VideoProgress.tsx
@@ -25,8 +25,12 @@ export const VideoProgress = ({
if (videoEl.buffered.length > 0) {
bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), duration));
}
- setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
- setBufferProgress(isNaN(bufferProgress) ? 0 : bufferProgress);
+ if (!isNaN(videoProgress)) {
+ setVideoProgress(videoProgress);
+ }
+ if (!isNaN(bufferProgress)) {
+ setBufferProgress(bufferProgress);
+ }
}, [videoEl]);
const timeupdateHandler = useCallback(() => {
if (!videoEl || seekProgress) {
@@ -42,7 +46,7 @@ export const VideoProgress = ({
return function cleanup() {
videoEl?.removeEventListener('timeupdate', timeupdateHandler);
};
- }, [timeupdateHandler, videoEl]);
+ }, [setProgress, timeupdateHandler, videoEl]);
const onProgress = (progress: number[]) => {
const progress1 = Math.floor(getPercentage(progress[0], 100));
diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/index.ts b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/index.ts
index e85f278b49..2a488a597c 100644
--- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/index.ts
+++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/index.ts
@@ -2,15 +2,13 @@
import { LeftControls, RightControls, VideoControls } from './Controls';
// @ts-ignore
import { HMSVideo } from './HMSVideo';
-import { PlayPauseButton } from './PlayPauseButton';
-import { SeekControls } from './SeekControls';
+import { PlayPauseSeekControls, PlayPauseSeekOverlayControls } from './PlayPauseSeekControls';
import { VideoProgress } from './VideoProgress';
import { VideoTime } from './VideoTime';
import { VolumeControl } from './VolumeControl';
export const HMSVideoPlayer = {
Root: HMSVideo,
- PlayPauseButton: PlayPauseButton,
Progress: VideoProgress,
Duration: VideoTime,
Volume: VolumeControl,
@@ -19,5 +17,8 @@ export const HMSVideoPlayer = {
Left: LeftControls,
Right: RightControls,
},
- Seeker: SeekControls,
+ PlayPauseSeekControls: {
+ Overlay: PlayPauseSeekOverlayControls,
+ Button: PlayPauseSeekControls,
+ },
};
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx b/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
index 2be40db094..d8d2230a75 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
@@ -15,7 +15,7 @@ import {
useHMSStore,
useHMSVanillaStore,
} from '@100mslive/react-sdk';
-import { BackwardArrowIcon, ColoredHandIcon, ForwardArrowIcon, GoLiveIcon } from '@100mslive/react-icons';
+import { ColoredHandIcon, GoLiveIcon } from '@100mslive/react-icons';
import { ChatToggle } from '../components/Footer/ChatToggle';
import { HlsStatsOverlay } from '../components/HlsStatsOverlay';
import { HMSVideoPlayer } from '../components/HMSVideo';
@@ -42,7 +42,7 @@ import { APP_DATA, EMOJI_REACTION_TYPE, POLL_STATE, POLL_VIEWS, SIDE_PANE_OPTION
let hlsPlayer;
const toastMap = {};
-const ToggleChat = () => {
+const ToggleChat = ({ isFullScreen = false }) => {
const { elements } = useRoomLayoutConferencingScreen();
const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane));
const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
@@ -51,7 +51,10 @@ const ToggleChat = () => {
const hmsActions = useHMSActions();
useEffect(() => {
- match({ sidepane, isMobile, showChat })
+ match({ sidepane, isMobile, showChat, isFullScreen })
+ .with({ isFullScreen: true }, () => {
+ hmsActions.setAppData(APP_DATA.sidePane, '');
+ })
.with({ isMobile: true, showChat: true, sidepane: P.when(value => !value) }, () => {
toggleChat();
})
@@ -61,12 +64,13 @@ const ToggleChat = () => {
.otherwise(() => {
//do nothing
});
- }, [sidepane, isMobile, toggleChat, showChat, hmsActions]);
+ }, [sidepane, isMobile, toggleChat, showChat, hmsActions, isFullScreen]);
return null;
};
const HLSView = () => {
const videoRef = useRef(null);
const hlsViewRef = useRef();
+ const { elements } = useRoomLayoutConferencingScreen();
const hlsState = useHMSStore(selectHLSState);
const enablHlsStats = useHMSStore(selectAppData(APP_DATA.hlsStats));
const notification = useHMSNotifications(HMSNotificationTypes.POLL_STOPPED);
@@ -81,7 +85,11 @@ const HLSView = () => {
const [hasCaptions, setHasCaptions] = useState(false);
const [currentSelectedQuality, setCurrentSelectedQuality] = useState(null);
const [isHlsAutoplayBlocked, setIsHlsAutoplayBlocked] = useState(false);
- const [isSeekEnabled, setIsSeekEnabled] = useState(false);
+ const [hoverControlsVisible, setHoverControlsVisible] = useState({
+ seekForward: false,
+ pausePlay: false,
+ seekBackward: false,
+ });
const [isPaused, setIsPaused] = useState(false);
const [show, toggle] = useToggle(false);
const lastHlsUrl = usePrevious(hlsUrl);
@@ -93,6 +101,8 @@ const HLSView = () => {
const controlsTimerRef = useRef();
const [seekProgress, setSeekProgress] = useState(false);
const isFullScreenSupported = screenfull.isEnabled;
+ const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
+ const showChat = !!elements?.chat;
const isMobile = useMedia(config.media.md);
const isLandscape = useIsLandscape();
@@ -233,7 +243,19 @@ const HLSView = () => {
setIsVideoLive(isLive);
};
- const playbackEventHandler = data => setIsPaused(data.state === HLSPlaybackState.paused);
+ const playbackEventHandler = data => {
+ setIsPaused(data.state === HLSPlaybackState.paused);
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ pausePlay: true,
+ });
+ setTimeout(() => {
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ pausePlay: false,
+ });
+ }, 2000);
+ };
const captionEnabledEventHandler = isCaptionEnabled => {
setIsCaptionEnabled(isCaptionEnabled);
};
@@ -314,28 +336,63 @@ const HLSView = () => {
};
}, [controlsVisible, isFullScreen, seekProgress, qualityDropDownOpen]);
- const onSeekTo = useCallback(seek => {
- hlsPlayer?.seekTo(videoRef.current?.currentTime + seek);
- }, []);
+ const onSeekTo = useCallback(
+ seek => {
+ match({ isLandscape, isMobile, seek })
+ .with({ seek: -10, isMobile: false, isLandscape: false }, () => {
+ setHoverControlsVisible({ ...hoverControlsVisible, seekBackward: true });
+ setTimeout(() => {
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ seekBackward: false,
+ });
+ }, 1000);
+ })
+ .with({ seek: 10, isMobile: false, isLandscape: false }, () => {
+ setHoverControlsVisible({ ...hoverControlsVisible, seekForward: true });
+ setTimeout(() => {
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ seekForward: false,
+ });
+ }, 1000);
+ })
+ .otherwise(() => null);
+ hlsPlayer?.seekTo(videoRef.current?.currentTime + seek);
+ },
+ [hoverControlsVisible, isLandscape, isMobile],
+ );
const onDoubleClickHandler = useCallback(
event => {
if (!(isMobile || isLandscape) || hlsState?.variants[0]?.playlist_type !== HLSPlaylistType.DVR) {
return;
}
const sidePercentage = (event.screenX * 100) / event.target.clientWidth;
- setIsSeekEnabled(true);
// there is space for pause/unpause button
if (sidePercentage < 45) {
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ seekBackward: true,
+ });
onSeekTo(-10);
} else {
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ seekForward: true,
+ });
onSeekTo(10);
}
setTimeout(() => {
- setIsSeekEnabled(false);
- }, 200);
+ setHoverControlsVisible({
+ ...hoverControlsVisible,
+ seekForward: false,
+ seekBackward: false,
+ });
+ }, 1000);
},
- [hlsState?.variants, isLandscape, isMobile, onSeekTo],
+ [hlsState?.variants, hoverControlsVisible, isLandscape, isMobile, onSeekTo],
);
+
const onClickHandler = useCallback(async () => {
match({ isMobile, isLandscape, playlist_type: hlsState?.variants[0]?.playlist_type })
.with({ playlist_type: HLSPlaylistType.DVR, isMobile: false, isLandscape: false }, async () => {
@@ -356,6 +413,7 @@ const HLSView = () => {
)
.otherwise(() => null);
}, [hlsState?.variants, isLandscape, isMobile, isPaused]);
+
const onHoverHandler = useCallback(
event => {
event.preventDefault();
@@ -423,6 +481,7 @@ const HLSView = () => {
justify="center"
css={{
flex: isLandscape ? '2 1 0' : '1 1 0',
+ transition: 'all 0.3s ease-in-out',
'&:fullscreen': {
'& video': {
height: 'unset !important',
@@ -484,53 +543,37 @@ const HLSView = () => {
}}
>
<>
+ {!(isMobile || isLandscape) && (
+
+ {!showLoader && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR && (
+
+ )}
+
+ )}
{isMobile || isLandscape ? (
<>
-
- {!showLoader && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR && (
- <>
-
-
-
-
-
-
-
-
-
- >
- )}
-
+ {!showLoader && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR && (
+
+ )}
{
}}
>
- {isLandscape && }
+ {(isLandscape || (isMobile && isFullScreen)) && showChat && (
+ {
+ if (isFullScreen) {
+ toggle();
+ }
+ toggleChat();
+ }}
+ />
+ )}
{hasCaptions && !isHlsAutoplayBlocked && }
{hlsViewRef.current && availableLayers.length > 0 && !isHlsAutoplayBlocked ? (
{
<>
{hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR ? (
<>
- {
- onSeekTo(-10);
- }}
- title="backward"
- >
-
-
-
- {
- onSeekTo(10);
- }}
- title="forward"
- >
-
-
+
{!isVideoLive ? : null}
>
) : null}
@@ -631,7 +667,8 @@ const HLSView = () => {
)}
{
+ onClick={async e => {
+ e.stopPropagation();
await hlsPlayer?.seekToLivePosition();
setIsVideoLive(true);
}}
@@ -690,7 +727,7 @@ const HLSView = () => {
-
+
{isMobile && !isFullScreen && }
);