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 && ( - - )} - - + + + 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}