diff --git a/examples/prebuilt-react-integration/vite.config.js b/examples/prebuilt-react-integration/vite.config.js index 8c3199c2b7..14d7d98e40 100644 --- a/examples/prebuilt-react-integration/vite.config.js +++ b/examples/prebuilt-react-integration/vite.config.js @@ -6,5 +6,5 @@ export default defineConfig({ plugins: [react()], define: { 'process.env': {}, - } + }, }); diff --git a/packages/hms-video-store/src/index.ts b/packages/hms-video-store/src/index.ts index 5fafe2bfd9..dfc3890b9f 100644 --- a/packages/hms-video-store/src/index.ts +++ b/packages/hms-video-store/src/index.ts @@ -54,7 +54,7 @@ export type { } from './internal'; export { HMSReactiveStore } from './reactive-store/HMSReactiveStore'; -export { HMSPluginUnsupportedTypes, HMSRecordingState } from './internal'; +export { HMSPluginUnsupportedTypes, HMSRecordingState, HLSPlaylistType } from './internal'; export type { HMSVideoPlugin, HMSAudioPlugin, diff --git a/packages/hms-video-store/src/interfaces/room.ts b/packages/hms-video-store/src/interfaces/room.ts index 4a7cad0c5b..63d9725463 100644 --- a/packages/hms-video-store/src/interfaces/room.ts +++ b/packages/hms-video-store/src/interfaces/room.ts @@ -97,8 +97,14 @@ export interface HMSHLS { error?: HMSException; } +export enum HLSPlaylistType { + DVR = 'dvr', + NO_DVR = 'no-dvr', +} + export interface HLSVariant { url: string; + playlist_type?: HLSPlaylistType; meetingURL?: string; metadata?: string; startedAt?: Date; diff --git a/packages/hms-video-store/src/notification-manager/HMSNotifications.ts b/packages/hms-video-store/src/notification-manager/HMSNotifications.ts index 1e5959ef70..d6487f9c78 100644 --- a/packages/hms-video-store/src/notification-manager/HMSNotifications.ts +++ b/packages/hms-video-store/src/notification-manager/HMSNotifications.ts @@ -279,9 +279,15 @@ export interface HLSNotification { hls_recording?: HLSRecording; } +export enum HLSPlaylistType { + DVR = 'dvr', + NO_DVR = 'no-dvr', +} + export interface HLSVariantInfo { url: string; meeting_url?: string; + playlist_type?: HLSPlaylistType; metadata?: string; started_at?: number; initialised_at?: number; diff --git a/packages/hms-video-store/src/notification-manager/managers/RoomUpdateManager.ts b/packages/hms-video-store/src/notification-manager/managers/RoomUpdateManager.ts index 90a66cf6e1..d367681eac 100644 --- a/packages/hms-video-store/src/notification-manager/managers/RoomUpdateManager.ts +++ b/packages/hms-video-store/src/notification-manager/managers/RoomUpdateManager.ts @@ -191,6 +191,7 @@ export class RoomUpdateManager { meetingURL: variant?.meeting_url, url: variant?.url, metadata: variant?.metadata, + playlist_type: variant?.playlist_type, startedAt: convertDateNumToDate(variant?.started_at), initialisedAt: convertDateNumToDate(variant?.initialised_at), state: variant.state, diff --git a/packages/react-icons/assets/PauseIcon.svg b/packages/react-icons/assets/PauseIcon.svg index 84b0048c50..fae40aa104 100644 --- a/packages/react-icons/assets/PauseIcon.svg +++ b/packages/react-icons/assets/PauseIcon.svg @@ -1,4 +1,4 @@ - - - - \ No newline at end of file + + + + diff --git a/packages/react-icons/assets/PlayIcon.svg b/packages/react-icons/assets/PlayIcon.svg index e88494cf05..769bb52580 100644 --- a/packages/react-icons/assets/PlayIcon.svg +++ b/packages/react-icons/assets/PlayIcon.svg @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + diff --git a/packages/react-icons/src/PauseIcon.tsx b/packages/react-icons/src/PauseIcon.tsx index a412acf80b..023d9ec4c2 100644 --- a/packages/react-icons/src/PauseIcon.tsx +++ b/packages/react-icons/src/PauseIcon.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { SVGProps } from 'react'; const SvgPauseIcon = (props: SVGProps) => ( - - + + ); export default SvgPauseIcon; diff --git a/packages/react-icons/src/PlayIcon.tsx b/packages/react-icons/src/PlayIcon.tsx index cf5ed72e0d..57776928b4 100644 --- a/packages/react-icons/src/PlayIcon.tsx +++ b/packages/react-icons/src/PlayIcon.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { SVGProps } from 'react'; const SvgPlayIcon = (props: SVGProps) => ( - + diff --git a/packages/roomkit-react/src/Prebuilt/common/hooks.ts b/packages/roomkit-react/src/Prebuilt/common/hooks.ts index 4b68ba15bc..5f2690cb1a 100644 --- a/packages/roomkit-react/src/Prebuilt/common/hooks.ts +++ b/packages/roomkit-react/src/Prebuilt/common/hooks.ts @@ -1,6 +1,8 @@ import { useEffect, useRef, useState } from 'react'; +import { useMedia } from 'react-use'; import { JoinForm_JoinBtnType } from '@100mslive/types-prebuilt/elements/join_form'; import { + parsedUserAgent, selectAvailableRoleNames, selectIsConnectedToRoom, selectPeerCount, @@ -10,6 +12,7 @@ import { useHMSStore, useHMSVanillaStore, } from '@100mslive/react-sdk'; +import { config } from '../../Theme'; import { useRoomLayout } from '../provider/roomLayoutProvider'; import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; import { CHAT_SELECTOR } from './constants'; @@ -100,3 +103,21 @@ export const useParticipants = (params?: { metadata?: { isHandRaised?: boolean } } return { participants: participantList, isConnected, peerCount, rolesWithParticipants }; }; + +export const useIsLandscape = () => { + const isMobile = parsedUserAgent.getDevice().type === 'mobile'; + const isLandscape = useMedia(config.media.ls); + return isMobile && isLandscape; +}; + +export const useLandscapeHLSStream = () => { + const isLandscape = useIsLandscape(); + const { screenType } = useRoomLayoutConferencingScreen(); + return isLandscape && screenType === 'hls_live_streaming'; +}; + +export const useMobileHLSStream = () => { + const isMobile = useMedia(config.media.md); + const { screenType } = useRoomLayoutConferencingScreen(); + return isMobile && screenType === 'hls_live_streaming'; +}; diff --git a/packages/roomkit-react/src/Prebuilt/components/Chat/ChatFooter.tsx b/packages/roomkit-react/src/Prebuilt/components/Chat/ChatFooter.tsx index 93c4b20fd3..f82743285b 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Chat/ChatFooter.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Chat/ChatFooter.tsx @@ -2,10 +2,12 @@ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'reac import { useMedia } from 'react-use'; import data from '@emoji-mart/data'; import Picker from '@emoji-mart/react'; -import { HMSException, selectLocalPeer, useHMSActions, useHMSStore } from '@100mslive/react-sdk'; +import { HMSException, selectLocalPeer, useAVToggle, useHMSActions, useHMSStore } from '@100mslive/react-sdk'; import { EmojiIcon, PauseCircleIcon, SendIcon, VerticalMenuIcon } from '@100mslive/react-icons'; import { Box, config as cssConfig, Flex, IconButton as BaseIconButton, Popover, styled, Text } from '../../..'; import { IconButton } from '../../../IconButton'; +import { MoreSettings } from '../MoreSettings/MoreSettings'; +import { RaiseHand } from '../RaiseHand'; // @ts-ignore import { ToastManager } from '../Toast/ToastManager'; import { ChatSelectorContainer } from './ChatSelectorContainer'; @@ -17,7 +19,7 @@ import { useSetSubscribedChatSelector, useSubscribeChatSelector } from '../AppDa import { useIsPeerBlacklisted } from '../hooks/useChatBlacklist'; // @ts-ignore import { useEmojiPickerStyles } from './useEmojiPickerStyles'; -import { useDefaultChatSelection } from '../../common/hooks'; +import { useDefaultChatSelection, useLandscapeHLSStream, useMobileHLSStream } from '../../common/hooks'; import { CHAT_SELECTOR, SESSION_STORE_KEY } from '../../common/constants'; const TextArea = styled('textarea', { @@ -77,7 +79,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo const inputRef = useRef(null); const [draftMessage, setDraftMessage] = useChatDraftMessage(); const isMobile = useMedia(cssConfig.media.md); - const { elements } = useRoomLayoutConferencingScreen(); + const { elements, screenType } = useRoomLayoutConferencingScreen(); const message_placeholder = elements?.chat?.message_placeholder || 'Send a message'; const localPeer = useHMSStore(selectLocalPeer); const isOverlayChat = elements?.chat?.is_overlay; @@ -87,18 +89,21 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo const defaultSelection = useDefaultChatSelection(); const selection = selectedPeer.name || selectedRole || defaultSelection; const isLocalPeerBlacklisted = useIsPeerBlacklisted({ local: true }); + const { toggleAudio, toggleVideo } = useAVToggle(); + const noAVPermissions = !(toggleAudio || toggleVideo); + const isMwebHLSStream = useMobileHLSStream(); + const isLandscapeHLSStream = useLandscapeHLSStream(); useEffect(() => { if (!selectedPeer.id && !selectedRole && !['Everyone', ''].includes(defaultSelection)) { setRoleSelector(defaultSelection); } else { // @ts-ignore - if (!elements?.chat?.disable_autofocus) { + if (!(isMobile || isLandscapeHLSStream) || !elements?.chat?.disable_autofocus) { inputRef.current?.focus(); } } - // @ts-ignore - }, [defaultSelection, elements?.chat?.disable_autofocus, selectedPeer, selectedRole, setRoleSelector]); + }, [defaultSelection, selectedPeer, selectedRole, setRoleSelector, isMobile, isLandscapeHLSStream, elements?.chat]); const sendMessage = useCallback(async () => { const message = inputRef?.current?.value; if (!message || !message.trim().length) { @@ -197,18 +202,17 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo align="center" css={{ bg: isOverlayChat && isMobile ? '$surface_dim' : '$surface_default', - minHeight: '$16', maxHeight: '$24', position: 'relative', - py: '$6', pl: '$8', - flexGrow: 1, + flexGrow: '1', r: '$1', '@md': { minHeight: 'unset', h: '$14', boxSizing: 'border-box', }, + ...(isLandscapeHLSStream ? { minHeight: '$14', py: 0 } : {}), }} > {children} @@ -223,6 +227,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo placeholder={message_placeholder} ref={inputRef} required + autoFocus={!(isMobile || isLandscapeHLSStream)} onKeyPress={async event => { if (event.key === 'Enter') { if (!event.shiftKey) { @@ -237,7 +242,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo onCut={e => e.stopPropagation()} onCopy={e => e.stopPropagation()} /> - {!isMobile ? ( + {!isMobile && !isLandscapeHLSStream ? ( { if (inputRef.current) { @@ -260,6 +265,17 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo + {(isMwebHLSStream || isLandscapeHLSStream) && ( + + {noAVPermissions ? : null} + + + )} )} diff --git a/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx b/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx index 5c93344d99..19b4b75bdd 100644 --- a/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/ConferenceScreen.tsx @@ -6,10 +6,13 @@ import { selectAppData, selectIsConnectedToRoom, selectRoomState, + useAVToggle, useHMSActions, useHMSStore, } from '@100mslive/react-sdk'; import { Footer } from './Footer/Footer'; +import { LeaveRoom } from './Leave/LeaveRoom'; +import { MoreSettings } from './MoreSettings/MoreSettings'; import { HLSFailureModal } from './Notifications/HLSFailureModal'; // @ts-ignore: No implicit Any import { ActivatedPIP } from './PIP/PIPComponent'; @@ -23,12 +26,14 @@ import { VideoStreamingSection } from '../layouts/VideoStreamingSection'; import FullPageProgress from './FullPageProgress'; import { Header } from './Header'; import { PreviousRoleInMetadata } from './PreviousRoleInMetadata'; +import { RaiseHand } from './RaiseHand'; import { useRoomLayoutConferencingScreen, useRoomLayoutPreviewScreen, } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; // @ts-ignore: No implicit Any import { useAuthToken, useSetAppDataByKey } from './AppData/useUISettings'; +import { useLandscapeHLSStream, useMobileHLSStream } from '../common/hooks'; // @ts-ignore: No implicit Any import { APP_DATA, isAndroid, isIOS, isIPadOS } from '../common/constants'; @@ -47,12 +52,20 @@ export const ConferenceScreen = () => { const isMobileDevice = isAndroid || isIOS || isIPadOS; const dropdownListRef = useRef(); const [isHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted); + + const { toggleAudio, toggleVideo } = useAVToggle(); + const noAVPermissions = !(toggleAudio || toggleVideo); + // using it in hls stream to show action button when chat is disabled + const showChat = !!screenProps.elements?.chat; const toggleControls = () => { if (dropdownListRef.current?.length === 0 && isMobileDevice) { setHideControls(value => !value); } }; const autoRoomJoined = useRef(isPreviewScreenEnabled); + const isMobileHLSStream = useMobileHLSStream(); + const isLandscapeHLSStream = useLandscapeHLSStream(); + const isMwebHLSStream = isMobileHLSStream || isLandscapeHLSStream; useEffect(() => { let timeout: undefined | ReturnType; @@ -113,7 +126,7 @@ export const ConferenceScreen = () => { ) : null} - {!screenProps.hideSections.includes('header') && ( + {!(screenProps.hideSections.includes('header') || isMwebHLSStream) && ( {
)} + {isMwebHLSStream && ( + + + + )} { /> ) : null} - {!screenProps.hideSections.includes('footer') && screenProps.elements && ( + {!screenProps.hideSections.includes('footer') && screenProps.elements && !isMwebHLSStream && ( {
)} + {isMwebHLSStream && !showChat && ( + + {noAVPermissions ? : null} + + + )} diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx index 0e51746e43..bb0f5a7719 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Footer/Footer.tsx @@ -12,7 +12,6 @@ import { EmojiReaction } from '../EmojiReaction'; import { LeaveRoom } from '../Leave/LeaveRoom'; // @ts-ignore: No implicit Any import { MoreSettings } from '../MoreSettings/MoreSettings'; -// @ts-ignore: No implicit Any import { RaiseHand } from '../RaiseHand'; // @ts-ignore: No implicit Any import { ScreenshareToggle } from '../ScreenShareToggle'; diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/Controls.jsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/Controls.jsx index d6ff7a9752..9e1b57e335 100644 --- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/Controls.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/Controls.jsx @@ -1,4 +1,4 @@ -import { Flex, styled } from '../../../'; +import { Flex, styled } from '../../..'; export const VideoControls = styled(Flex, { justifyContent: 'center', diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.jsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.jsx deleted file mode 100644 index 6fc0d40344..0000000000 --- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import { Flex, IconButton, Tooltip } from '../../../'; - -export const FullScreenButton = ({ isFullScreen, icon, onToggle }) => { - return ( - - - {icon} - - - ); -}; diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.tsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.tsx new file mode 100644 index 0000000000..ce4a5f4114 --- /dev/null +++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/FullscreenButton.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { ExpandIcon, ShrinkIcon } from '@100mslive/react-icons'; +import { Flex, IconButton, Tooltip } from '../../..'; + +export const FullScreenButton = ({ isFullScreen, onToggle }: { isFullScreen: boolean; onToggle: () => void }) => { + return ( + + + {isFullScreen ? : } + + + ); +}; diff --git a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.jsx b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx similarity index 66% rename from packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.jsx rename to packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx index 7d30488671..584e9bf294 100644 --- a/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/HMSVideo/HLSAutoplayBlockedPrompt.tsx @@ -1,14 +1,21 @@ import React from 'react'; -import { Button, Dialog, Text } from '../../../'; +import { Button, Dialog, Text } from '../../..'; +// @ts-ignore import { DialogContent, DialogRow } from '../../primitives/DialogContent'; -export function HLSAutoplayBlockedPrompt({ open, unblockAutoPlay }) { +export function HLSAutoplayBlockedPrompt({ + open, + unblockAutoPlay, +}: { + open: boolean; + unblockAutoPlay: () => Promise; +}) { return ( { + onOpenChange={async value => { if (!value) { - unblockAutoPlay(); + await unblockAutoPlay(); } }} > @@ -22,8 +29,8 @@ export function HLSAutoplayBlockedPrompt({ open, unblockAutoPlay }) {
Participants  @@ -122,7 +132,12 @@ export const SidePaneTabs = React.memo<{ {showChatSettings ? : null} {isOverlayChat && isChatOpen ? null : ( { e.stopPropagation(); if (activeTab === SIDE_PANE_OPTIONS.CHAT) { @@ -133,12 +148,16 @@ export const SidePaneTabs = React.memo<{ }} data-testid="close_chat" > - + {screenType === 'hls_live_streaming' && isChatOpen ? null : } )} - {showChat ? : } + {activeTab === SIDE_PANE_OPTIONS.CHAT ? ( + + ) : ( + + )} ) : ( { const hlsViewRef = useRef(null); const hlsState = useHMSStore(selectHLSState); const enablHlsStats = useHMSStore(selectAppData(APP_DATA.hlsStats)); + const { elements, screenType } = useRoomLayoutConferencingScreen(); const notification = useHMSNotifications(HMSNotificationTypes.POLL_STOPPED); const hmsActions = useHMSActions(); - const { themeType, theme } = useTheme(); + const { themeType } = useTheme(); const [streamEnded, setStreamEnded] = useState(false); let [hlsStatsState, setHlsStatsState] = useState(null); const hlsUrl = hlsState.variants[0]?.url; const [availableLayers, setAvailableLayers] = useState([]); const [isVideoLive, setIsVideoLive] = useState(true); - const [isUserSelectedAuto, setIsUserSelectedAuto] = useState(true); const [isCaptionEnabled, setIsCaptionEnabled] = useState(true); const [hasCaptions, setHasCaptions] = useState(false); const [currentSelectedQuality, setCurrentSelectedQuality] = useState(null); const [isHlsAutoplayBlocked, setIsHlsAutoplayBlocked] = useState(false); const [isPaused, setIsPaused] = useState(false); - const isFullScreenSupported = screenfull.isEnabled; const [show, toggle] = useToggle(false); - const [controlsVisible, setControlsVisible] = useState(true); - const controlsRef = useRef(); - const controlsTimerRef = useRef(); - const [qualityDropDownOpen, setQualityDropDownOpen] = useState(false); const lastHlsUrl = usePrevious(hlsUrl); const togglePollView = usePollViewToggle(); const vanillaStore = useHMSVanillaStore(); + const [controlsVisible, setControlsVisible] = useState(true); + const [isUserSelectedAuto, setIsUserSelectedAuto] = useState(true); + const [qualityDropDownOpen, setQualityDropDownOpen] = useState(false); + const controlsRef = useRef(null); + const controlsTimerRef = useRef(); + const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane)); + const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT); + const showChat = !!elements?.chat; + const isFullScreenSupported = screenfull.isEnabled; const isMobile = useMedia(config.media.md); + const isLandscape = useIsLandscape(); + const isFullScreen = useFullscreen(hlsViewRef, show, { onClose: () => toggle(false), }); const [showLoader, setShowLoader] = useState(false); + + const isMwebHLSStream = screenType === 'hls_live_streaming' && isMobile; + + useEffect(() => { + if (sidepane === '' && isMwebHLSStream && showChat) { + toggleChat(); + } + }, [sidepane, isMwebHLSStream, toggleChat, showChat]); // FIXME: move this logic to player controller in next release useEffect(() => { /** @@ -111,6 +131,15 @@ const HLSView = () => { }; }, [hlsUrl]); + const handleQuality = useCallback( + quality => { + if (hlsPlayer) { + setIsUserSelectedAuto(quality.height?.toString().toLowerCase() === 'auto'); + hlsPlayer?.setLayer(quality); + } + }, + [availableLayers], //eslint-disable-line + ); /** * initialize HMSHLSPlayer and add event listeners. */ @@ -210,10 +239,9 @@ const HLSView = () => { hlsPlayer.off(HMSHLSPlayerEvents.MANIFEST_LOADED, manifestLoadedHandler); hlsPlayer.off(HMSHLSPlayerEvents.LAYER_UPDATED, layerUpdatedHandler); hlsPlayer.reset(); - hlsPlayer = null; }; } - }, [hlsUrl]); + }, [hlsUrl, togglePollView, vanillaStore]); /** * initialize and subscribe to hlsState @@ -239,16 +267,6 @@ const HLSView = () => { } }; - const handleQuality = useCallback( - quality => { - if (hlsPlayer) { - setIsUserSelectedAuto(quality.height.toString().toLowerCase() === 'auto'); - hlsPlayer.setLayer(quality); - } - }, - [availableLayers], //eslint-disable-line - ); - const sfnOverlayClose = () => { hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats); }; @@ -272,8 +290,31 @@ const HLSView = () => { }; }, [controlsVisible, isFullScreen, qualityDropDownOpen]); + const onTouchHandler = useCallback( + event => { + event.preventDefault(); + // logic for invisible when tapping + if (event.type === 'ontouchstart' && controlsVisible) { + setControlsVisible(false); + return; + } + // normal scemnario + if (event.type === 'ontouchstart' || qualityDropDownOpen) { + setControlsVisible(true); + return; + } + if (isFullScreen && !controlsVisible && event.type === 'touchmove') { + setControlsVisible(true); + if (controlsTimerRef.current) { + clearTimeout(controlsTimerRef.current); + } + } + }, + [controlsVisible, isFullScreen, qualityDropDownOpen], + ); const onHoverHandler = useCallback( event => { + // normal scemnario if (event.type === 'mouseenter' || qualityDropDownOpen) { setControlsVisible(true); return; @@ -295,157 +336,232 @@ const HLSView = () => { key="hls-viewer" id={`hls-viewer-${themeType}`} ref={hlsViewRef} + direction={isMobile || isLandscape ? 'column' : 'row'} css={{ - size: '100%', + w: sidepane !== '' && isLandscape ? '55%' : '100%', + h: sidepane !== '' && isMobile ? '36%' : '100%', }} > - {hlsStatsState?.url && enablHlsStats ? ( - - ) : null} {hlsUrl && !streamEnded ? ( - - - {showLoader && ( + <> + + {hlsStatsState?.url && enablHlsStats && !(isMobile || isLandscape) ? ( + + ) : null} - - - )} - - {isMobile && isPaused && ( - - await hlsPlayer?.play()} data-testid="play_btn"> - - - - )} - - {!isMobile && ( - + {showLoader && ( + - - { - isPaused ? await hlsPlayer?.play() : hlsPlayer?.pause(); - }} - isPaused={isPaused} - /> - - - { - await hlsPlayer.seekToLivePosition(); - setIsVideoLive(true); + + + )} + + <> + {isMobile || isLandscape ? ( + <> + {!showLoader && ( + + {isPaused ? ( + { + await hlsPlayer?.play(); + }} + data-testid="play_btn" + > + + + ) : ( + { + await hlsPlayer?.pause(); + }} + data-testid="pause_btn" + > + + + )} + + )} + + + + {isLandscape && } + {hasCaptions && } + {availableLayers.length > 0 ? ( + + ) : null} + + + + + ) : null} + + {!(isMobile || isLandscape) && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR && ( + + )} + - - - - - {isVideoLive ? 'LIVE' : 'GO LIVE'} - - - - - + + {!(isMobile || isLandscape) && ( + <> + { + isPaused ? await hlsPlayer?.play() : hlsPlayer?.pause(); + }} + isPaused={isPaused} + /> + {!isVideoLive ? : null} + + + )} + { + await hlsPlayer?.seekToLivePosition(); + setIsVideoLive(true); + }} + key="jump-to-live_btn" + data-testid="jump-to-live_btn" + > + + + + + {isVideoLive ? 'LIVE' : 'GO LIVE'} + + + + + {(isMobile || isLandscape) && !isVideoLive ? : null} + - - {hasCaptions && ( - hlsPlayer?.toggleCaption()} isEnabled={isCaptionEnabled} /> - )} - {availableLayers.length > 0 ? ( - - ) : null} - {isFullScreenSupported ? ( - : } - /> + + {hasCaptions && !(isMobile || isLandscape) && ( + + )} + {availableLayers.length > 0 && !(isMobile || isLandscape) ? ( + + ) : null} + {isFullScreenSupported ? ( + + ) : null} + + + {(isMobile || isLandscape) && hlsState?.variants[0]?.playlist_type === HLSPlaylistType.DVR ? ( + ) : null} - - - )} + + + - - + + + {isMobile && !isFullScreen && } + ) : ( diff --git a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx index 04c4c0b74e..7288852441 100644 --- a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx +++ b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx @@ -20,6 +20,7 @@ import { useRoomLayoutConferencingScreen, useRoomLayoutPreviewScreen, } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen'; +import { useIsLandscape, useLandscapeHLSStream, useMobileHLSStream } from '../common/hooks'; import { translateAcross } from '../../utils'; import { APP_DATA, SIDE_PANE_OPTIONS, UI_SETTINGS } from '../common/constants'; @@ -31,6 +32,7 @@ const SidePane = ({ hideControls?: boolean; }) => { const isMobile = useMedia(cssConfig.media.md); + const isLandscape = useIsLandscape(); const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane)); const activeScreensharePeerId = useHMSStore(selectAppData(APP_DATA.activeScreensharePeerId)); const trackId = useHMSStore(selectVideoTrackByPeerID(activeScreensharePeerId))?.id; @@ -38,6 +40,9 @@ const SidePane = ({ const { elements: preview_elements } = useRoomLayoutPreviewScreen(); const layoutMode = useUISettings(UI_SETTINGS.layoutMode); + const isLandscapeHLSStream = useLandscapeHLSStream(); + const isMobileHLSStream = useMobileHLSStream(); + const backgroundMedia = preview_elements?.virtual_background?.background_media?.length ? preview_elements?.virtual_background?.background_media : elements?.virtual_background?.background_media || []; @@ -48,7 +53,9 @@ const SidePane = ({ ViewComponent = ; } if (sidepane === SIDE_PANE_OPTIONS.PARTICIPANTS || sidepane === SIDE_PANE_OPTIONS.CHAT) { - ViewComponent = ; + ViewComponent = ( + + ); } if (sidepane === SIDE_PANE_OPTIONS.VB) { ViewComponent = ; @@ -82,11 +89,11 @@ const SidePane = ({ justify="center" css={{ w: '$100', - h: '100%', + h: isMobileHLSStream ? '64%' : '100%', flexShrink: 0, gap: '$4', position: 'relative', - '@md': { position: mwebStreamingChat ? 'absolute' : '', zIndex: 12 }, + '@md': { position: mwebStreamingChat || isLandscape ? 'absolute' : '', zIndex: 12 }, }} > {trackId && layoutMode === LayoutMode.GALLERY && ( @@ -103,14 +110,15 @@ const SidePane = ({ { @@ -104,9 +106,17 @@ export const VideoStreamingSection = ({ position: 'relative', gap: '$4', }} + direction={isMobileHLSStream ? 'column' : 'row'} > {ViewComponent} - +