Skip to content

Commit

Permalink
feat: redesign hls player ui for mweb
Browse files Browse the repository at this point in the history
  • Loading branch information
amar-1995 authored Feb 27, 2024
1 parent 23d594d commit b5ea0d8
Show file tree
Hide file tree
Showing 42 changed files with 1,072 additions and 528 deletions.
2 changes: 1 addition & 1 deletion examples/prebuilt-react-integration/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export default defineConfig({
plugins: [react()],
define: {
'process.env': {},
}
},
});
2 changes: 1 addition & 1 deletion packages/hms-video-store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions packages/hms-video-store/src/interfaces/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions packages/react-icons/assets/PauseIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions packages/react-icons/assets/PlayIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions packages/react-icons/src/PauseIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';
import { SVGProps } from 'react';
const SvgPauseIcon = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 9 12" fill="none" {...props}>
<path d="M6.254.167h2.503V11.85H6.254V.167ZM.833.167h2.504V11.85H.833V.167Z" fill="currentColor" />
<svg width="24px" height="24px" viewBox="0 0 49 48" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M27.509 10h6.008v28.04H27.51V10ZM14.5 10h6.009v28.04H14.5V10Z" fill="currentColor" />
</svg>
);
export default SvgPauseIcon;
4 changes: 2 additions & 2 deletions packages/react-icons/src/PlayIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from 'react';
import { SVGProps } from 'react';
const SvgPlayIcon = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 20 20" fill="none" {...props}>
<svg width="24px" height="24px" viewBox="0 0 49 48" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="M14.1 8.84 8.09 4.445a1.47 1.47 0 0 0-.706-.27 1.532 1.532 0 0 0-.758.109 1.343 1.343 0 0 0-.576.451c-.14.196-.215.424-.216.656v9.235c.001.232.076.46.216.655.14.196.34.353.576.452.236.099.499.136.758.107a1.47 1.47 0 0 0 .705-.268l6.012-4.395c.194-.142.35-.32.457-.521.107-.202.163-.422.163-.645 0-.223-.056-.443-.163-.645a1.547 1.547 0 0 0-.457-.52V8.84Z"
d="M34.34 21.217 19.913 10.67a3.53 3.53 0 0 0-1.693-.647 3.677 3.677 0 0 0-1.82.258 3.223 3.223 0 0 0-1.382 1.084 2.718 2.718 0 0 0-.518 1.574v22.164c.002.557.182 1.102.518 1.573.337.47.816.845 1.382 1.083a3.678 3.678 0 0 0 1.818.259 3.53 3.53 0 0 0 1.693-.646l14.43-10.547c.464-.34.838-.767 1.096-1.25.257-.484.39-1.013.39-1.548 0-.535-.133-1.064-.39-1.548a3.714 3.714 0 0 0-1.097-1.25v-.012Z"
fill="currentColor"
/>
</svg>
Expand Down
21 changes: 21 additions & 0 deletions packages/roomkit-react/src/Prebuilt/common/hooks.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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';
Expand Down Expand Up @@ -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';
};
36 changes: 26 additions & 10 deletions packages/roomkit-react/src/Prebuilt/components/Chat/ChatFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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', {
Expand Down Expand Up @@ -77,7 +79,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
const inputRef = useRef<HTMLTextAreaElement>(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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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}
Expand All @@ -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) {
Expand All @@ -237,7 +242,7 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
onCut={e => e.stopPropagation()}
onCopy={e => e.stopPropagation()}
/>
{!isMobile ? (
{!isMobile && !isLandscapeHLSStream ? (
<EmojiPicker
onSelect={emoji => {
if (inputRef.current) {
Expand All @@ -260,6 +265,17 @@ export const ChatFooter = ({ onSend, children }: { onSend: (count: number) => vo
<SendIcon />
</BaseIconButton>
</Flex>
{(isMwebHLSStream || isLandscapeHLSStream) && (
<Flex
css={{
alignItems: 'center',
}}
gap="1"
>
{noAVPermissions ? <RaiseHand /> : null}
<MoreSettings elements={elements} screenType={screenType} />
</Flex>
)}
</Flex>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

Expand All @@ -47,12 +52,20 @@ export const ConferenceScreen = () => {
const isMobileDevice = isAndroid || isIOS || isIPadOS;
const dropdownListRef = useRef<string[]>();
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<typeof setTimeout>;
Expand Down Expand Up @@ -113,7 +126,7 @@ export const ConferenceScreen = () => {
</Box>
) : null}
<Flex css={{ size: '100%', overflow: 'hidden' }} direction="column">
{!screenProps.hideSections.includes('header') && (
{!(screenProps.hideSections.includes('header') || isMwebHLSStream) && (
<Box
ref={headerRef}
css={{
Expand All @@ -129,6 +142,11 @@ export const ConferenceScreen = () => {
<Header />
</Box>
)}
{isMwebHLSStream && (
<Flex align="center" gap="2" css={{ position: 'absolute', left: '$4', top: '$4', zIndex: 1 }}>
<LeaveRoom screenType={screenProps.screenType} />
</Flex>
)}
<Box
css={{
w: '100%',
Expand All @@ -155,7 +173,7 @@ export const ConferenceScreen = () => {
/>
) : null}
</Box>
{!screenProps.hideSections.includes('footer') && screenProps.elements && (
{!screenProps.hideSections.includes('footer') && screenProps.elements && !isMwebHLSStream && (
<Box
ref={footerRef}
css={{
Expand All @@ -174,6 +192,20 @@ export const ConferenceScreen = () => {
<Footer elements={screenProps.elements} screenType={screenProps.screenType} />
</Box>
)}
{isMwebHLSStream && !showChat && (
<Flex
css={{
alignItems: 'center',
pr: '$4',
pb: '$4',
}}
justify="end"
gap="1"
>
{noAVPermissions ? <RaiseHand /> : null}
<MoreSettings elements={screenProps.elements} screenType={screenProps.screenType} />
</Flex>
)}
<RoleChangeRequestModal />
<HLSFailureModal />
<ActivatedPIP />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Flex, styled } from '../../../';
import { Flex, styled } from '../../..';

export const VideoControls = styled(Flex, {
justifyContent: 'center',
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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 (
<Tooltip title={`${isFullScreen ? 'Exit' : 'Go'} fullscreen`} side="top">
<IconButton css={{ margin: '0px' }} onClick={onToggle} key="fullscreen_btn" data-testid="fullscreen_btn">
<Flex>{isFullScreen ? <ShrinkIcon /> : <ExpandIcon />}</Flex>
</IconButton>
</Tooltip>
);
};
Loading

0 comments on commit b5ea0d8

Please sign in to comment.