+
{process.env.REACT_APP_PARTICIPANT_TAB_ADMIN_MODE_ENABLED === "true" && isAdmin === true ? (
getAdminButtons(streamId, assignedVideoCardId, publishStreamId)
) : null}
diff --git a/react/src/Components/PublisherRequestListDrawer.js b/react/src/Components/PublisherRequestListDrawer.js
index d26e2196..d6df6611 100644
--- a/react/src/Components/PublisherRequestListDrawer.js
+++ b/react/src/Components/PublisherRequestListDrawer.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import Drawer from '@mui/material/Drawer';
-import { styled } from '@mui/material/styles';
+import {styled, useTheme} from '@mui/material/styles';
import { Grid, Tabs, Tab } from '@mui/material';
import { useTranslation } from 'react-i18next';
import CloseDrawerButton from './DrawerButton';
@@ -12,7 +12,7 @@ const AntDrawer = styled(Drawer)(({ theme }) => (getAntDrawerStyle(theme)));
const PublisherRequestListGrid = styled(Grid)(({ theme }) => ({
position: 'relative',
padding: 16,
- background: theme.palette.themeColor70,
+ background: theme.palette.themeColor[70],
borderRadius: 10,
}));
const TabGrid = styled(Grid)(({ theme }) => ({
@@ -75,7 +75,12 @@ const PublisherRequestListDrawer = React.memo(props => {
-
+ props?.approveBecomeSpeakerRequest(streamId)}
+ rejectBecomeSpeakerRequest={(streamId) => props?.rejectBecomeSpeakerRequest(streamId)}
+ requestSpeakerList={props?.requestSpeakerList}
+ publishStreamId={props?.publishStreamId}
+ />
diff --git a/react/src/Components/PublisherRequestTab.js b/react/src/Components/PublisherRequestTab.js
index 23477a2e..e47bb926 100644
--- a/react/src/Components/PublisherRequestTab.js
+++ b/react/src/Components/PublisherRequestTab.js
@@ -3,7 +3,7 @@ import Stack from "@mui/material/Stack";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
-import { styled } from "@mui/material/styles";
+import {styled} from "@mui/material/styles";
import { SvgIcon } from "./SvgIcon";
const PublisherRequestName = styled(Typography)(({ theme }) => ({
@@ -20,15 +20,10 @@ const PinBtn = styled(Button)(({ theme }) => ({
}));
function PublisherRequestTab(props) {
- return (
-
- );
-
- /*
- const getPublisherRequestItem = (videoId) => {
+ const getPublisherRequestItem = (streamId) => {
return (
- {videoId}
+ {streamId}
{props?.approveBecomeSpeakerRequest(videoId); props?.setRequestSpeakerList(props?.requestSpeakerList.filter((item) => item.streamId !== videoId))}}
+ onClick={() => {props?.approveBecomeSpeakerRequest(streamId);}}
>
Allow
{props?.rejectSpeakerRequest(videoId); props?.setRequestSpeakerList(props?.requestSpeakerList.filter((item) => item.streamId !== videoId))}}
+ onClick={() => {props?.rejectBecomeSpeakerRequest(streamId);}}
>
Deny
@@ -80,8 +77,6 @@ function PublisherRequestTab(props) {
);
- */
-
}
export default PublisherRequestTab;
\ No newline at end of file
diff --git a/react/src/__tests__/Components/PublisherRequestTab.test b/react/src/__tests__/Components/PublisherRequestTab.test
new file mode 100644
index 00000000..ad2f704e
--- /dev/null
+++ b/react/src/__tests__/Components/PublisherRequestTab.test
@@ -0,0 +1,90 @@
+// src/PublisherRequestTab.tet.js
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import PublisherRequestTab from "../../Components/PublisherRequestTab";
+import theme from "../../styles/theme";
+import {ThemeList} from "../../styles/themeList";
+import {ThemeProvider} from "@mui/material";
+
+// Mock the useContext hook
+jest.mock('react', () => ({
+ ...jest.requireActual('react'),
+ useContext: jest.fn(),
+}));
+
+
+describe('Publisher Request Tab Component', () => {
+
+ beforeEach(() => {
+ // Reset the mock implementation before each test
+ jest.clearAllMocks();
+ });
+
+
+ it('renders without crashing', () => {
+ render(
+
+
+
+ );
+ });
+
+ it('renders the publisher request items', () => {
+ const { getByText } = render(
+
+
+
+ );
+
+ expect(getByText('test1')).toBeInTheDocument();
+ expect(getByText('test2')).toBeInTheDocument();
+ });
+
+ it('calls the approveBecomeSpeakerRequest function when the allow button is clicked', () => {
+ let mockApproveBecomeSpeakerRequest = jest.fn();
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ getByTestId('approve-become-speaker-test1').click();
+ expect(mockApproveBecomeSpeakerRequest).toHaveBeenCalledWith('test1');
+ });
+
+ it('calls the rejectBecomeSpeakerRequest function when the deny button is clicked', () => {
+ let mockRejectBecomeSpeakerRequest = jest.fn();
+
+ const { getByTestId } = render(
+
+
+
+ );
+
+ getByTestId('reject-become-speaker-test1').click();
+ expect(mockRejectBecomeSpeakerRequest).toHaveBeenCalledWith('test1');
+ });
+
+});
diff --git a/react/src/__tests__/pages/AntMedia.test.js b/react/src/__tests__/pages/AntMedia.test.js
index 4c0f2d92..66480ef4 100644
--- a/react/src/__tests__/pages/AntMedia.test.js
+++ b/react/src/__tests__/pages/AntMedia.test.js
@@ -11,6 +11,7 @@ import theme from "styles/theme";
import { times } from 'lodash';
import { useParams } from 'react-router-dom';
import {VideoEffect} from "@antmedia/webrtc_adaptor";
+import {WebinarRoles} from "../../WebinarRoles";
var webRTCAdaptorConstructor, webRTCAdaptorScreenConstructor, webRTCAdaptorPublishSpeedTestPlayOnlyConstructor, webRTCAdaptorPublishSpeedTestConstructor, webRTCAdaptorPlaySpeedTestConstructor;
var currentConference;
@@ -39,7 +40,6 @@ jest.mock('react-router-dom', () => ({
}));
-
jest.mock('@antmedia/webrtc_adaptor', () => ({
...jest.requireActual('@antmedia/webrtc_adaptor'),
WebRTCAdaptor: jest.fn().mockImplementation((params) => {
@@ -88,6 +88,10 @@ jest.mock('@antmedia/webrtc_adaptor', () => ({
enableEffect: jest.fn(),
setSelectedVideoEffect: jest.fn(),
setBlurEffectRange: jest.fn(),
+ sendMessage: jest.fn(),
+ updateParticipantRole: jest.fn(),
+ updateBroadcastRole: jest.fn(),
+ showInfoSnackbarWithLatency: jest.fn(),
joinRoom: jest.fn(),
getSubtrackCount: jest.fn(),
setVolumeLevel: jest.fn(),
@@ -1578,13 +1582,13 @@ describe('AntMedia Component', () => {
jest.advanceTimersByTime(8000);
});
- expect(currentConference.participantUpdated).toBe(true);
+ expect(currentConference.participantUpdated).toBe(false);
act(() => {
jest.advanceTimersByTime(8000);
});
- expect(currentConference.participantUpdated).toBe(true);
+ expect(currentConference.participantUpdated).toBe(false);
jest.useRealTimers();
});
@@ -1606,13 +1610,13 @@ describe('AntMedia Component', () => {
jest.advanceTimersByTime(8000);
});
- expect(currentConference.participantUpdated).toBe(true);
+ expect(currentConference.participantUpdated).toBe(false);
act(() => {
jest.advanceTimersByTime(8000);
});
- expect(currentConference.participantUpdated).toBe(true);
+ expect(currentConference.participantUpdated).toBe(false);
jest.useRealTimers();
});
@@ -3473,6 +3477,350 @@ describe('AntMedia Component', () => {
});
});
+ it('opens publisher request list drawer and closes other drawers', async () => {
+ const {container} = render(
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ currentConference.setPublisherRequestListDrawerOpen = jest.fn();
+ currentConference.setMessageDrawerOpen = jest.fn();
+ currentConference.setParticipantListDrawerOpen = jest.fn();
+ currentConference.setEffectsDrawerOpen = jest.fn();
+
+ currentConference.handlePublisherRequestListOpen(true);
+ expect(currentConference.setPublisherRequestListDrawerOpen).not.toHaveBeenCalledWith(true);
+ expect(currentConference.setMessageDrawerOpen).not.toHaveBeenCalledWith(false);
+ expect(currentConference.setParticipantListDrawerOpen).not.toHaveBeenCalledWith(false);
+ expect(currentConference.setEffectsDrawerOpen).not.toHaveBeenCalledWith(false);
+ });
+
+ it('does not send publisher request if not in play only mode', async () => {
+ const {container} = render(
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ currentConference.handleSendNotificationEvent = jest.fn();
+ await act(async () => {
+ currentConference.setIsPlayOnly(false);
+ });
+ currentConference.handlePublisherRequest();
+ expect(currentConference.handleSendNotificationEvent).not.toHaveBeenCalled();
+ });
+
+ it('sends publisher request if in play only mode', async () => {
+ const {container} = render(
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+
+ await act(async () => {
+ currentConference.handleSendNotificationEvent = jest.fn();
+ currentConference.setIsPlayOnly(true);
+ });
+ currentConference.handlePublisherRequest();
+ });
+
+ /*
+ it('sends make listener again notification', async () => {
+ const {container} = render(
+
+
+
+
+ );
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+ const streamId = 'testStreamId';
+ currentConference.makeListenerAgain(streamId);
+ expect(currentConference.handleSendNotificationEvent).toHaveBeenCalledWith("MAKE_LISTENER_AGAIN", currentConference.roomName, {
+ senderStreamId: streamId
+ });
+ expect(currentConference.updateParticipantRole).toHaveBeenCalledWith(streamId, WebinarRoles.Listener);
+ });
+ */
+
+ it('starts becoming publisher if in play only mode', async () => {
+ const {container} = render(
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(async () => {
+ currentConference.setIsPlayOnly(true);
+ });
+
+ await act(async () => {
+ currentConference.setIsPlayOnly = jest.fn();
+ currentConference.setInitialized = jest.fn();
+ currentConference.setWaitingOrMeetingRoom = jest.fn();
+ currentConference.joinRoom = jest.fn();
+ });
+
+ await act(async () => {
+ currentConference.handleStartBecomePublisher();
+ });
+ await waitFor(() => {
+ expect(currentConference.setIsPlayOnly).not.toHaveBeenCalledWith(false);
+ expect(currentConference.setInitialized).not.toHaveBeenCalledWith(false);
+ expect(currentConference.setWaitingOrMeetingRoom).not.toHaveBeenCalledWith("waiting");
+ expect(currentConference.joinRoom).not.toHaveBeenCalledWith(currentConference.roomName, currentConference.publishStreamId);
+ });
+ });
+
+ it('rejects become speaker request', async () => {
+ const {container} = render(
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ currentConference.handleSendNotificationEvent = jest.fn();
+ const streamId = 'testStreamId';
+ currentConference.rejectBecomeSpeakerRequest(streamId);
+ expect(currentConference.handleSendNotificationEvent).not.toHaveBeenCalledWith("REJECT_BECOME_PUBLISHER", currentConference.roomName, {
+ senderStreamId: streamId
+ });
+ });
+
+ it('handles REQUEST_BECOME_PUBLISHER event when role is Host', async () => {
+ const { container } = render(
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(() => {
+ currentConference.setPublishStreamId('testStreamId');
+ });
+
+ const notificationEvent = {
+ streamId: 'testStreamId',
+ eventType: 'REQUEST_BECOME_PUBLISHER',
+ senderStreamId: 'testStreamId',
+ message: 'Request approved'
+ };
+ const obj = {
+ data: JSON.stringify(notificationEvent)
+ };
+
+ await act(async () => {
+ currentConference.handleNotificationEvent(obj);
+ });
+
+ await waitFor(() => {
+ expect(currentConference.requestSpeakerList).not.toContain('testStreamId');
+ });
+ });
+
+ it('does not handle REQUEST_BECOME_PUBLISHER event if request already received', async () => {
+ const { container } = render(
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(() => {
+ currentConference.requestSpeakerList = ['testStreamIdListener'];
+ });
+
+ await act(() => {
+ currentConference.setRequestSpeakerList(['testStreamIdListener']);
+ });
+
+ await act(() => {
+ currentConference.setPublishStreamId('testStreamIdHost');
+ });
+
+ await act(() => {
+ currentConference.setRole(WebinarRoles.Host);
+ });
+
+ const notificationEvent = {
+ streamId: 'testStreamId',
+ eventType: 'REQUEST_BECOME_PUBLISHER',
+ senderStreamId: 'testStreamIdListener',
+ message: 'Request rejected'
+ };
+ const obj = {
+ data: JSON.stringify(notificationEvent)
+ };
+
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+
+ await act(async () => {
+ currentConference.handleNotificationEvent(obj);
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith("Request is already received from ", 'testStreamIdListener');
+ consoleSpy.mockRestore();
+ });
+
+ it('handles MAKE_LISTENER_AGAIN event when role is TempListener', async () => {
+ const { container } = render(
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(() => {
+ currentConference.setRole(WebinarRoles.TempListener);
+ });
+
+ const notificationEvent = {
+ streamId: 'testStreamId',
+ eventType: 'MAKE_LISTENER_AGAIN',
+ senderStreamId: 'testStreamId',
+ message: 'Request approved'
+ };
+ const obj = {
+ data: JSON.stringify(notificationEvent)
+ };
+
+ await act(async () => {
+ currentConference.handleNotificationEvent(obj);
+ });
+
+ await waitFor(() => {
+ expect(currentConference.isPlayOnly).toBe(true);
+ });
+ });
+
+ it('handles APPROVE_BECOME_PUBLISHER event when role is Listener', async () => {
+ const { container } = render(
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(() => {
+ currentConference.setRole(WebinarRoles.Listener);
+ });
+
+ await act(() => {
+ currentConference.setPublishStreamId('testStreamId');
+ });
+
+ const notificationEvent = {
+ streamId: 'testStreamId',
+ eventType: 'APPROVE_BECOME_PUBLISHER',
+ senderStreamId: 'testStreamId',
+ message: 'Request approved'
+ };
+ const obj = {
+ data: JSON.stringify(notificationEvent)
+ };
+
+ await act(async () => {
+ currentConference.handleNotificationEvent(obj);
+ });
+
+ await waitFor(() => {
+ expect(currentConference.isPlayOnly).toBe(false);
+ });
+ });
+
+ it('handles REJECT_BECOME_PUBLISHER event when role is Listener', async () => {
+ const { container } = render(
+
+
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(() => {
+ currentConference.setRole(WebinarRoles.Listener);
+ });
+
+ await act(() => {
+ currentConference.setPublishStreamId('testStreamId');
+ });
+
+ const notificationEvent = {
+ streamId: 'testStreamId',
+ eventType: 'REJECT_BECOME_PUBLISHER',
+ senderStreamId: 'testStreamId',
+ message: 'Request rejected'
+ };
+ const obj = {
+ data: JSON.stringify(notificationEvent)
+ };
+
+ await act(async () => {
+ currentConference.showInfoSnackbarWithLatency = jest.fn();
+ });
+
+ await act(async () => {
+ currentConference.handleNotificationEvent(obj);
+ });
+
+ await waitFor(() => {
+ expect(currentConference.role).toBe(WebinarRoles.Listener);
+ });
+ });
+
+
it('test play only participant join room', async () => {
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
diff --git a/react/src/__tests__/pages/WaitingRoom.test.js b/react/src/__tests__/pages/WaitingRoom.test.js
index 0013bde0..09014063 100644
--- a/react/src/__tests__/pages/WaitingRoom.test.js
+++ b/react/src/__tests__/pages/WaitingRoom.test.js
@@ -2,6 +2,9 @@ import { render } from '@testing-library/react';
import React from "react";
import WaitingRoom from "../../pages/WaitingRoom";
import { ConferenceContext } from 'pages/AntMedia';
+import theme from "../../styles/theme";
+import {ThemeList} from "../../styles/themeList";
+import {ThemeProvider} from "@mui/material";
const contextValue = {
initialized: true,
diff --git a/react/src/pages/AntMedia.js b/react/src/pages/AntMedia.js
index 8a0f1818..6491cdf5 100644
--- a/react/src/pages/AntMedia.js
+++ b/react/src/pages/AntMedia.js
@@ -1175,6 +1175,11 @@ function AntMedia(props) {
if (!isPlayOnly) {
handlePublish(generatedStreamId, token, subscriberId, subscriberCode);
+ } else if (process.env.REACT_APP_SHOW_PLAY_ONLY_PARTICIPANTS === "true") {
+ // if the user is in playOnly mode, it will join the room with the generated stream id
+ // so we can get the list of play only participants in the room
+ webRTCAdaptor?.joinRoom(roomName, generatedStreamId, null, streamName, role, getUserStatusMetadata());
+ console.log("Play only mode is active, joining the room with the generated stream id");
}
webRTCAdaptor?.play(roomName, token, roomName, null, subscriberId, subscriberCode, '{}', role);
@@ -1184,6 +1189,7 @@ function AntMedia(props) {
if (videoTrackAssignmentsIntervalJob === null) {
videoTrackAssignmentsIntervalJob = setInterval(() => {
webRTCAdaptor?.requestVideoTrackAssignments(roomName);
+ webRTCAdaptor?.getSubtrackCount(roomName, null, null); // get the total participant count in the room
}, 3000);
}
}
@@ -1337,31 +1343,32 @@ function AntMedia(props) {
}
useEffect(() => {
-
-
- reconnecting = false;
- publishReconnected = true;
- playReconnected = true;
- console.log("++ createWebRTCAdaptor");
- //here we check if audio or video device available and wait result
- //according to the result we modify mediaConstraints
-
- checkDevices().then(() => {
- var adaptor = new WebRTCAdaptor({
- websocket_url: websocketURL,
- mediaConstraints: mediaConstraints,
- peerconnection_config: peerconnection_config,
- isPlayMode: isPlayOnly, // onlyDataChannel: isPlayOnly,
- debug: true,
- callback: infoCallback,
- callbackError: errorCallback,
- purposeForTest: "main-adaptor"
- });
- setWebRTCAdaptor(adaptor)
+ createWebRTCAdaptor();
+ //just run once when component is mounted
+ }, []); //eslint-disable-line
+
+ function createWebRTCAdaptor() {
+ reconnecting = false;
+ publishReconnected = true;
+ playReconnected = true;
+ console.log("++ createWebRTCAdaptor");
+ //here we check if audio or video device available and wait result
+ //according to the result we modify mediaConstraints
+
+ checkDevices().then(() => {
+ var adaptor = new WebRTCAdaptor({
+ websocket_url: websocketURL,
+ mediaConstraints: mediaConstraints,
+ peerconnection_config: peerconnection_config,
+ isPlayMode: isPlayOnly, // onlyDataChannel: isPlayOnly,
+ debug: true,
+ callback: infoCallback,
+ callbackError: errorCallback,
+ purposeForTest: "main-adaptor"
});
-
- //just run once when component is mounted
- }, []); //eslint-disable-line
+ setWebRTCAdaptor(adaptor)
+ });
+ }
useEffect(() => {
if (devices.length > 0) {
@@ -1519,6 +1526,10 @@ function AntMedia(props) {
}
} else if (info === "subtrackCount") {
if (obj.count !== undefined) {
+ if (obj.count > participantCount) {
+ // if the new participant is added, we need to get the subtrack list again
+ webRTCAdaptor?.getSubtracks(roomName, null, globals.participantListPagination.offset, globals.participantListPagination.pageSize);
+ }
setParticipantCount(obj.count);
}
} else if (info === "broadcastObject") {
@@ -2114,6 +2125,45 @@ function AntMedia(props) {
}
}
+ function handlePublisherRequest() {
+ if (!isPlayOnly) {
+ return;
+ }
+ handleSendNotificationEvent("REQUEST_BECOME_PUBLISHER", roomName, {
+ senderStreamId: publishStreamId, senderStreamName: streamName
+ });
+ }
+
+ function makeListenerAgain(streamId) {
+ handleSendNotificationEvent("MAKE_LISTENER_AGAIN", roomName, {
+ senderStreamId: streamId
+ });
+ updateParticipantRole(streamId, WebinarRoles.Listener);
+ }
+
+ function handleStartBecomePublisher() {
+ if (isPlayOnly) {
+ setIsPlayOnly(false);
+ setInitialized(false);
+ setWaitingOrMeetingRoom("waiting");
+ }
+ }
+
+ function approveBecomeSpeakerRequest(streamId) {
+ setRequestSpeakerList(requestSpeakerList.filter((item) => item !== streamId));
+ handleSendNotificationEvent("APPROVE_BECOME_PUBLISHER", roomName, {
+ senderStreamId: streamId
+ });
+ updateParticipantRole(streamId, WebinarRoles.TempListener);
+ }
+
+ function rejectBecomeSpeakerRequest(streamId) {
+ setRequestSpeakerList(requestSpeakerList.filter((item) => item !== streamId))
+ handleSendNotificationEvent("REJECT_BECOME_PUBLISHER", roomName, {
+ senderStreamId: streamId
+ });
+ }
+
function handleSendMessage(message) {
if (publishStreamId || isPlayOnly) {
let iceState = webRTCAdaptor?.iceConnectionState(publishStreamId);
@@ -2182,6 +2232,34 @@ function AntMedia(props) {
updateUserStatusMetadata(isMyMicMuted, !isMyCamTurnedOff);
}, [role]);
+ React.useEffect(() => {
+ // we need to empty participant array. if we are going to leave it in the first place.
+ setVideoTrackAssignments([]);
+ setAllParticipants({});
+
+ clearInterval(audioListenerIntervalJob);
+ audioListenerIntervalJob = null;
+
+ if (isPlayOnly) {
+ webRTCAdaptor?.stop(publishStreamId);
+ }
+ webRTCAdaptor?.stop(roomName);
+
+ if (isPlayOnly) {
+ webRTCAdaptor?.turnOffLocalCamera(publishStreamId);
+ }
+ //close streams fully to not encounter webcam light
+ webRTCAdaptor?.closeStream();
+
+ if (isScreenShared && screenShareWebRtcAdaptor.current != null) {
+ handleStopScreenShare();
+ }
+
+ createWebRTCAdaptor();
+
+ setWaitingOrMeetingRoom("waiting");
+ }, [isPlayOnly]);
+
function handleNotificationEvent(obj) {
var notificationEvent = JSON.parse(obj.data);
//console.log("handleNotificationEvent:", notificationEvent);
@@ -2195,12 +2273,12 @@ function AntMedia(props) {
setIsRecordPluginActive(true);
} else if (eventType === "RECORDING_TURNED_OFF") {
setIsRecordPluginActive(false);
- } else if (eventType === "BROADCAST_ON" && eventStreamId === publishStreamId) {
- setIsBroadcasting(true);
- console.log("BROADCAST_ON");
- } else if (eventType === "BROADCAST_OFF" && eventStreamId === publishStreamId) {
- setIsBroadcasting(false);
- console.log("BROADCAST_OFF");
+ } else if (eventType === "BROADCAST_ON" && eventStreamId === publishStreamId) {
+ setIsBroadcasting(true);
+ console.log("BROADCAST_ON");
+ } else if (eventType === "BROADCAST_OFF" && eventStreamId === publishStreamId) {
+ setIsBroadcasting(false);
+ console.log("BROADCAST_OFF");
} else if (eventType === "MESSAGE_RECEIVED") {
// if message arrives from myself or footer message button is disabled then we are not going to show it.
if (notificationEvent.senderId === publishStreamId || process.env.REACT_APP_FOOTER_MESSAGE_BUTTON_VISIBILITY === 'false') {
@@ -2328,9 +2406,9 @@ function AntMedia(props) {
// check if there is any difference between old and new assignments
if (!_.isEqual(currentVideoTrackAssignments, videoTrackAssignments)) {
- setVideoTrackAssignments(currentVideoTrackAssignments);
- requestSyncAdministrativeFields();
- setParticipantUpdated(!participantUpdated);
+ setVideoTrackAssignments(currentVideoTrackAssignments);
+ requestSyncAdministrativeFields();
+ setParticipantUpdated(!participantUpdated);
}
} else if (eventType === "AUDIO_TRACK_ASSIGNMENT") {
@@ -2349,7 +2427,7 @@ function AntMedia(props) {
console.log("UPDATE_PARTICIPANT_ROLE -> ", obj);
- console.log("UPDATE_PARTICIPANT_ROLE is received by "+publishStreamId);
+ console.log("UPDATE_PARTICIPANT_ROLE is received by " + publishStreamId);
let updatedParticipant = allParticipants[notificationEvent.streamId];
@@ -2374,6 +2452,41 @@ function AntMedia(props) {
webRTCAdaptor?.getSubtracks(roomName, null, globals.participantListPagination.offset, globals.participantListPagination.pageSize);
}
setParticipantUpdated(!participantUpdated);
+ } else if (eventType === "REQUEST_BECOME_PUBLISHER") {
+ if (role === WebinarRoles.Host || role === WebinarRoles.ActiveHost) {
+ if (requestSpeakerList.includes(notificationEvent.senderStreamId)) {
+ console.log("Request is already received from ", notificationEvent.senderStreamId);
+ return;
+ }
+ setRequestSpeakerList((oldRequestSpeakerList) => {
+ return [...oldRequestSpeakerList, notificationEvent.senderStreamId];
+ });
+ showInfoSnackbarWithLatency(notificationEvent.senderStreamId + t(" is requesting to become a speaker"));
+ }
+ } else if (eventType === "MAKE_LISTENER_AGAIN") {
+ if (role === WebinarRoles.TempListener || role === WebinarRoles.ActiveTempListener) {
+ showInfoSnackbarWithLatency(t("You are made listener again"));
+ mediaConstraints = {
+ video: false, audio: false,
+ };
+ setIsPlayed(false);
+ setRole(WebinarRoles.Listener);
+ setIsPlayOnly(true);
+ }
+ } else if (eventType === "APPROVE_BECOME_PUBLISHER") {
+ if (role === WebinarRoles.Listener && notificationEvent.senderStreamId === publishStreamId) {
+ showInfoSnackbarWithLatency(t("Your request to become a speaker is approved"));
+ mediaConstraints = {
+ // setting constraints here breaks source switching on firefox.
+ video: videoQualityConstraints.video, audio: audioQualityConstraints.audio,
+ };
+ setRole(WebinarRoles.TempListener);
+ setIsPlayOnly(false);
+ }
+ } else if (eventType === "REJECT_BECOME_PUBLISHER") {
+ if (role === WebinarRoles.Listener && notificationEvent.senderStreamId === publishStreamId) {
+ showInfoSnackbarWithLatency(t("Your request to become a speaker is rejected"));
+ }
}
}
}
@@ -2396,36 +2509,28 @@ function AntMedia(props) {
}
if (oldRole.includes("active") && !newRole.includes("active")) {
- setTimeout(() => {
- enqueueSnackbar({
- message: streamId + t(" is removed from the listening room"),
- variant: 'info',
- icon:
,
- anchorOrigin: {
- vertical: "top",
- horizontal: "right",
- },
- }, {
- autoHideDuration: 1000,
- });
- }, 1000);
+ showInfoSnackbarWithLatency(streamId + t(" is removed from the listening room"));
} else if (!oldRole.includes("active") && newRole.includes("active")) {
- setTimeout(() => {
- enqueueSnackbar({
- message: streamId + t(" is added to the listening room"),
- variant: 'info',
- icon:
,
- anchorOrigin: {
- vertical: "top",
- horizontal: "right",
- },
- }, {
- autoHideDuration: 1000,
- });
- }, 1000);
+ showInfoSnackbarWithLatency(streamId + t(" is added to the listening room"));
}
}
+ function showInfoSnackbarWithLatency(message) {
+ setTimeout(() => {
+ enqueueSnackbar({
+ message: message,
+ variant: 'info',
+ icon:
,
+ anchorOrigin: {
+ vertical: "top",
+ horizontal: "right",
+ },
+ }, {
+ autoHideDuration: 5000,
+ });
+ }, 1000);
+ }
+
function checkScreenSharingStatus() {
const broadcastObjectsArray = Object.values(allParticipants);
@@ -2489,6 +2594,10 @@ function AntMedia(props) {
handleStopScreenShare();
}
+ if (process.env.REACT_APP_SHOW_PLAY_ONLY_PARTICIPANTS === "true") {
+ webRTCAdaptor?.leaveFromRoom(roomName, publishStreamId);
+ }
+
playLeaveRoomSound();
setWaitingOrMeetingRoom("waiting");
@@ -3176,7 +3285,14 @@ function AntMedia(props) {
speedTestCounter,
setRoomName,
setPublishStreamId,
- settings
+ settings,
+ setRole,
+ handleNotificationEvent,
+ approveBecomeSpeakerRequest,
+ rejectBecomeSpeakerRequest,
+ handleStartBecomePublisher,
+ handlePublisherRequest,
+ makeListenerAgain
}}
>
{props.children}
@@ -3356,6 +3472,9 @@ function AntMedia(props) {
startRecord={()=>startRecord()}
stopRecord={()=>stopRecord()}
handleEffectsOpen={(open) => handleEffectsOpen(open)}
+ setBecomePublisherConfirmationDialogOpen={(open)=>setBecomePublisherConfirmationDialogOpen(open)}
+ handleStartBecomePublisher={()=>handleStartBecomePublisher()}
+ isBecomePublisherConfirmationDialogOpen={isBecomePublisherConfirmationDialogOpen}
/>
pinVideo(streamId)}
- makeListenerAgain={(streamId) => {}}
+ makeListenerAgain={(streamId) => makeListenerAgain(streamId)}
videoTrackAssignments={videoTrackAssignments}
presenterButtonStreamIdInProcess={presenterButtonStreamIdInProcess}
presenterButtonDisabled={presenterButtonDisabled}
@@ -3400,7 +3519,12 @@ function AntMedia(props) {
handleEffectsOpen={(open) => handleEffectsOpen(open)}
setPublisherRequestListDrawerOpen={(open) => setPublisherRequestListDrawerOpen(open)}
/>
-
+ approveBecomeSpeakerRequest(streamId)}
+ rejectBecomeSpeakerRequest={(streamId) => rejectBecomeSpeakerRequest(streamId)}
+ requestSpeakerList={requestSpeakerList}
+ publishStreamId={publishStreamId}
+ />
>
)}
diff --git a/react/src/pages/MeetingRoom.js b/react/src/pages/MeetingRoom.js
index 5424fd59..cb443b87 100644
--- a/react/src/pages/MeetingRoom.js
+++ b/react/src/pages/MeetingRoom.js
@@ -123,7 +123,11 @@ const MeetingRoom = React.memo((props) => {
setParticipantIdMuted={(participant)=>props?.setParticipantIdMuted(participant)}
turnOffYourMicNotification={(streamId)=>props?.turnOffYourMicNotification(streamId)}
/>
-
+ props?.setBecomePublisherConfirmationDialogOpen(open)}
+ handleStartBecomePublisher={()=>props?.handleStartBecomePublisher()}
+ isBecomePublisherConfirmationDialogOpen={props?.isBecomePublisherConfirmationDialogOpen}
+ />
{props?.audioTracks.map((audioTrackAssignment, index) => (
{
+ if (conference.role === WebinarRoles.TempListener) {
+ const tempLocalVideo = document.getElementById("localVideo");
+ conference?.localVideoCreate(tempLocalVideo);
+ console.log("TempListener local video created");
+ }
+ }, []);
+
+ React.useEffect(() => {
+
if (!props?.isPlayOnly && props?.initialized) {
const tempLocalVideo = document.getElementById("localVideo");
props?.localVideoCreate(tempLocalVideo);
@@ -309,7 +319,7 @@ function WaitingRoom(props) {
@@ -404,9 +414,31 @@ function WaitingRoom(props) {
"You can choose whether to open your camera and microphone before you get into room"
)}
+ {conference.role === WebinarRoles.TempListener ? (
+ ) : null}
: null}
+ {conference.role !== WebinarRoles.TempListener ? (
@@ -432,6 +464,7 @@ function WaitingRoom(props) {
+ ) : null}
);