diff --git a/apps/100ms-custom-app/package.json b/apps/100ms-custom-app/package.json
index 6a6465ad34..837d8d91f7 100644
--- a/apps/100ms-custom-app/package.json
+++ b/apps/100ms-custom-app/package.json
@@ -3,13 +3,14 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@100mslive/react-icons": "0.8.15-alpha.0",
- "@100mslive/roomkit-react": "0.1.6-alpha.0",
+ "@100mslive/react-icons": "0.8.15-alpha.2",
+ "@100mslive/roomkit-react": "0.1.6-alpha.2",
"axios": "^0.21.1",
"js-cookies": "^1.0.4",
"lodash.merge": "^4.6.2",
"react": "^18.1.0",
"react-dom": "^18.1.0",
+ "react-use": "^17.4.0",
"web-vitals": "^1.0.1",
"zipyai": "^1.3.24"
},
diff --git a/apps/100ms-custom-app/public/.well-known/apple-app-site-association b/apps/100ms-custom-app/public/.well-known/apple-app-site-association
index 24e7776635..e9b9d40ab0 100644
--- a/apps/100ms-custom-app/public/.well-known/apple-app-site-association
+++ b/apps/100ms-custom-app/public/.well-known/apple-app-site-association
@@ -1,21 +1,42 @@
{
"applinks": {
- "apps": [
-
- ],
+ "apps": [],
"details": [
- {
+ {
"appID": "5N85PP82A9.live.100ms.videoapp",
"paths": [
- "*/meeting/*","*/preview/*"
+ "*/meeting/*",
+ "*/preview/*"
]
},
{
"appID": "5N85PP82A9.com.brytecam.app",
"paths": [
- "*/meeting/*","*/preview/*"
+ "*/meeting/*",
+ "*/preview/*"
+ ]
+ },
+ {
+ "appID": "5N85PP82A9.live.100ms.reactnative",
+ "paths": [
+ "*/meeting/*",
+ "*/preview/*"
+ ]
+ },
+ {
+ "appID": "5N85PP82A9.live.100ms.flutter",
+ "paths": [
+ "*/meeting/*",
+ "*/preview/*"
+ ]
+ },
+ {
+ "appID": "5N85PP82A9.live.100ms.videoapp.roomkit",
+ "paths": [
+ "*/meeting/*",
+ "*/preview/*"
]
}
]
}
-}
\ No newline at end of file
+}
diff --git a/apps/100ms-custom-app/src/App.jsx b/apps/100ms-custom-app/src/App.jsx
index 4e9ccb3bcd..42fd489094 100644
--- a/apps/100ms-custom-app/src/App.jsx
+++ b/apps/100ms-custom-app/src/App.jsx
@@ -1,20 +1,44 @@
-import React, { useEffect, useState } from 'react';
+import React, { Suspense, useEffect, useRef, useState } from 'react';
import { Flex, HMSPrebuilt } from '@100mslive/roomkit-react';
import { useOverridePrebuiltLayout } from './hooks/useOverridePrebuiltLayout';
import { useSearchParam } from './hooks/useSearchParam';
import {
+ fetchData,
getAuthTokenUsingRoomIdRole,
getRoomCodeFromUrl,
getRoomIdRoleFromUrl,
} from './utils/utils';
+const Header = React.lazy(() => import('./components/Header'));
+
const App = () => {
const roomCode = getRoomCodeFromUrl();
+ const [onlyEmail, setOnlyEmail] = useState(false);
const [authToken, setAuthToken] = useState(useSearchParam('auth_token'));
+ const [data, setData] = useState({});
+ const [showHeader, setShowHeader] = useState(false);
// added subdomain in query param for easy testing in vercel links
const subdomain = useSearchParam('subdomain') || window.location.hostname;
const { roomId, role } = getRoomIdRoleFromUrl();
const { overrideLayout, isHeadless } = useOverridePrebuiltLayout();
+ const hmsPrebuiltRef = useRef();
+
+ useEffect(() => {
+ if (roomCode) {
+ fetchData(subdomain, roomCode, setOnlyEmail, setData, setShowHeader);
+ }
+ }, []);
+
+ useEffect(() => {
+ // remove notifications and messages for beam
+ // enable beam speaker logging for transcription
+ if ((authToken || roomCode) && hmsPrebuiltRef.current && isHeadless) {
+ const { hmsActions } = hmsPrebuiltRef.current;
+ hmsActions?.enableBeamSpeakerLabelsLogging?.();
+ hmsActions?.ignoreMessageTypes?.(['chat', 'EMOJI_REACTION']);
+ hmsActions?.setAppData?.('disableNotificiations', true);
+ }
+ }, [authToken, roomCode, isHeadless]);
useEffect(() => {
if (!roomCode && !authToken) {
@@ -36,6 +60,15 @@ const App = () => {
direction="column"
css={{ size: '100%', overflowY: 'hidden', bg: '$background_dim' }}
>
+ {onlyEmail && showHeader && (
+
+
+
+ )}
{(authToken || roomCode) && (
{
init: process.env.REACT_APP_INIT_ENDPOINT,
},
}}
+ ref={hmsPrebuiltRef}
/>
)}
diff --git a/apps/100ms-custom-app/src/components/Header.js b/apps/100ms-custom-app/src/components/Header.js
index fd66c30f18..ef69e0e039 100644
--- a/apps/100ms-custom-app/src/components/Header.js
+++ b/apps/100ms-custom-app/src/components/Header.js
@@ -1,68 +1,42 @@
-import React, { Suspense, useCallback, useState } from 'react';
-import { CodeIcon, EditIcon, InviteIcon } from '@100mslive/react-icons';
-import { Button, Flex, styled, Text } from '@100mslive/roomkit-react';
-import { AppAnalytics } from '../utils/analytics';
+import React, { Suspense, useState } from 'react';
+import { useMedia } from 'react-use';
import {
- apiBasePath,
- getInitialsFromEmail,
- getRandomColor,
-} from '../utils/utils';
+ BrushDesignIcon,
+ PeopleAddIcon,
+ PersonContactIcon,
+} from '@100mslive/react-icons';
+import {
+ Button,
+ config as cssConfig,
+ Flex,
+ styled,
+ Text,
+} from '@100mslive/roomkit-react';
import darkLogo from '../assets/images/100ms_dark.svg';
import logo from '../assets/images/100ms_logo.svg';
-const DownloadCodeModal = React.lazy(() => import('./DownloadCodeModal'));
const InviteLinksModal = React.lazy(() => import('./InviteLinksModal'));
+
const LogoImg = styled('img', {
- maxHeight: '$14',
+ maxHeight: '$10',
width: 'auto',
cursor: 'pointer',
'@md': {
- maxHeight: '$12',
+ maxHeight: '$10',
},
});
-const randomColor = getRandomColor();
export default function Header({
- savingData,
- refreshData,
- settings,
- roomLinks,
- onlyEmail,
- toggleModal,
+ roomLinks = {},
+ policyID = '',
+ theme = 'DARK',
}) {
const [modal, togModal] = useState(false);
- const [codeModal, setCodeModal] = useState(false);
+ const isMobile = useMedia(cssConfig.media.md);
- const generateEnvData = useCallback(
- logo => {
- return `REACT_APP_TILE_SHAPE=${settings.tile_shape}\nREACT_APP_THEME=${
- settings.theme
- }\nREACT_APP_COLOR=${settings.brand_color}\nREACT_APP_LOGO=${
- logo || ''
- }\nREACT_APP_FONT=${
- settings.font
- }\nREACT_APP_TOKEN_GENERATION_ENDPOINT=${`${
- apiBasePath + window.location.hostname
- }/`}\nREACT_APP_ENV=${process.env.REACT_APP_ENV}\n`;
- },
- [settings.tile_shape, settings.brand_color, settings.theme, settings.font]
- );
-
- const downloadCode = async () => {
- await refreshData().then(logo => {
- var envFile = document.createElement('a');
- const data = generateEnvData(logo);
- envFile.setAttribute(
- 'href',
- `data:text/plain;charset=utf-8,${encodeURIComponent(data)}`
- );
- envFile.download = 'example.env';
- envFile.style.display = 'none';
- document.body.appendChild(envFile);
- envFile.click();
- document.body.removeChild(envFile);
- });
- };
+ if (isMobile) {
+ return null;
+ }
return (
<>
@@ -79,75 +53,94 @@ export default function Header({
onClick={() => {
window.open(process.env.REACT_APP_DASHBOARD_LINK);
}}
- src={settings.theme === 'dark' ? logo : darkLogo}
+ src={theme !== 'DARK' ? darkLogo : logo}
alt="100ms logo"
width={132}
height={40}
/>
- {onlyEmail && (
- <>
- {roomLinks && Object.keys(roomLinks).length > 0 && (
-
- )}
-
-
+
+
+ {roomLinks && Object.keys(roomLinks).length > 0 && (
+ togModal(true)}
+ align="center"
+ css={{
+ color: '$on_surface_medium',
+ borderRight: '1px solid $border_default',
+ cursor: 'pointer',
+ mr: '$md',
+ px: '$md',
+ gap: '$4',
+ '&:hover': { color: '$on_surface_high' },
}}
>
-
-
+
+
+ Invite Others
+
+
+ )}
+
- >
- )}
-
-
- {getInitialsFromEmail()}
-
-
+
+ >
- {codeModal && (
-
- setCodeModal(false)}
- />
-
- )}
+
{modal && (
{
useSearchParam(QUERY_PARAM_SKIP_PREVIEW_HEADFUL) === 'true' ||
directJoinHeadfulFromEnv;
let skipPreview = useSearchParam(QUERY_PARAM_SKIP_PREVIEW) === 'true';
+ const isMobile = useMedia(cssConfig.media.md);
let overrideLayout = undefined;
@@ -28,10 +31,11 @@ export const useOverridePrebuiltLayout = () => {
grid: {
enable_local_tile_inset: false,
hide_participant_name_on_tile: true,
- hide_audio_level_on_tile: true,
- rounded_video_tile: false,
+ rounded_video_tile: !isMobile,
hide_audio_mute_on_tile: true,
- video_object_fit: 'cover',
+ video_object_fit: isMobile ? 'cover' : 'contain',
+ edge_to_edge: true,
+ hide_metadata_on_tile: true,
},
},
},
diff --git a/apps/100ms-custom-app/src/utils/utils.js b/apps/100ms-custom-app/src/utils/utils.js
index 8c89b98f34..412e034b82 100644
--- a/apps/100ms-custom-app/src/utils/utils.js
+++ b/apps/100ms-custom-app/src/utils/utils.js
@@ -2,7 +2,7 @@ import axios from 'axios';
import cookies from 'js-cookies';
function isRoomCode(str) {
- const regex = /^[A-Za-z]{3}(-[A-Za-z]{3,4}){2}$/;
+ const regex = /^[A-Za-z]*(-[A-Za-z]*){2}$/;
return regex.test(str);
}
@@ -183,3 +183,38 @@ export const getAuthTokenUsingRoomIdRole = async function ({
throw Error('failed to get auth token using roomid and role');
}
};
+
+export const fetchData = async (
+ subdomain,
+ roomCode,
+ setOnlyEmail,
+ setData,
+ setShowHeader
+) => {
+ const jwt = getAuthInfo().token;
+
+ const url = `${apiBasePath}apps/get-details?domain=${subdomain}&room_id=${roomCode}`;
+ const headers = {};
+ if (jwt) {
+ headers['Authorization'] = `Bearer ${jwt}`;
+ }
+ headers['Content-Type'] = 'application/json';
+
+ getWithRetry(url, headers)
+ .then(res => {
+ if (res.data.success) {
+ setOnlyEmail(res.data.same_user);
+ setShowHeader(true);
+ setData({
+ roomLinks: res.data.room_links,
+ policyID: res.data.policy_id,
+ theme: res.data.theme,
+ });
+ }
+ })
+ .catch(err => {
+ setShowHeader(false);
+ const errorMessage = `[Get Details] ${err.message}`;
+ console.error(errorMessage);
+ });
+};
diff --git a/apps/100ms-web/package.json b/apps/100ms-web/package.json
index 78f3bae108..b7f5718953 100644
--- a/apps/100ms-web/package.json
+++ b/apps/100ms-web/package.json
@@ -9,11 +9,11 @@
"src"
],
"dependencies": {
- "@100mslive/hls-player": "0.1.15-alpha.0",
- "@100mslive/hms-virtual-background": "1.11.15-alpha.0",
- "@100mslive/react-icons": "0.8.15-alpha.0",
- "@100mslive/react-sdk": "0.8.15-alpha.0",
- "@100mslive/roomkit-react": "0.1.6-alpha.0",
+ "@100mslive/hls-player": "0.1.15-alpha.2",
+ "@100mslive/hms-virtual-background": "1.11.15-alpha.2",
+ "@100mslive/react-icons": "0.8.15-alpha.2",
+ "@100mslive/react-sdk": "0.8.15-alpha.2",
+ "@100mslive/roomkit-react": "0.1.6-alpha.2",
"@emoji-mart/data": "^1.0.6",
"@emoji-mart/react": "^1.0.1",
"@tldraw/tldraw": "^1.18.4",
diff --git a/packages/hls-player/package.json b/packages/hls-player/package.json
index 30efad914e..7e3069f621 100644
--- a/packages/hls-player/package.json
+++ b/packages/hls-player/package.json
@@ -1,6 +1,6 @@
{
"name": "@100mslive/hls-player",
- "version": "0.1.15-alpha.0",
+ "version": "0.1.15-alpha.2",
"description": "HLS client library which uses HTML5 Video element and Media Source Extension for playback",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
@@ -31,8 +31,8 @@
"author": "100ms",
"license": "MIT",
"dependencies": {
- "@100mslive/hls-stats": "0.2.15-alpha.0",
+ "@100mslive/hls-stats": "0.2.15-alpha.2",
"eventemitter2": "^6.4.7",
- "hls.js": "^1.3.0"
+ "hls.js": "1.4.3"
}
}
diff --git a/packages/hls-stats/package.json b/packages/hls-stats/package.json
index 4a3bfeff90..6876479ee0 100644
--- a/packages/hls-stats/package.json
+++ b/packages/hls-stats/package.json
@@ -1,6 +1,6 @@
{
"name": "@100mslive/hls-stats",
- "version": "0.2.15-alpha.0",
+ "version": "0.2.15-alpha.2",
"description": "A simple library that provides stats for your hls stream",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
@@ -31,9 +31,9 @@
"author": "Ragzzy-R",
"license": "ISC",
"devDependencies": {
- "hls.js": "^1.3.0"
+ "hls.js": "1.4.3"
},
"peerDependencies": {
- "hls.js": "^1.3.0"
+ "hls.js": "1.4.3"
}
}
diff --git a/packages/hms-noise-suppression/package.json b/packages/hms-noise-suppression/package.json
index 5f47b872db..b53ed786e2 100644
--- a/packages/hms-noise-suppression/package.json
+++ b/packages/hms-noise-suppression/package.json
@@ -1,5 +1,5 @@
{
- "version": "0.9.15-alpha.0",
+ "version": "0.9.15-alpha.2",
"license": "MIT",
"main": "dist/index.cjs.js",
"typings": "dist/index.d.ts",
@@ -37,6 +37,6 @@
"author": "vishaldhull09",
"module": "dist/index.js",
"devDependencies": {
- "@100mslive/hms-video": "0.9.15-alpha.0"
+ "@100mslive/hms-video": "0.9.15-alpha.2"
}
}
diff --git a/packages/hms-video-store/package.json b/packages/hms-video-store/package.json
index 76e089b20d..34d88ca6a3 100644
--- a/packages/hms-video-store/package.json
+++ b/packages/hms-video-store/package.json
@@ -1,5 +1,5 @@
{
- "version": "0.10.15-alpha.0",
+ "version": "0.10.15-alpha.2",
"license": "MIT",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
@@ -41,7 +41,7 @@
"author": "100ms",
"sideEffects": false,
"dependencies": {
- "@100mslive/hms-video": "0.9.15-alpha.0",
+ "@100mslive/hms-video": "0.9.15-alpha.2",
"eventemitter2": "^6.4.7",
"immer": "^9.0.6",
"reselect": "4.0.0",
diff --git a/packages/hms-video-store/src/core/IHMSActions.ts b/packages/hms-video-store/src/core/IHMSActions.ts
index 5028e586ef..a3a807ba39 100644
--- a/packages/hms-video-store/src/core/IHMSActions.ts
+++ b/packages/hms-video-store/src/core/IHMSActions.ts
@@ -4,6 +4,7 @@ import {
HMSAudioTrackSettings,
HMSConfig,
HMSLogLevel,
+ HMSMidCallPreviewConfig,
HMSPluginSupportResult,
HMSPreferredSimulcastLayer,
HMSPreviewConfig,
@@ -45,7 +46,7 @@ import { HMSRoleChangeRequest } from './selectors';
* @category Core
*/
export interface IHMSActions }> {
- preview(config: HMSPreviewConfig): Promise;
+ preview(config: HMSMidCallPreviewConfig | HMSPreviewConfig): Promise;
/**
* join function can be used to join the room, if the room join is successful,
* current details of participants and track details are populated in the store.
@@ -63,6 +64,11 @@ export interface IHMSActions;
+ /**
+ * stop tracks fetched during midcall preview and general cleanup
+ */
+ cancelMidCallPreview(): Promise;
+
/**
* If you want to enable screenshare for the local peer this class can be called.
* The store will be populated with the incoming track, and the subscriber(or
diff --git a/packages/hms-video-store/src/core/hmsSDKStore/HMSSDKActions.ts b/packages/hms-video-store/src/core/hmsSDKStore/HMSSDKActions.ts
index d76e198afa..d55ee5bf98 100644
--- a/packages/hms-video-store/src/core/hmsSDKStore/HMSSDKActions.ts
+++ b/packages/hms-video-store/src/core/hmsSDKStore/HMSSDKActions.ts
@@ -1447,19 +1447,11 @@ export class HMSSDKActions {
let peer = this.store.getState(selectPeerByID(sdkPeer.peerId));
const actionName = PEER_NOTIFICATION_TYPES[type] || 'peerUpdate';
- if (
- [
- sdkTypes.HMSPeerUpdate.PEER_JOINED,
- sdkTypes.HMSPeerUpdate.PEER_LEFT,
- sdkTypes.HMSPeerUpdate.ROLE_UPDATED,
- ].includes(type)
- ) {
+ if (type === sdkTypes.HMSPeerUpdate.ROLE_UPDATED) {
+ this.syncRoomState(actionName);
+ this.updateMidCallPreviewRoomState(type, sdkPeer);
+ } else if ([sdkTypes.HMSPeerUpdate.PEER_JOINED, sdkTypes.HMSPeerUpdate.PEER_LEFT].includes(type)) {
this.syncRoomState(actionName);
- if (this.store.getState(selectIsInPreview)) {
- this.setState(store => {
- store.room.roomState = HMSRoomState.Connected;
- }, 'midCallPreviewCompleted');
- }
// if peer wasn't available before sync(will happen if event is peer join)
if (!peer) {
peer = this.store.getState(selectPeerByID(sdkPeer.peerId));
@@ -1480,6 +1472,14 @@ export class HMSSDKActions {
+ store.room.roomState = HMSRoomState.Connected;
+ }, 'midCallPreviewCompleted');
+ }
+ }
+
private setSessionStoreValueLocally(
updates: SessionStoreUpdate | SessionStoreUpdate[],
actionName = 'setSessionStore',
diff --git a/packages/hms-video-web/package.json b/packages/hms-video-web/package.json
index f2b9e6298f..7016b432a0 100644
--- a/packages/hms-video-web/package.json
+++ b/packages/hms-video-web/package.json
@@ -1,6 +1,6 @@
{
"name": "@100mslive/hms-video",
- "version": "0.9.15-alpha.0",
+ "version": "0.9.15-alpha.2",
"license": "MIT",
"main": "dist/index.cjs.js",
"typings": "dist/index.d.ts",
diff --git a/packages/hms-video-web/src/interfaces/config.ts b/packages/hms-video-web/src/interfaces/config.ts
index 0f283d2018..6a2f33e370 100644
--- a/packages/hms-video-web/src/interfaces/config.ts
+++ b/packages/hms-video-web/src/interfaces/config.ts
@@ -59,13 +59,21 @@ export interface HMSConfig {
autoManageWakeLock?: boolean;
}
+export interface HMSMidCallPreviewConfig {
+ /** the role that would be used for preview, note that the role from token would be used to join */
+ asRole?: string;
+ /**
+ * initial settings for audio/video and device to be used. Please don't pass
+ * this field while joining if you're using preview, the state changes in preview will be remembered
+ * across to join.
+ */
+ settings?: InitialSettings;
+}
+
/**
* the config object tells the SDK options you want to preview with(use if you want preview and join with different roles)
* @link https://docs.100ms.live/javascript/v2/features/preview
*/
-export interface HMSPreviewConfig extends HMSConfig {
- /** the role that would be used for preview, note that the role from token would be used to join */
- asRole?: string;
-}
+export interface HMSPreviewConfig extends HMSConfig, HMSMidCallPreviewConfig {}
export type { InitialSettings as HMSConfigInitialSettings };
diff --git a/packages/hms-video-web/src/sdk/index.ts b/packages/hms-video-web/src/sdk/index.ts
index bcba8213fd..5da31c167d 100644
--- a/packages/hms-video-web/src/sdk/index.ts
+++ b/packages/hms-video-web/src/sdk/index.ts
@@ -408,6 +408,7 @@ export class HMSSdk implements HMSInterface {
const tracks = await this.localTrackManager.getTracksToPublish(settings);
tracks.forEach(track => this.setLocalPeerTrack(track));
this.localPeer?.audioTrack && this.initPreviewTrackAudioLevelMonitor();
+ await this.initDeviceManagers();
this.listener?.onPreview(this.store.getRoom()!, tracks);
}
diff --git a/packages/hms-virtual-background/package.json b/packages/hms-virtual-background/package.json
index 6b2bcc80f9..a2b2461b0e 100755
--- a/packages/hms-virtual-background/package.json
+++ b/packages/hms-virtual-background/package.json
@@ -1,5 +1,5 @@
{
- "version": "1.11.15-alpha.0",
+ "version": "1.11.15-alpha.2",
"license": "MIT",
"main": "dist/index.cjs.js",
"typings": "dist/index.d.ts",
@@ -30,7 +30,7 @@
"author": "ashish17",
"module": "dist/index.js",
"devDependencies": {
- "@100mslive/hms-video": "0.9.15-alpha.0"
+ "@100mslive/hms-video": "0.9.15-alpha.2"
},
"dependencies": {
"@mediapipe/selfie_segmentation": "^0.1.1632777926",
diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json
index 4bebe690c8..d530889f3a 100644
--- a/packages/react-icons/package.json
+++ b/packages/react-icons/package.json
@@ -4,7 +4,7 @@
"main": "dist/index.cjs.js",
"module": "dist/index.js",
"typings": "dist/index.d.ts",
- "version": "0.8.15-alpha.0",
+ "version": "0.8.15-alpha.2",
"author": "100ms",
"license": "MIT",
"files": [
diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json
index b1f7503388..5de5b480a4 100644
--- a/packages/react-sdk/package.json
+++ b/packages/react-sdk/package.json
@@ -4,7 +4,7 @@
"main": "dist/index.cjs.js",
"module": "dist/index.js",
"typings": "dist/index.d.ts",
- "version": "0.8.15-alpha.0",
+ "version": "0.8.15-alpha.2",
"author": "100ms",
"license": "MIT",
"files": [
@@ -43,7 +43,7 @@
"react": ">=16.8 <19.0.0"
},
"dependencies": {
- "@100mslive/hms-video-store": "0.10.15-alpha.0",
+ "@100mslive/hms-video-store": "0.10.15-alpha.2",
"react-resize-detector": "^7.0.0",
"zustand": "^3.6.2"
}
diff --git a/packages/react-sdk/src/hooks/useDevices.ts b/packages/react-sdk/src/hooks/useDevices.ts
index d3f9cdde8a..d89886871b 100644
--- a/packages/react-sdk/src/hooks/useDevices.ts
+++ b/packages/react-sdk/src/hooks/useDevices.ts
@@ -2,7 +2,9 @@ import { useCallback } from 'react';
import {
DeviceType,
selectDevices,
+ selectIsAllowedToPreviewMedia,
selectIsAllowedToPublish,
+ selectIsInPreview,
selectLocalMediaSettings,
} from '@100mslive/hms-video-store';
import { hooksErrHandler } from '../hooks/types';
@@ -45,7 +47,9 @@ export const useDevices = (handleError: hooksErrHandler = logErrorHandler): useD
const actions = useHMSActions();
const sdkAllDevices: DeviceTypeAndInfo = useHMSStore(selectDevices);
const sdkSelectedDevices = useHMSStore(selectLocalMediaSettings);
- const isAllowedToPublish = useHMSStore(selectIsAllowedToPublish);
+ const isInPreview = useHMSStore(selectIsInPreview);
+ const selectAllowed = isInPreview ? selectIsAllowedToPreviewMedia : selectIsAllowedToPublish;
+ const isAllowedToPublish = useHMSStore(selectAllowed);
const selectedDeviceIDs: DeviceTypeAndInfo = {
[DeviceType.audioOutput]: sdkSelectedDevices.audioOutputDeviceId,
diff --git a/packages/react-sdk/src/hooks/usePreviewJoin.ts b/packages/react-sdk/src/hooks/usePreviewJoin.ts
index c383ff1345..fb1b13d85e 100644
--- a/packages/react-sdk/src/hooks/usePreviewJoin.ts
+++ b/packages/react-sdk/src/hooks/usePreviewJoin.ts
@@ -125,18 +125,17 @@ export const usePreviewJoin = ({
if (!token) {
return;
}
- if (roomState !== HMSRoomState.Disconnected) {
- return;
- }
- if (isConnected) {
- await actions.leave();
- }
try {
+ if (isConnected || roomState !== HMSRoomState.Disconnected) {
+ await actions.leave().catch(() => {
+ // Do nothing as this might lead to leave called before join
+ });
+ }
await actions.preview(config);
} catch (err) {
handleError(err as Error, 'preview');
}
- }, [actions, handleError, token, roomState, config, isConnected]);
+ }, [token, isConnected, roomState, actions, config, handleError]);
const join = useCallback(async () => {
if (!token) {
diff --git a/packages/roomkit-react/package.json b/packages/roomkit-react/package.json
index 944309e351..0d0c0ed824 100644
--- a/packages/roomkit-react/package.json
+++ b/packages/roomkit-react/package.json
@@ -10,7 +10,7 @@
"prebuilt",
"roomkit"
],
- "version": "0.1.6-alpha.0",
+ "version": "0.1.6-alpha.2",
"author": "100ms",
"license": "MIT",
"files": [
@@ -76,10 +76,10 @@
"react": ">=17.0.2 <19.0.0"
},
"dependencies": {
- "@100mslive/hls-player": "0.1.15-alpha.0",
- "@100mslive/hms-virtual-background": "1.11.15-alpha.0",
- "@100mslive/react-icons": "0.8.15-alpha.0",
- "@100mslive/react-sdk": "0.8.15-alpha.0",
+ "@100mslive/hls-player": "0.1.15-alpha.2",
+ "@100mslive/hms-virtual-background": "1.11.15-alpha.2",
+ "@100mslive/react-icons": "0.8.15-alpha.2",
+ "@100mslive/react-sdk": "0.8.15-alpha.2",
"@100mslive/types-prebuilt": "0.12.0",
"@emoji-mart/data": "^1.0.6",
"@emoji-mart/react": "^1.0.1",
diff --git a/packages/roomkit-react/src/Prebuilt/App.tsx b/packages/roomkit-react/src/Prebuilt/App.tsx
index caa734c7dc..589e0c5e1f 100644
--- a/packages/roomkit-react/src/Prebuilt/App.tsx
+++ b/packages/roomkit-react/src/Prebuilt/App.tsx
@@ -13,8 +13,6 @@ import {
// @ts-ignore: No implicit Any
import { AppData } from './components/AppData/AppData';
// @ts-ignore: No implicit Any
-import { BeamSpeakerLabelsLogging } from './components/AudioLevel/BeamSpeakerLabelsLogging';
-// @ts-ignore: No implicit Any
import AuthToken from './components/AuthToken';
// @ts-ignore: No implicit Any
import { ErrorBoundary } from './components/ErrorBoundary';
@@ -40,6 +38,8 @@ import { HMSPrebuiltContext, useHMSPrebuiltContext } from './AppContext';
import { FlyingEmoji } from './plugins/FlyingEmoji';
// @ts-ignore: No implicit Any
import { RemoteStopScreenshare } from './plugins/RemoteStopScreenshare';
+// @ts-ignore: No implicit Any
+import { useIsNotificationDisabled } from './components/AppData/useUISettings';
import { useAutoStartStreaming } from './components/hooks/useAutoStartStreaming';
import {
useRoomLayoutLeaveScreen,
@@ -77,16 +77,6 @@ export type HMSPrebuiltRefType = {
hmsNotifications: IHMSNotifications;
};
-// TODO: remove now that there are options to change to portrait
-const getAspectRatio = ({ width, height }: { width: number; height: number }) => {
- const host = process.env.REACT_APP_HOST_NAME || '';
- const portraitDomains = (process.env.REACT_APP_PORTRAIT_MODE_DOMAINS || '').split(',');
- if (portraitDomains.includes(host) && width > height) {
- return { width: height, height: width };
- }
- return { width, height };
-};
-
export const HMSPrebuilt = React.forwardRef(
(
{
@@ -103,9 +93,7 @@ export const HMSPrebuilt = React.forwardRef {
- const aspectRatio = '1-1';
const metadata = '';
- const { 0: width, 1: height } = aspectRatio.split('-').map(el => parseInt(el));
const reactiveStore = useRef();
const [hydrated, setHydrated] = React.useState(false);
@@ -218,7 +206,6 @@ export const HMSPrebuilt = React.forwardRef {
}>
+ }>
}
@@ -278,7 +265,7 @@ const RouteList = () => {
}>
+ }>
}
@@ -289,7 +276,7 @@ const RouteList = () => {
}>
+ }>
}
@@ -297,7 +284,7 @@ const RouteList = () => {
}>
+ }>
}
@@ -352,16 +339,16 @@ function AppRoutes({
defaultAuthToken?: string;
}) {
const roomLayout = useRoomLayout();
+ const isNotificationsDisabled = useIsNotificationDisabled();
return (
<>
-
+ {!isNotificationsDisabled && }
-
{roomLayout && (
diff --git a/packages/roomkit-react/src/Prebuilt/AppContext.tsx b/packages/roomkit-react/src/Prebuilt/AppContext.tsx
index ae3df08daf..8a08efd24e 100644
--- a/packages/roomkit-react/src/Prebuilt/AppContext.tsx
+++ b/packages/roomkit-react/src/Prebuilt/AppContext.tsx
@@ -6,7 +6,7 @@ type HMSPrebuiltContextType = {
role?: string;
userName?: string;
userId?: string;
- endpoints?: object;
+ endpoints?: Record;
onLeave?: () => void;
};
diff --git a/packages/roomkit-react/src/Prebuilt/IconButton.jsx b/packages/roomkit-react/src/Prebuilt/IconButton.jsx
index 1a86330ef5..d2e8157116 100644
--- a/packages/roomkit-react/src/Prebuilt/IconButton.jsx
+++ b/packages/roomkit-react/src/Prebuilt/IconButton.jsx
@@ -9,11 +9,21 @@ const IconButton = styled(BaseIconButton, {
r: '$1',
variants: {
active: {
+ true: {
+ color: '$on_surface_high',
+ backgroundColor: '$transparent',
+ },
false: {
border: '1px solid transparent',
color: '$on_primary_high',
},
},
+ disabled: {
+ true: {
+ backgroundColor: '$surface_brighter',
+ color: '$on_surface_low',
+ },
+ },
},
});
diff --git a/packages/roomkit-react/src/Prebuilt/common/constants.js b/packages/roomkit-react/src/Prebuilt/common/constants.js
index c61bba8d7e..ad1a903ec5 100644
--- a/packages/roomkit-react/src/Prebuilt/common/constants.js
+++ b/packages/roomkit-react/src/Prebuilt/common/constants.js
@@ -29,7 +29,6 @@ export const APP_DATA = {
appConfig: 'appConfig',
sidePane: 'sidePane',
hlsStats: 'hlsStats',
- hlsViewerRole: 'hlsViewerRole',
waitingViewerRole: 'waitingViewerRole',
subscribedNotifications: 'subscribedNotifications',
logo: 'logo',
@@ -45,10 +44,10 @@ export const APP_DATA = {
pdfConfig: 'pdfConfig',
minimiseInset: 'minimiseInset',
activeScreensharePeerId: 'activeScreensharePeerId',
+ disableNotificiations: 'disableNotificiations',
};
export const UI_SETTINGS = {
isAudioOnly: 'isAudioOnly',
- isHeadless: 'isHeadless',
maxTileCount: 'maxTileCount',
uiViewMode: 'uiViewMode',
showStatsOnTiles: 'showStatsOnTiles',
@@ -93,3 +92,5 @@ export const SESSION_STORE_KEY = {
PINNED_MESSAGE: 'pinnedMessage',
SPOTLIGHT: 'spotlight',
};
+
+export const ROLE_CHANGE_DECLINED = 'role_change_declined';
diff --git a/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx b/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
index 14d96ee75a..d1ac7d1e6d 100644
--- a/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/AppData/AppData.jsx
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import {
HMSRoomState,
selectAvailableRoleNames,
+ selectFullAppData,
selectHLSState,
selectIsConnectedToRoom,
selectLocalPeerRoleName,
@@ -36,7 +37,6 @@ export const getAppDetails = appDetails => {
const initialAppData = {
[APP_DATA.uiSettings]: {
[UI_SETTINGS.isAudioOnly]: false,
- [UI_SETTINGS.isHeadless]: false,
[UI_SETTINGS.maxTileCount]: 9,
[UI_SETTINGS.showStatsOnTiles]: false,
[UI_SETTINGS.enableAmbientMusic]: false,
@@ -65,6 +65,7 @@ const initialAppData = {
[APP_DATA.authToken]: '',
[APP_DATA.minimiseInset]: false,
[APP_DATA.activeScreensharePeerId]: '',
+ [APP_DATA.disableNotificiations]: false,
};
export const AppData = React.memo(({ appDetails, tokenEndpoint }) => {
@@ -76,6 +77,7 @@ export const AppData = React.memo(({ appDetails, tokenEndpoint }) => {
const roleNames = useHMSStore(selectAvailableRoleNames);
const rolesMap = useHMSStore(selectRolesMap);
const localPeerRole = useHMSStore(selectLocalPeerRoleName);
+ const appData = useHMSStore(selectFullAppData);
useEffect(() => {
if (!isConnected && sidePane && sidePane !== SIDE_PANE_OPTIONS.PARTICIPANTS) {
@@ -84,12 +86,16 @@ export const AppData = React.memo(({ appDetails, tokenEndpoint }) => {
}, [isConnected, sidePane, resetSidePane]);
useEffect(() => {
- hmsActions.initAppData(initialAppData);
+ hmsActions.initAppData({
+ ...initialAppData,
+ ...appData,
+ });
hmsActions.setFrameworkInfo({
type: 'react-web',
isPrebuilt: true,
version: React.version,
});
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [hmsActions]);
useEffect(() => {
diff --git a/packages/roomkit-react/src/Prebuilt/components/AppData/useUISettings.js b/packages/roomkit-react/src/Prebuilt/components/AppData/useUISettings.js
index 9ba927c737..af672ba3d0 100644
--- a/packages/roomkit-react/src/Prebuilt/components/AppData/useUISettings.js
+++ b/packages/roomkit-react/src/Prebuilt/components/AppData/useUISettings.js
@@ -11,7 +11,7 @@ import {
useHMSVanillaStore,
} from '@100mslive/react-sdk';
import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
-import { APP_DATA, SESSION_STORE_KEY, UI_SETTINGS } from '../../common/constants';
+import { APP_DATA, SESSION_STORE_KEY } from '../../common/constants';
/**
* fields saved related to UI settings in store's app data can be
@@ -46,11 +46,6 @@ export const useSetUiSettings = uiSettingKey => {
return [value, setValue];
};
-export const useIsHeadless = () => {
- const isHeadless = useUISettings(UI_SETTINGS.isHeadless);
- return isHeadless;
-};
-
export const useWaitingViewerRole = () => {
return useHMSStore(selectAppData(APP_DATA.waitingViewerRole));
};
@@ -90,6 +85,11 @@ export const useSubscribedNotifications = notificationKey => {
return notificationPreference;
};
+export const useIsNotificationDisabled = () => {
+ const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.disableNotificiations));
+ return notificationPreference;
+};
+
export const useSetSubscribedNotifications = notificationKey => {
const value = useSubscribedNotifications(notificationKey);
const setValue = useSetAppData({
diff --git a/packages/roomkit-react/src/Prebuilt/components/AudioLevel/BeamSpeakerLabelsLogging.jsx b/packages/roomkit-react/src/Prebuilt/components/AudioLevel/BeamSpeakerLabelsLogging.jsx
deleted file mode 100644
index 1bebfa7df8..0000000000
--- a/packages/roomkit-react/src/Prebuilt/components/AudioLevel/BeamSpeakerLabelsLogging.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { useEffect } from 'react';
-import { useHMSActions } from '@100mslive/react-sdk';
-import { useIsHeadless } from '../AppData/useUISettings';
-import { FeatureFlags } from '../../services/FeatureFlags';
-
-export function BeamSpeakerLabelsLogging() {
- const hmsActions = useHMSActions();
- const isHeadless = useIsHeadless();
-
- useEffect(() => {
- if (FeatureFlags.enableBeamSpeakersLogging && isHeadless) {
- hmsActions.enableBeamSpeakerLabelsLogging();
- }
- }, [hmsActions, isHeadless]);
- return null;
-}
diff --git a/packages/roomkit-react/src/Prebuilt/components/AudioVideoToggle.jsx b/packages/roomkit-react/src/Prebuilt/components/AudioVideoToggle.jsx
index dacbc16399..d9f5fe1b05 100644
--- a/packages/roomkit-react/src/Prebuilt/components/AudioVideoToggle.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/AudioVideoToggle.jsx
@@ -67,16 +67,17 @@ export const AudioVideoToggle = ({ hideOptions = false }) => {
const videoTrackId = useHMSStore(selectLocalVideoTrackID);
const localVideoTrack = useHMSStore(selectVideoTrackByID(videoTrackId));
const roomState = useHMSStore(selectRoomState);
- const isAudioPermitted = toggleAudio && audioInput?.length > 0;
- const isVideoPermitted = toggleVideo && videoInput?.length > 0;
+ const hasAudioDevices = audioInput?.length > 0;
+ const hasVideoDevices = videoInput?.length > 0;
return (
{toggleAudio ? (
- hideOptions ? (
+ hideOptions || !hasAudioDevices ? (
{
) : (
{
) : null}
{toggleVideo ? (
- hideOptions ? (
+ hideOptions || !hasVideoDevices ? (
{
) : (
{
) : null;
};
-export const Chat = ({ screenType }) => {
+export const Chat = ({ screenType, hideControls = false }) => {
const notification = useHMSNotifications(HMSNotificationTypes.PEER_LEFT);
const [peerSelector, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER_ID);
const [roleSelector, setRoleSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.ROLE);
@@ -76,10 +76,11 @@ export const Chat = ({ screenType }) => {
peerId: peerSelector && peerName ? peerSelector : '',
selection: roleSelector ? roleSelector : peerSelector && peerName ? peerName : 'Everyone',
});
- const [isSelectorOpen, setSelectorOpen] = useState(false);
+ const [isSelectorOpen] = useState(false);
const listRef = useRef(null);
const hmsActions = useHMSActions();
const { setPinnedMessage } = useSetPinnedMessage();
+
useEffect(() => {
if (notification && notification.data && peerSelector === notification.data.id) {
setPeerSelector('');
@@ -92,8 +93,15 @@ export const Chat = ({ screenType }) => {
}, [notification, peerSelector, setPeerSelector]);
const storeMessageSelector = selectHMSMessagesCount;
+ const { elements } = useRoomLayoutConferencingScreen();
const isMobile = useMedia(cssConfig.media.md);
+ let isScrolledToBottom = false;
+ if (listRef.current) {
+ const currentRef = listRef.current._outerRef;
+ isScrolledToBottom = currentRef.scrollHeight - currentRef.clientHeight - currentRef.scrollTop < 10;
+ }
+
const messagesCount = useHMSStore(storeMessageSelector) || 0;
const scrollToBottom = useCallback(
(unreadCount = 0) => {
@@ -109,13 +117,18 @@ export const Chat = ({ screenType }) => {
);
return (
-
- {!isMobile ? (
- <>
- setSelectorOpen(value => !value)} />
-
- >
- ) : null}
+
+ {isMobile && elements?.chat?.is_overlay ? null : (
+ <>{elements?.chat?.allow_pinning_messages ? : null}>
+ )}
{
}}
peerId={chatOptions.peerId}
>
- {!isSelectorOpen && (
+ {!isSelectorOpen && !isScrolledToBottom && (
)}
diff --git a/packages/roomkit-react/src/Prebuilt/components/Chat/ChatBody.jsx b/packages/roomkit-react/src/Prebuilt/components/Chat/ChatBody.jsx
index 542e75076a..3edd8d8834 100644
--- a/packages/roomkit-react/src/Prebuilt/components/Chat/ChatBody.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/Chat/ChatBody.jsx
@@ -14,7 +14,7 @@ import {
useHMSActions,
useHMSStore,
} from '@100mslive/react-sdk';
-import { CopyIcon, PinIcon, VerticalMenuIcon } from '@100mslive/react-icons';
+import { PinIcon, VerticalMenuIcon } from '@100mslive/react-icons';
import { Dropdown } from '../../../Dropdown';
import { IconButton } from '../../../IconButton';
import { Box, Flex } from '../../../Layout';
@@ -22,7 +22,7 @@ import { Text } from '../../../Text';
import { config as cssConfig, styled } from '../../../Theme';
import { Tooltip } from '../../../Tooltip';
import emptyChat from '../../images/empty-chat.svg';
-import { ToastManager } from '../Toast/ToastManager';
+import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
import { useSetPinnedMessage } from '../hooks/useSetPinnedMessage';
const formatTime = date => {
@@ -126,10 +126,9 @@ const getMessageType = ({ roles, receiver }) => {
}
return receiver ? 'private' : '';
};
-const ChatActions = ({ onPin, showPinAction, messageContent }) => {
+const ChatActions = ({ onPin, showPinAction }) => {
const [open, setOpen] = useState(false);
- const isMobile = useMedia(cssConfig.media.md);
- if (!isMobile && !showPinAction) {
+ if (!showPinAction) {
return null;
}
@@ -154,8 +153,7 @@ const ChatActions = ({ onPin, showPinAction, messageContent }) => {
Pin Message
- {isMobile && showPinAction ? : null}
- {isMobile ? (
+ {/* {isMobile ? (
{
@@ -177,7 +175,7 @@ const ChatActions = ({ onPin, showPinAction, messageContent }) => {
Copy Message
- ) : null}
+ ) : null} */}
@@ -203,7 +201,8 @@ const ChatMessage = React.memo(({ index, style = {}, message, setRowHeight, onPi
}
}, [index, setRowHeight]);
const isMobile = useMedia(cssConfig.media.md);
-
+ const { elements } = useRoomLayoutConferencingScreen();
+ const isOverlay = elements?.chat?.is_overlay && isMobile;
const hmsActions = useHMSActions();
const localPeerId = useHMSStore(selectLocalPeerID);
const permissions = useHMSStore(selectPermissions);
@@ -212,7 +211,7 @@ const ChatMessage = React.memo(({ index, style = {}, message, setRowHeight, onPi
receiver: message.recipientPeer,
});
// show pin action only if peer has remove others permission and the message is of broadcast type
- const showPinAction = permissions.removeOthers && !messageType;
+ const showPinAction = permissions.removeOthers && !messageType && elements?.chat?.allow_pinning_messages;
useEffect(() => {
if (message.id && !message.read && inView) {
@@ -233,7 +232,7 @@ const ChatMessage = React.memo(({ index, style = {}, message, setRowHeight, onPi
css={{
flexWrap: 'wrap',
// Theme independent color, token should not be used for transparent chat
- bg: messageType ? (isMobile ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
+ bg: messageType ? (isOverlay ? 'rgba(0, 0, 0, 0.64)' : '$surface_default') : undefined,
r: messageType ? '$1' : undefined,
px: messageType ? '$4' : '$2',
py: messageType ? '$4' : 0,
@@ -244,7 +243,7 @@ const ChatMessage = React.memo(({ index, style = {}, message, setRowHeight, onPi
>
{message.senderName === 'You' || !message.senderName ? (
-
+
{message.senderName || 'Anonymous'}
) : (
-
+
{message.senderName}
)}
- {!isMobile ? (
+ {!isOverlay ? (
- {!isMobile ? (
-
- ) : null}
+ {!isOverlay ? : null}
e.stopPropagation()}
>
@@ -395,8 +393,9 @@ export const ChatBody = React.forwardRef(({ role, peerId, scrollToBottom }, list
let messages = useHMSStore(storeMessageSelector);
messages = useMemo(() => messages?.filter(message => message.type === 'chat') || [], [messages]);
const isMobile = useMedia(cssConfig.media.md);
+ const { elements } = useRoomLayoutConferencingScreen();
- if (messages.length === 0 && !isMobile) {
+ if (messages.length === 0 && !(isMobile && elements?.chat?.is_overlay)) {
return (
void }) {
const [showEmoji, setShowEmoji] = useState(false);
const ref = useEmojiPickerStyles(showEmoji);
return (
@@ -62,14 +66,26 @@ function EmojiPicker({ onSelect }) {
);
}
-export const ChatFooter = ({ role, peerId, onSend, children /* onSelect, selection, screenType */ }) => {
+export const ChatFooter = ({
+ role,
+ peerId,
+ onSend,
+ children /* onSelect, selection, screenType */,
+}: {
+ role: any;
+ peerId: string;
+ onSend: any;
+ children: ReactNode;
+}) => {
const hmsActions = useHMSActions();
- const inputRef = useRef(null);
+ const inputRef = useRef(null);
const [draftMessage, setDraftMessage] = useChatDraftMessage();
const isMobile = useMedia(cssConfig.media.md);
+ const { elements } = useRoomLayoutConferencingScreen();
+ const isOverlayChat = elements?.chat?.is_overlay;
const sendMessage = useCallback(async () => {
- const message = inputRef.current.value;
+ const message = inputRef?.current?.value;
if (!message || !message.trim().length) {
return;
}
@@ -86,7 +102,8 @@ export const ChatFooter = ({ role, peerId, onSend, children /* onSelect, selecti
onSend();
}, 0);
} catch (error) {
- ToastManager.addToast({ title: error.message });
+ const err = error as Error;
+ ToastManager.addToast({ title: err.message });
}
}, [role, peerId, hmsActions, onSend]);
@@ -113,7 +130,7 @@ export const ChatFooter = ({ role, peerId, onSend, children /* onSelect, selecti
{children}
+
+
- {screenType !== 'hls_live_streaming' ? (
-
-
-
- Raise Hand
-
-
- {isHandRaised ? : null}
-
-
- ) : null}
-
- {isBRBEnabled && screenType === 'hls_live_streaming' ? (
+ {isBRBEnabled && screenType !== 'hls_live_streaming' ? (
@@ -129,8 +117,6 @@ export const DesktopOptions = ({
) : null}
- {isBRBEnabled && screenType === 'hls_live_streaming' ? : null}
-
{screenType !== 'hls_live_streaming' ? (
import('../../../plugins/VirtualBackground/VirtualBackground'));
@@ -38,14 +63,18 @@ const MODALS = {
EMBED_URL: 'embedUrl',
};
-export const MwebOptions = ({ elements }) => {
+export const MwebOptions = ({
+ elements,
+ screenType,
+}: {
+ elements: DefaultConferencingScreen_Elements;
+ screenType: keyof ConferencingScreen;
+}) => {
const hmsActions = useHMSActions();
const permissions = useHMSStore(selectPermissions);
const isConnected = useHMSStore(selectIsConnectedToRoom);
const { isBrowserRecordingOn, isStreamingOn, isHLSRunning } = useRecordingStreaming();
-
const [openModals, setOpenModals] = useState(new Set());
-
const [openOptionsSheet, setOpenOptionsSheet] = useState(false);
const [openSettingsSheet, setOpenSettingsSheet] = useState(false);
const [showEmojiCard, setShowEmojiCard] = useState(false);
@@ -54,11 +83,12 @@ export const MwebOptions = ({ elements }) => {
const toggleParticipants = useSidepaneToggle(SIDE_PANE_OPTIONS.PARTICIPANTS);
const peerCount = useHMSStore(selectPeerCount);
const emojiCardRef = useRef(null);
+ const { isBRBOn, toggleBRB, isHandRaised, toggleHandRaise } = useMyMetadata();
// const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
- const updateState = (modalName, value) => {
+ const updateState = (modalName: string, value: boolean) => {
setOpenModals(modals => {
const copy = new Set(modals);
if (value) {
@@ -75,13 +105,13 @@ export const MwebOptions = ({ elements }) => {
return (
<>
-
-
-
+
+
+
-
-
-
+
+
+
{
px: '$9',
}}
>
- {
- toggleParticipants();
- setOpenOptionsSheet(false);
- }}
- >
- {getFormattedCount(peerCount)}
-
- Participants
-
+ {elements?.participant_list && (
+ {
+ toggleParticipants();
+ setOpenOptionsSheet(false);
+ }}
+ >
+ {getFormattedCount(peerCount)}
+
+ Participants
+
+ )}
+
+ {screenType !== 'hls_live_streaming' ? (
+ {
+ toggleHandRaise();
+ setOpenOptionsSheet(false);
+ }}
+ >
+ {isHandRaised ? : }
+ {isHandRaised ? 'Lower' : 'Raise'} Hand
+
+ ) : null}
{/* {isVideoOn ? (
@@ -144,6 +189,19 @@ export const MwebOptions = ({ elements }) => {
)}
+ {elements?.brb && (
+ {
+ toggleBRB();
+ setOpenOptionsSheet(false);
+ }}
+ >
+
+ Be Right Back
+
+ )}
+
{
setOpenSettingsSheet(true);
@@ -174,6 +232,7 @@ export const MwebOptions = ({ elements }) => {
setOpenOptionsSheet(false);
setIsRecordingLoading(false);
} catch (error) {
+ // @ts-ignore
if (error.message.includes('stream already running')) {
ToastManager.addToast({
title: 'Recording already running',
@@ -181,6 +240,7 @@ export const MwebOptions = ({ elements }) => {
});
} else {
ToastManager.addToast({
+ // @ts-ignore
title: error.message,
variant: 'error',
});
@@ -206,13 +266,13 @@ export const MwebOptions = ({ elements }) => {
-
+
{openModals.has(MODALS.MUTE_ALL) && (
- updateState(MODALS.MUTE_ALL, value)} isMobile />
+ updateState(MODALS.MUTE_ALL, value)} isMobile />
)}
{openModals.has(MODALS.CHANGE_NAME) && (
updateState(MODALS.CHANGE_NAME, value)}
+ onOpenChange={(value: boolean) => updateState(MODALS.CHANGE_NAME, value)}
openParentSheet={() => setOpenOptionsSheet(true)}
/>
)}
@@ -248,6 +308,7 @@ export const MwebOptions = ({ elements }) => {
setShowRecordingOn(false);
} catch (error) {
ToastManager.addToast({
+ // @ts-ignore
title: error.message,
variant: 'error',
});
diff --git a/packages/roomkit-react/src/Prebuilt/components/Notifications/Notifications.jsx b/packages/roomkit-react/src/Prebuilt/components/Notifications/Notifications.jsx
index f92686e8b5..b1a44fd221 100644
--- a/packages/roomkit-react/src/Prebuilt/components/Notifications/Notifications.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/Notifications/Notifications.jsx
@@ -1,10 +1,11 @@
/* eslint-disable no-case-declarations */
-import React, { useEffect } from 'react';
-import { useNavigate } from 'react-router-dom';
+import React, { useCallback, useEffect } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
import {
HMSNotificationTypes,
HMSRoomState,
selectRoomState,
+ useCustomEvent,
useHMSNotifications,
useHMSStore,
} from '@100mslive/react-sdk';
@@ -20,19 +21,31 @@ import { ReconnectNotifications } from './ReconnectNotifications';
import { TrackBulkUnmuteModal } from './TrackBulkUnmuteModal';
import { TrackNotifications } from './TrackNotifications';
import { TrackUnmuteModal } from './TrackUnmuteModal';
-import { useIsHeadless, useSubscribedNotifications } from '../AppData/useUISettings';
+import { useIsNotificationDisabled, useSubscribedNotifications } from '../AppData/useUISettings';
+import { useRedirectToLeave } from '../hooks/useRedirectToLeave';
import { getMetadata } from '../../common/utils';
-
+import { ROLE_CHANGE_DECLINED } from '../../common/constants';
export function Notifications() {
const notification = useHMSNotifications();
const navigate = useNavigate();
+ const params = useParams();
const subscribedNotifications = useSubscribedNotifications() || {};
- const isHeadless = useIsHeadless();
const roomState = useHMSStore(selectRoomState);
const updateRoomLayoutForRole = useUpdateRoomLayout();
+ const isNotificationDisabled = useIsNotificationDisabled();
+ const { redirectToLeave } = useRedirectToLeave();
+
+ const handleRoleChangeDenied = useCallback(request => {
+ ToastManager.addToast({
+ title: `${request.peerName} denied your request to join the ${request.role.name} role`,
+ variant: 'error',
+ });
+ }, []);
+
+ useCustomEvent({ type: ROLE_CHANGE_DECLINED, onEvent: handleRoleChangeDenied });
useEffect(() => {
- if (!notification) {
+ if (!notification || isNotificationDisabled) {
return;
}
switch (notification.type) {
@@ -40,14 +53,14 @@ export function Notifications() {
if (roomState !== HMSRoomState.Connected) {
return;
}
- // Don't toast message when metadata is updated and raiseHand is false.
- // Don't toast message in case of local peer.
+ // Don't show toast message when metadata is updated and raiseHand is false.
+ // Don't show toast message in case of local peer.
const metadata = getMetadata(notification.data?.metadata);
- if (!metadata?.isHandRaised || notification.data.isLocal || isHeadless) return;
+ if (!metadata?.isHandRaised || notification.data.isLocal) return;
console.debug('Metadata updated', notification.data);
if (!subscribedNotifications.METADATA_UPDATED) return;
- ToastBatcher.showToast({ notification });
+ ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
break;
case HMSNotificationTypes.NAME_UPDATED:
console.log(notification.data.id + ' changed their name to ' + notification.data.name);
@@ -69,7 +82,7 @@ export function Notifications() {
@@ -242,8 +238,4 @@ const PeerMetadata = ({ peerId }) => {
const VideoTile = React.memo(Tile);
-const showAudioMuted = ({ hideAudioMute, isAudioMuted }) => {
- return isAudioMuted && !hideAudioMute;
-};
-
export default VideoTile;
diff --git a/packages/roomkit-react/src/Prebuilt/components/conference.jsx b/packages/roomkit-react/src/Prebuilt/components/conference.jsx
index 63a635d26b..c2a13e3711 100644
--- a/packages/roomkit-react/src/Prebuilt/components/conference.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/conference.jsx
@@ -23,14 +23,13 @@ import {
useRoomLayoutConferencingScreen,
useRoomLayoutPreviewScreen,
} from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
-import { useAuthToken, useIsHeadless, useSetAppDataByKey } from './AppData/useUISettings';
-import { APP_DATA, EMOJI_REACTION_TYPE, isAndroid, isIOS, isIPadOS } from '../common/constants';
+import { useAuthToken, useSetAppDataByKey } from './AppData/useUISettings';
+import { APP_DATA, isAndroid, isIOS, isIPadOS } from '../common/constants';
const Conference = () => {
const navigate = useNavigate();
const { roomId, role } = useParams();
- const isHeadless = useIsHeadless();
- const { userName } = useHMSPrebuiltContext();
+ const { userName, endpoints } = useHMSPrebuiltContext();
const screenProps = useRoomLayoutConferencingScreen();
const { isPreviewScreenEnabled } = useRoomLayoutPreviewScreen();
const roomState = useHMSStore(selectRoomState);
@@ -42,11 +41,11 @@ const Conference = () => {
const authTokenInAppData = useAuthToken();
const headerRef = useRef();
const footerRef = useRef();
+ const isMobileDevice = isAndroid || isIOS || isIPadOS;
const dropdownListRef = useRef();
- const performAutoHide = hideControls && (isAndroid || isIOS || isIPadOS);
const [isHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted);
const toggleControls = () => {
- if (dropdownListRef.current?.length === 0) {
+ if (dropdownListRef.current?.length === 0 && isMobileDevice) {
setHideControls(value => !value);
}
};
@@ -58,14 +57,14 @@ const Conference = () => {
clearTimeout(timeout);
timeout = setTimeout(() => {
if (dropdownListRef.current.length === 0) {
- setHideControls(true);
+ setHideControls(isMobileDevice);
}
}, 5000);
}
return () => {
clearTimeout(timeout);
};
- }, [dropdownList, hideControls]);
+ }, [dropdownList, hideControls, isMobileDevice]);
useEffect(() => {
if (!roomId) {
@@ -90,9 +89,7 @@ const Conference = () => {
.join({
userName,
authToken: authTokenInAppData,
- initEndpoint: process.env.REACT_APP_ENV
- ? `https://${process.env.REACT_APP_ENV}-init.100ms.live/init`
- : undefined,
+ initEndpoint: endpoints?.init,
initialSettings: {
isAudioMuted: !isPreviewScreenEnabled,
isVideoMuted: !isPreviewScreenEnabled,
@@ -101,14 +98,7 @@ const Conference = () => {
})
.catch(console.error);
}
- }, [authTokenInAppData, hmsActions, isConnectedToRoom, isPreviewScreenEnabled, roomState, userName]);
-
- useEffect(() => {
- // beam doesn't need to store messages, saves on unnecessary store updates in large calls
- if (isHeadless) {
- hmsActions.ignoreMessageTypes(['chat', EMOJI_REACTION_TYPE]);
- }
- }, [isHeadless, hmsActions]);
+ }, [authTokenInAppData, endpoints?.init, hmsActions, isConnectedToRoom, isPreviewScreenEnabled, roomState, userName]);
useEffect(() => {
return () => {
@@ -117,71 +107,78 @@ const Conference = () => {
}, []);
if (!isConnectedToRoom) {
- return ;
- }
-
- if (isHLSStarted) {
- return ;
+ return ;
}
return (
-
- {!screenProps.hideSections.includes('header') && (
-
-
+ <>
+ {isHLSStarted ? (
+
+
- )}
-
-
-
- {!screenProps.hideSections.includes('footer') && (
+ ) : null}
+
+ {!screenProps.hideSections.includes('header') && (
+
+
+
+ )}
-
+
- )}
-
-
-
-
+ {!screenProps.hideSections.includes('footer') && (
+
+
+
+ )}
+
+
+
+
+ >
);
};
diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useMetadata.jsx b/packages/roomkit-react/src/Prebuilt/components/hooks/useMetadata.jsx
index 71b572f612..7be346a869 100644
--- a/packages/roomkit-react/src/Prebuilt/components/hooks/useMetadata.jsx
+++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useMetadata.jsx
@@ -1,45 +1,36 @@
-import { useCallback, useState } from 'react';
-import { selectLocalPeerID, selectPeerMetadata, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
+import { useCallback } from 'react';
+import {
+ selectLocalPeerID,
+ selectPeerMetadata,
+ useHMSActions,
+ useHMSStore,
+ useHMSVanillaStore,
+} from '@100mslive/react-sdk';
export const useMyMetadata = () => {
const hmsActions = useHMSActions();
const localPeerId = useHMSStore(selectLocalPeerID);
+ const vanillaStore = useHMSVanillaStore();
const metaData = useHMSStore(selectPeerMetadata(localPeerId));
- const [isHandRaised, setHandRaised] = useState(metaData?.isHandRaised || false);
- const [isBRBOn, setBRBOn] = useState(metaData?.isBRBOn || false); // BRB = be right back
const update = async updatedFields => {
try {
- await hmsActions.changeMetadata(Object.assign(metaData, updatedFields));
+ // get current state from store and merge updated fields
+ const currentMetadata = vanillaStore.getState(selectPeerMetadata(localPeerId));
+ await hmsActions.changeMetadata(Object.assign(currentMetadata, updatedFields));
return true;
} catch (error) {
- console.error('failed to update metadata ', metaData, updatedFields);
+ console.error('failed to update metadata ', updatedFields);
}
};
const toggleHandRaise = useCallback(async () => {
- const brbUpdate = !isHandRaised ? false : isBRBOn;
- const success = await update({
- isHandRaised: !isHandRaised,
- isBRBOn: brbUpdate,
- });
- if (success) {
- setBRBOn(brbUpdate);
- setHandRaised(!isHandRaised);
- }
- }, [isHandRaised, isBRBOn]); //eslint-disable-line
+ await update({ isHandRaised: !metaData?.isHandRaised, isBRBOn: false });
+ }, [metaData?.isHandRaised]); //eslint-disable-line
const toggleBRB = useCallback(async () => {
- const handRaiseUpdate = !isBRBOn ? false : isHandRaised;
- const success = await update({
- isHandRaised: handRaiseUpdate,
- isBRBOn: !isBRBOn,
- });
- if (success) {
- setBRBOn(!isBRBOn);
- setHandRaised(handRaiseUpdate);
- }
- }, [isHandRaised, isBRBOn]); //eslint-disable-line
+ await update({ isBRBOn: !metaData?.isBRBOn, isHandRaised: false });
+ }, [metaData?.isBRBOn]); //eslint-disable-line
const setPrevRole = async role => {
await update({
@@ -48,8 +39,8 @@ export const useMyMetadata = () => {
};
return {
- isHandRaised,
- isBRBOn,
+ isHandRaised: !!metaData?.isHandRaised,
+ isBRBOn: !!metaData?.isBRBOn,
metaData,
updateMetaData: update,
toggleHandRaise,
diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useRedirectToLeave.tsx b/packages/roomkit-react/src/Prebuilt/components/hooks/useRedirectToLeave.tsx
new file mode 100644
index 0000000000..ea951681a2
--- /dev/null
+++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useRedirectToLeave.tsx
@@ -0,0 +1,34 @@
+import { useCallback } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useHMSPrebuiltContext } from '../../AppContext';
+// @ts-ignore: No implicit Any
+import { PictureInPicture } from '../PIP/PIPManager';
+// @ts-ignore: No implicit Any
+import { ToastManager } from '../Toast/ToastManager';
+import { useRoomLayoutLeaveScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
+
+export const useRedirectToLeave = () => {
+ const { isLeaveScreenEnabled } = useRoomLayoutLeaveScreen();
+ const { onLeave } = useHMSPrebuiltContext();
+ const params = useParams();
+ const navigate = useNavigate();
+
+ const redirect = useCallback(
+ (timeout = 0) => {
+ setTimeout(() => {
+ const prefix = isLeaveScreenEnabled ? '/leave/' : '/';
+ if (params.role) {
+ navigate(prefix + params.roomId + '/' + params.role);
+ } else {
+ navigate(prefix + params.roomId);
+ }
+ PictureInPicture.stop().catch(() => console.error('stopping pip'));
+ ToastManager.clearAllToast();
+ onLeave?.();
+ }, timeout);
+ },
+ [isLeaveScreenEnabled, navigate, onLeave, params.role, params.roomId],
+ );
+
+ return { redirectToLeave: redirect };
+};
diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx b/packages/roomkit-react/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx
index 015799658e..7b058098b9 100644
--- a/packages/roomkit-react/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useRoleProminencePeers.tsx
@@ -12,7 +12,7 @@ export const useRoleProminencePeers = (prominentRoles: string[], peers: HMSPeer[
if (pinnedTrack) {
if (pinnedTrack.peerId === peer.id) {
acc[0].push(peer);
- } else {
+ } else if (!(isInsetEnabled && peer.isLocal)) {
acc[1].push(peer);
}
return acc;
diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useTileLayout.tsx b/packages/roomkit-react/src/Prebuilt/components/hooks/useTileLayout.tsx
index 82150411d3..17b63f888d 100644
--- a/packages/roomkit-react/src/Prebuilt/components/hooks/useTileLayout.tsx
+++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useTileLayout.tsx
@@ -10,7 +10,6 @@ import {
import { config as cssConfig } from '../../../Theme';
const aspectRatioConfig = { default: [1 / 1, 4 / 3, 16 / 9], mobile: [1 / 1, 3 / 4, 9 / 16] };
-const gap = 8; // gap between flex items
export const usePagesWithTiles = ({ peers, maxTileCount }: { peers: HMSPeer[]; maxTileCount: number }) => {
const vanillaStore = useHMSVanillaStore();
@@ -39,9 +38,11 @@ export const usePagesWithTiles = ({ peers, maxTileCount }: { peers: HMSPeer[]; m
export const useTileLayout = ({
pageList,
maxTileCount,
+ edgeToEdge = false,
}: {
pageList: TrackWithPeerAndDimensions[][];
maxTileCount: number;
+ edgeToEdge?: boolean;
}) => {
const vanillaStore = useHMSVanillaStore();
const [ref, { width, height }] = useMeasure();
@@ -76,6 +77,7 @@ export const useTileLayout = ({
return rowElements;
});
+ const gap = edgeToEdge && isMobile ? 0 : 8; // gap between flex items
const maxHeight = height - (maxRows - 1) * gap;
const maxRowHeight = maxHeight / matrix.length;
const aspectRatios =
@@ -86,25 +88,29 @@ export const useTileLayout = ({
for (const row of matrix) {
let tileWidth = (width - (row.length - 1) * gap) / row.length;
let tileHeight = 0;
- const calcHeights = aspectRatios.map(aR => tileWidth / aR);
- for (const h of calcHeights) {
- if (h < maxRowHeight) {
- if (tileHeight < h) {
- tileHeight = h;
+ if (edgeToEdge) {
+ tileHeight = maxRowHeight;
+ } else {
+ const calcHeights = aspectRatios.map(aR => tileWidth / aR);
+ for (const h of calcHeights) {
+ if (h < maxRowHeight) {
+ if (tileHeight < h) {
+ tileHeight = h;
+ }
}
}
- }
- // tileHeight is not calculated as it could be exceeding the max possible height
- // find the max possible width instead
- if (tileHeight === 0) {
- tileHeight = maxRowHeight;
- const calcWidths = aspectRatios.map(aR => tileHeight * aR);
- tileWidth = 0;
- for (const w of calcWidths) {
- if (w < width) {
- if (tileWidth < w) {
- tileWidth = w;
+ // tileHeight is not calculated as it could be exceeding the max possible height
+ // find the max possible width instead
+ if (tileHeight === 0) {
+ tileHeight = maxRowHeight;
+ const calcWidths = aspectRatios.map(aR => tileHeight * aR);
+ tileWidth = 0;
+ for (const w of calcWidths) {
+ if (w < width) {
+ if (tileWidth < w) {
+ tileWidth = w;
+ }
}
}
}
@@ -116,6 +122,6 @@ export const useTileLayout = ({
}
}
setPagesWithTiles([...pageList]);
- }, [width, height, maxTileCount, pageList, vanillaStore, isMobile]);
+ }, [width, height, maxTileCount, pageList, vanillaStore, isMobile, edgeToEdge]);
return { pagesWithTiles, ref };
};
diff --git a/packages/roomkit-react/src/Prebuilt/components/hooks/useVideoTileLayout.ts b/packages/roomkit-react/src/Prebuilt/components/hooks/useVideoTileLayout.ts
index 70df4b321a..7bb14a63c1 100644
--- a/packages/roomkit-react/src/Prebuilt/components/hooks/useVideoTileLayout.ts
+++ b/packages/roomkit-react/src/Prebuilt/components/hooks/useVideoTileLayout.ts
@@ -5,7 +5,9 @@ type TileContextType = {
hideParticipantNameOnTile?: boolean;
roundedVideoTile?: boolean;
hideAudioMuteOnTile?: boolean;
+ hideAudioLevelOnTile?: boolean;
objectFit?: 'cover' | 'contain';
+ hideMetadataOnTile?: boolean;
};
export const VideoTileContext = React.createContext({
@@ -13,7 +15,9 @@ export const VideoTileContext = React.createContext({
hideParticipantNameOnTile: false,
roundedVideoTile: true,
hideAudioMuteOnTile: false,
+ hideAudioLevelOnTile: false,
objectFit: 'contain',
+ hideMetadataOnTile: false,
});
export const useVideoTileContext = () => {
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/EmbedView.jsx b/packages/roomkit-react/src/Prebuilt/layouts/EmbedView.jsx
index 9ce2e24ae1..922fdeac9d 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/EmbedView.jsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/EmbedView.jsx
@@ -1,7 +1,5 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
- selectLocalPeerID,
- selectLocalPeerRoleName,
selectPeers,
selectPeerScreenSharing,
throwErrorHandler,
@@ -25,21 +23,13 @@ export const EmbedView = () => {
export const EmbebScreenShareView = ({ children }) => {
const peers = useHMSStore(selectPeers);
- const localPeerID = useHMSStore(selectLocalPeerID);
- const localPeerRole = useHMSStore(selectLocalPeerRoleName);
const peerPresenting = useHMSStore(selectPeerScreenSharing);
- const isPresenterFromMyRole = peerPresenting?.roleName?.toLowerCase() === localPeerRole?.toLowerCase();
- const amIPresenting = localPeerID === peerPresenting?.id;
- const showPresenterInSmallTile = amIPresenting || isPresenterFromMyRole;
const [, setActiveScreenSharePeer] = useSetAppDataByKey(APP_DATA.activeScreensharePeerId);
const smallTilePeers = useMemo(() => {
const smallTilePeers = peers.filter(peer => peer.id !== peerPresenting?.id);
- if (showPresenterInSmallTile && peerPresenting) {
- smallTilePeers.unshift(peerPresenting); // put presenter on first page
- }
return smallTilePeers;
- }, [peers, peerPresenting, showPresenterInSmallTile]);
+ }, [peers, peerPresenting]);
useEffect(() => {
setActiveScreenSharePeer(peerPresenting?.id);
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx b/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
index 33c1023f80..dc6dc8e4e4 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/HLSView.jsx
@@ -1,9 +1,9 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
-import { useFullscreen, useToggle } from 'react-use';
+import { useFullscreen, useMedia, useToggle } from 'react-use';
import { HLSPlaybackState, HMSHLSPlayer, HMSHLSPlayerEvents } from '@100mslive/hls-player';
import screenfull from 'screenfull';
import { selectAppData, selectHLSState, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
-import { ExpandIcon, RadioIcon, ShrinkIcon } from '@100mslive/react-icons';
+import { ColoredHandIcon, ExpandIcon, RadioIcon, ShrinkIcon } from '@100mslive/react-icons';
import { HlsStatsOverlay } from '../components/HlsStatsOverlay';
import { HMSVideoPlayer } from '../components/HMSVideo';
import { FullScreenButton } from '../components/HMSVideo/FullscreenButton';
@@ -14,7 +14,7 @@ import { IconButton } from '../../IconButton';
import { Box, Flex } from '../../Layout';
import { Loading } from '../../Loading';
import { Text } from '../../Text';
-import { useTheme } from '../../Theme';
+import { config, useTheme } from '../../Theme';
import { Tooltip } from '../../Tooltip';
import { APP_DATA, EMOJI_REACTION_TYPE } from '../common/constants';
@@ -26,7 +26,8 @@ const HLSView = () => {
const hlsState = useHMSStore(selectHLSState);
const enablHlsStats = useHMSStore(selectAppData(APP_DATA.hlsStats));
const hmsActions = useHMSActions();
- const { themeType } = useTheme();
+ const { themeType, theme } = useTheme();
+ const [streamEnded, setStreamEnded] = useState(false);
let [hlsStatsState, setHlsStatsState] = useState(null);
const hlsUrl = hlsState.variants[0]?.url;
const [availableLayers, setAvailableLayers] = useState([]);
@@ -37,6 +38,12 @@ const HLSView = () => {
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 isMobile = useMedia(config.media.md);
const isFullScreen = useFullscreen(hlsViewRef, show, {
onClose: () => toggle(false),
});
@@ -58,6 +65,19 @@ const HLSView = () => {
};
}, []);
+ useEffect(() => {
+ const videoElem = videoRef.current;
+ const setStreamEndedCallback = () => {
+ setStreamEnded(true);
+ // no point keeping the callback attached once the streaming is ended
+ videoElem?.removeEventListener('ended', setStreamEndedCallback);
+ };
+ videoElem?.addEventListener('ended', setStreamEndedCallback);
+ return () => {
+ videoElem?.removeEventListener('ended', setStreamEndedCallback);
+ };
+ }, [hlsUrl]);
+
/**
* initialize HMSHLSPlayer and add event listeners.
*/
@@ -82,7 +102,7 @@ const HLSView = () => {
const parsedPayload = parsePayload(payload);
switch (parsedPayload.type) {
case EMOJI_REACTION_TYPE:
- window.showFlyingEmoji(parsedPayload?.emojiId, parsedPayload?.senderId);
+ window.showFlyingEmoji?.({ emojiId: parsedPayload?.emojiId, senderId: parsedPayload?.senderId });
break;
default: {
const toast = {
@@ -167,6 +187,43 @@ const HLSView = () => {
hmsActions.setAppData(APP_DATA.hlsStats, !enablHlsStats);
};
+ useEffect(() => {
+ if (controlsVisible && isFullScreen && !qualityDropDownOpen) {
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current);
+ }
+ controlsTimerRef.current = setTimeout(() => {
+ setControlsVisible(false);
+ }, 5000);
+ }
+ if (!isFullScreen && controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current);
+ }
+ return () => {
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current);
+ }
+ };
+ }, [controlsVisible, isFullScreen, qualityDropDownOpen]);
+
+ const onHoverHandler = useCallback(
+ event => {
+ if (event.type === 'mouseenter' || qualityDropDownOpen) {
+ setControlsVisible(true);
+ return;
+ }
+ if (event.type === 'mouseleave') {
+ setControlsVisible(false);
+ } else if (isFullScreen && !controlsVisible && event.type === 'mousemove') {
+ setControlsVisible(true);
+ if (controlsTimerRef.current) {
+ clearTimeout(controlsTimerRef.current);
+ }
+ }
+ },
+ [controlsVisible, isFullScreen, qualityDropDownOpen],
+ );
+
return (
{
{hlsStatsState?.url && enablHlsStats ? (
) : null}
- {hlsUrl && hlsState.running ? (
+ {hlsUrl && !streamEnded ? (
{
width: '100%',
margin: '0 auto',
height: '100%',
- background: 'linear-gradient(180deg, rgba(0, 0, 0, 0.00) 0%, #000 100%)',
}}
>
@@ -203,91 +259,114 @@ const HLSView = () => {
)}
-
+
-
-
- {
- isPaused ? await hlsPlayer?.play() : hlsPlayer?.pause();
- }}
- isPaused={isPaused}
- />
-
-
- {
- await hlsPlayer.seekToLivePosition();
- setIsVideoLive(true);
- }}
- key="jump-to-live_btn"
- data-testid="jump-to-live_btn"
- >
-
-
-
-
- {isVideoLive ? 'LIVE' : 'GO LIVE'}
-
-
-
-
-
-
-
- {availableLayers.length > 0 ? (
-
- ) : null}
- {isFullScreenSupported ? (
- : }
+ {!isMobile && (
+
+
+ {
+ isPaused ? await hlsPlayer?.play() : hlsPlayer?.pause();
+ }}
+ isPaused={isPaused}
/>
- ) : null}
-
-
+
+
+ {
+ await hlsPlayer.seekToLivePosition();
+ setIsVideoLive(true);
+ }}
+ key="jump-to-live_btn"
+ data-testid="jump-to-live_btn"
+ >
+
+
+
+
+ {isVideoLive ? 'LIVE' : 'GO LIVE'}
+
+
+
+
+
+
+
+ {availableLayers.length > 0 ? (
+
+ ) : null}
+ {isFullScreenSupported ? (
+ : }
+ />
+ ) : null}
+
+
+ )}
) : (
-
+ {streamEnded ? : }
- Stream yet to start
+ {streamEnded ? 'Stream has ended' : 'Stream yet to start'}
- Sit back and relax
+ {streamEnded ? 'Have a nice day!' : 'Sit back and relax'}
)}
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
index 35e72fa59d..ebeee18e7c 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/SidePane.tsx
@@ -2,29 +2,35 @@ import React from 'react';
import { useMedia } from 'react-use';
import { ConferencingScreen } from '@100mslive/types-prebuilt';
import { selectAppData, selectVideoTrackByPeerID, useHMSStore } from '@100mslive/react-sdk';
-// @ts-ignore: No implicit Any
-import { Chat } from '../components/Chat/Chat';
-// @ts-ignore: No implicit Any
-import { ParticipantList } from '../components/Footer/ParticipantList';
+import { SidePaneTabs } from '../components/SidePaneTabs';
// @ts-ignore: No implicit Any
import { StreamingLanding } from '../components/Streaming/StreamingLanding';
+import { TileCustomisationProps } from '../components/VideoLayouts/GridLayout';
// @ts-ignore: No implicit Any
import VideoTile from '../components/VideoTile';
import { Box, Flex } from '../../Layout';
import { config as cssConfig } from '../../Theme';
+import { useRoomLayoutConferencingScreen } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
// @ts-ignore: No implicit Any
import { APP_DATA, SIDE_PANE_OPTIONS } from '../common/constants';
-const SidePane = ({ screenType }: { screenType: keyof ConferencingScreen }) => {
+const SidePane = ({
+ screenType,
+ tileProps,
+ hideControls = false,
+}: {
+ screenType: keyof ConferencingScreen;
+ tileProps: TileCustomisationProps;
+ hideControls: boolean;
+}) => {
const isMobile = useMedia(cssConfig.media.md);
const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane));
const activeScreensharePeerId = useHMSStore(selectAppData(APP_DATA.activeScreensharePeerId));
const trackId = useHMSStore(selectVideoTrackByPeerID(activeScreensharePeerId))?.id;
+ const { elements } = useRoomLayoutConferencingScreen();
let ViewComponent;
- if (sidepane === SIDE_PANE_OPTIONS.PARTICIPANTS) {
- ViewComponent = ;
- } else if (sidepane === SIDE_PANE_OPTIONS.CHAT) {
- ViewComponent = ;
+ if (sidepane === SIDE_PANE_OPTIONS.PARTICIPANTS || sidepane === SIDE_PANE_OPTIONS.CHAT) {
+ ViewComponent = ;
} else if (sidepane === SIDE_PANE_OPTIONS.STREAMING) {
ViewComponent = ;
}
@@ -32,7 +38,15 @@ const SidePane = ({ screenType }: { screenType: keyof ConferencingScreen }) => {
return null;
}
- const mwebStreamingChat = isMobile && sidepane === SIDE_PANE_OPTIONS.CHAT;
+ const tileLayout = {
+ hideParticipantNameOnTile: tileProps?.hide_participant_name_on_tile,
+ roundedVideoTile: tileProps?.rounded_video_tile,
+ hideAudioMuteOnTile: tileProps?.hide_audio_mute_on_tile,
+ hideMetadataOnTile: tileProps?.hide_metadata_on_tile,
+ objectFit: tileProps?.video_object_fit,
+ };
+
+ const mwebStreamingChat = isMobile && sidepane === SIDE_PANE_OPTIONS.CHAT && elements?.chat?.is_overlay;
return (
{
width="100%"
height={225}
rootCSS={{ p: 0, alignSelf: 'start', flexShrink: 0 }}
- objectFit="contain"
+ {...tileLayout}
/>
)}
{!!ViewComponent && (
diff --git a/packages/roomkit-react/src/Prebuilt/layouts/VideoStreamingSection.tsx b/packages/roomkit-react/src/Prebuilt/layouts/VideoStreamingSection.tsx
index 24e647b8d5..a798d23976 100644
--- a/packages/roomkit-react/src/Prebuilt/layouts/VideoStreamingSection.tsx
+++ b/packages/roomkit-react/src/Prebuilt/layouts/VideoStreamingSection.tsx
@@ -33,9 +33,11 @@ const HLSView = React.lazy(() => import('./HLSView'));
export const VideoStreamingSection = ({
screenType,
elements,
+ hideControls = false,
}: {
screenType: keyof ConferencingScreen;
elements: DefaultConferencingScreen_Elements | HLSLiveStreamingScreen_Elements;
+ hideControls: boolean;
}) => {
const localPeerRole = useHMSStore(selectLocalPeerRoleName);
// const { whiteboardOwner: whiteboardShared } = useWhiteboardMetadata();
@@ -82,7 +84,12 @@ export const VideoStreamingSection = ({
}}
>
{ViewComponent}
-
+
);
diff --git a/packages/roomkit-react/src/Prebuilt/plugins/FlyingEmoji.jsx b/packages/roomkit-react/src/Prebuilt/plugins/FlyingEmoji.jsx
index 7cb87d4b94..4c2b62cc42 100644
--- a/packages/roomkit-react/src/Prebuilt/plugins/FlyingEmoji.jsx
+++ b/packages/roomkit-react/src/Prebuilt/plugins/FlyingEmoji.jsx
@@ -1,9 +1,16 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useMedia } from 'react-use';
-import { selectLocalPeerID, selectPeerNameByID, useHMSStore, useHMSVanillaStore } from '@100mslive/react-sdk';
+import {
+ selectLocalPeerID,
+ selectPeerNameByID,
+ useCustomEvent,
+ useHMSStore,
+ useHMSVanillaStore,
+} from '@100mslive/react-sdk';
import { Box, Flex } from '../../Layout';
import { Text } from '../../Text';
import { config as cssConfig, keyframes } from '../../Theme';
+import { EMOJI_REACTION_TYPE } from '../common/constants';
let emojiCount = 1;
@@ -42,7 +49,7 @@ export function FlyingEmoji() {
const startingPoints = useMemo(() => getStartingPoints(isMobile), [isMobile]);
const showFlyingEmoji = useCallback(
- (emojiId, senderId) => {
+ ({ emojiId, senderId }) => {
if (!emojiId || !senderId || document.hidden) {
return;
}
@@ -67,6 +74,11 @@ export function FlyingEmoji() {
[localPeerId, vanillaStore, startingPoints],
);
+ useCustomEvent({
+ type: EMOJI_REACTION_TYPE,
+ onEvent: showFlyingEmoji,
+ });
+
useEffect(() => {
window.showFlyingEmoji = showFlyingEmoji;
}, [showFlyingEmoji]);
diff --git a/packages/roomkit-react/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx b/packages/roomkit-react/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx
index 2040c2a619..c7a0a4e017 100644
--- a/packages/roomkit-react/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx
+++ b/packages/roomkit-react/src/Prebuilt/plugins/VirtualBackground/VirtualBackground.jsx
@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { HMSVirtualBackgroundTypes } from '@100mslive/hms-virtual-background';
import {
- selectIsAllowedToPublish,
+ selectIsLocalVideoEnabled,
selectIsLocalVideoPluginPresent,
selectLocalPeerRole,
selectLocalVideoTrackID,
@@ -23,8 +23,8 @@ export const VirtualBackground = ({
}) => {
const pluginRef = useRef(null);
const hmsActions = useHMSActions();
- const isAllowedToPublish = useHMSStore(selectIsAllowedToPublish);
const role = useHMSStore(selectLocalPeerRole);
+ const isVideoOn = useHMSStore(selectIsLocalVideoEnabled);
const [isVBLoading, setIsVBLoading] = useState(false);
const [isVBSupported, setIsVBSupported] = useState(false);
const [isVBOn, setIsVBOn] = useState(false);
@@ -69,7 +69,7 @@ export const VirtualBackground = ({
}
}
- if (!isAllowedToPublish.video || !isVBSupported) {
+ if (!isVBSupported || !isVideoOn) {
return null;
}
if (asActionTile) {
diff --git a/packages/roomkit-react/src/Prebuilt/provider/roomLayoutProvider/hooks/useFetchRoomLayout.ts b/packages/roomkit-react/src/Prebuilt/provider/roomLayoutProvider/hooks/useFetchRoomLayout.ts
index 9af36a7790..2a01083d4a 100644
--- a/packages/roomkit-react/src/Prebuilt/provider/roomLayoutProvider/hooks/useFetchRoomLayout.ts
+++ b/packages/roomkit-react/src/Prebuilt/provider/roomLayoutProvider/hooks/useFetchRoomLayout.ts
@@ -29,7 +29,7 @@ export type useFetchRoomLayoutResponse = {
};
export const useFetchRoomLayout = ({
- endpoint = 'https://api.100ms.live/v2/layouts/ui',
+ endpoint = '',
authToken = '',
}: useFetchRoomLayoutProps): useFetchRoomLayoutResponse => {
const [layout, setLayout] = useState(undefined);
@@ -51,7 +51,7 @@ export const useFetchRoomLayout = ({
}
isFetchInProgress.current = true;
try {
- const resp = await fetchWithRetry(endpoint, {
+ const resp = await fetchWithRetry(endpoint || 'https://api.100ms.live/v2/layouts/ui', {
headers: {
Authorization: `Bearer ${authToken}`,
},
diff --git a/packages/roomkit-react/src/Prebuilt/services/FeatureFlags.jsx b/packages/roomkit-react/src/Prebuilt/services/FeatureFlags.jsx
index e39029cdd4..47be94efaf 100644
--- a/packages/roomkit-react/src/Prebuilt/services/FeatureFlags.jsx
+++ b/packages/roomkit-react/src/Prebuilt/services/FeatureFlags.jsx
@@ -8,7 +8,6 @@ export class FeatureFlags {
process.env.REACT_APP_ENABLE_WHITEBOARD &&
process.env.REACT_APP_PUSHER_APP_KEY &&
process.env.REACT_APP_PUSHER_AUTHENDPOINT;
- static enableBeamSpeakersLogging = process.env.REACT_APP_ENABLE_BEAM_SPEAKERS_LOGGING === 'true';
static init(roomId) {
if (!window.HMS) {
diff --git a/packages/roomkit-react/src/VideoTile/StyledVideoTile.tsx b/packages/roomkit-react/src/VideoTile/StyledVideoTile.tsx
index f10acb9437..d7f8c8c822 100644
--- a/packages/roomkit-react/src/VideoTile/StyledVideoTile.tsx
+++ b/packages/roomkit-react/src/VideoTile/StyledVideoTile.tsx
@@ -14,6 +14,7 @@ const Container = styled('div', {
width: '100%',
height: '100%',
position: 'relative',
+ overflow: 'hidden',
borderRadius: '$2',
display: 'flex',
justifyContent: 'center',
diff --git a/yarn.lock b/yarn.lock
index 0a4e9f9be9..bea4183117 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10543,10 +10543,10 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-hls.js@^1.3.0:
- version "1.4.10"
- resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.4.10.tgz#3feac40f21a558453b243b5b926b7317e70624e1"
- integrity sha512-wAVSj4Fm2MqOHy5+BlYnlKxXvJlv5IuZHjlzHu18QmjRzSDFQiUDWdHs5+NsFMQrgKEBwuWDcyvaMC9dUzJ5Uw==
+hls.js@1.4.3:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/hls.js/-/hls.js-1.4.3.tgz#9326a680f5905631f86344d0ae155b5ef9d1bad7"
+ integrity sha512-EE1MjIYDNO+ynbmCpAWfhUwQpyG8gUcKKuGDGgYgfRmW/g+inQUQ8sVVVY5WZaCxEGxDMGLbXhXGepkmDIMvdw==
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"