diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml
index d454429a..fb017b3f 100644
--- a/.github/workflows/build-and-deploy.yml
+++ b/.github/workflows/build-and-deploy.yml
@@ -87,7 +87,7 @@ jobs:
sed -i "s#^REACT_APP_TURN_SERVER_URL=.*#REACT_APP_TURN_SERVER_URL=\"turn:${{ secrets.STAGING_TURN_URL }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_USERNAME=.*#REACT_APP_TURN_SERVER_USERNAME=\"${{ secrets.STAGING_TURN_USERNAME }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_CREDENTIAL=.*#REACT_APP_TURN_SERVER_CREDENTIAL=\"${{ secrets.STAGING_TURN_PASSWORD }}\"#" .env.production
- npm install --legacy-peer-deps
+ npm install --force
npm run build
cd ..
if [ -d "webapp/src/main/webapp/static" ] && [ "$(ls -A webapp/src/main/webapp/static)" ]; then
@@ -118,7 +118,7 @@ jobs:
sed -i "s#^REACT_APP_TURN_SERVER_URL=.*#REACT_APP_TURN_SERVER_URL=\"turn:${{ secrets.STAGING_TURN_URL }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_USERNAME=.*#REACT_APP_TURN_SERVER_USERNAME=\"${{ secrets.STAGING_TURN_USERNAME }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_CREDENTIAL=.*#REACT_APP_TURN_SERVER_CREDENTIAL=\"${{ secrets.STAGING_TURN_PASSWORD }}\"#" .env.production
- npm install --legacy-peer-deps
+ npm install --force
npm run build
cd ..
if [ -d "webapp/src/main/webapp/static" ] && [ "$(ls -A webapp/src/main/webapp/static)" ]; then
@@ -187,7 +187,7 @@ jobs:
echo "ls:"
ls -al
cd react
- npm install --include=dev --legacy-peer-deps
+ npm install --include=dev --force
npm test
- name: Upload coverage to Codecov
@@ -374,7 +374,7 @@ jobs:
sed -i "s#^REACT_APP_TURN_SERVER_URL=.*#REACT_APP_TURN_SERVER_URL=\"turn:${{ secrets.STAGING_TURN_URL }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_USERNAME=.*#REACT_APP_TURN_SERVER_USERNAME=\"${{ secrets.STAGING_TURN_USERNAME }}\"#" .env.production
sed -i "s#^REACT_APP_TURN_SERVER_CREDENTIAL=.*#REACT_APP_TURN_SERVER_CREDENTIAL=\"${{ secrets.STAGING_TURN_PASSWORD }}\"#" .env.production
- npm install --legacy-peer-deps
+ npm install --force
npm run build
cd ..
if [ -d "webapp/src/main/webapp/static" ] && [ "$(ls -A webapp/src/main/webapp/static)" ]; then
diff --git a/react/.env.development.webinar b/react/.env.development.webinar
index 3db73e0a..3a56e68d 100644
--- a/react/.env.development.webinar
+++ b/react/.env.development.webinar
@@ -28,7 +28,7 @@ REACT_APP_FOOTER_MESSAGE_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_PARTICIPANT_LIST_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_END_CALL_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_CLOCK_VISIBILITY=true
-REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=false
+REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=true
# Option menu buttons configurations
REACT_APP_OPTION_MENU_GENERAL_SETTINGS_BUTTON_VISIBILITY=false
diff --git a/react/.env.production.webinar b/react/.env.production.webinar
index 743dd7a0..75b0d3c5 100644
--- a/react/.env.production.webinar
+++ b/react/.env.production.webinar
@@ -28,7 +28,7 @@ REACT_APP_FOOTER_MESSAGE_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_PARTICIPANT_LIST_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_END_CALL_BUTTON_VISIBILITY=true
REACT_APP_FOOTER_CLOCK_VISIBILITY=true
-REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=false
+REACT_APP_FOOTER_PUBLISHER_REQUEST_BUTTON_VISIBILITY=true
# Option menu buttons configurations
REACT_APP_OPTION_MENU_GENERAL_SETTINGS_BUTTON_VISIBILITY=false
diff --git a/react/src/Components/Footer/Components/MoreOptionsButton.js b/react/src/Components/Footer/Components/MoreOptionsButton.js
index 6612a6d6..72de6057 100644
--- a/react/src/Components/Footer/Components/MoreOptionsButton.js
+++ b/react/src/Components/Footer/Components/MoreOptionsButton.js
@@ -214,7 +214,7 @@ function MoreOptionsButton(props) {
- {t("Publisher Request List")}
+ {t("Publisher Request List")}
: null}
@@ -225,7 +225,7 @@ function MoreOptionsButton(props) {
- {t("Request becoming publisher")}
+ {t("Request becoming publisher")}
: null}
diff --git a/react/src/Components/Footer/Footer.js b/react/src/Components/Footer/Footer.js
index 09604210..d99a5ec7 100644
--- a/react/src/Components/Footer/Footer.js
+++ b/react/src/Components/Footer/Footer.js
@@ -222,7 +222,9 @@ function Footer(props) {
props?.handlePublisherRequest()}
+ handlePublisherRequest={()=> {
+ props?.handlePublisherRequest()
+ }}
/>
: null}
@@ -270,6 +272,7 @@ function Footer(props) {
({
- color: "#000",
+ color: theme.palette?.participantListIcon?.primary,
fontWeight: 500,
fontSize: 14,
}));
@@ -20,6 +20,8 @@ const PinBtn = styled(Button)(({ theme }) => ({
}));
function PublisherRequestTab(props) {
+ const theme = useTheme();
+
const getPublisherRequestItem = (streamId) => {
return (
{props?.approveBecomeSpeakerRequest(streamId);}}
@@ -43,6 +46,7 @@ function PublisherRequestTab(props) {
{props?.rejectBecomeSpeakerRequest(streamId);}}
@@ -58,10 +62,10 @@ function PublisherRequestTab(props) {
-
+
{props?.requestSpeakerList.length}
diff --git a/react/src/__tests__/pages/AntMedia.test.js b/react/src/__tests__/pages/AntMedia.test.js
index 66480ef4..c6012406 100644
--- a/react/src/__tests__/pages/AntMedia.test.js
+++ b/react/src/__tests__/pages/AntMedia.test.js
@@ -2275,6 +2275,37 @@ describe('AntMedia Component', () => {
consoleSpy.mockRestore();
});
+ it('streamIdInUseCounter is not incremented due to reconnection is true', async () => {
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+
+ const {container} = render(
+
+
+
+
+ );
+
+
+ await waitFor(() => {
+ expect(webRTCAdaptorConstructor).not.toBe(undefined);
+ });
+
+ await act(async () => {
+ webRTCAdaptorConstructor.callback("reconnection_attempt_for_player");
+ });
+
+ await act(async () => {
+ webRTCAdaptorConstructor.callbackError("streamIdInUse", "Stream ID is in use");
+ webRTCAdaptorConstructor.callbackError("streamIdInUse", "Stream ID is in use");
+ webRTCAdaptorConstructor.callbackError("streamIdInUse", "Stream ID is in use");
+ webRTCAdaptorConstructor.callbackError("streamIdInUse", "Stream ID is in use");
+ });
+
+ expect(consoleSpy).not.toHaveBeenCalledWith("This stream id is already in use. You may be logged in on another device.");
+
+ consoleSpy.mockRestore();
+ });
+
it('updates allParticipants and participantUpdated when subtrackList is provided', async () => {
const { container } = render(
diff --git a/react/src/pages/AntMedia.js b/react/src/pages/AntMedia.js
index 6491cdf5..9233c844 100644
--- a/react/src/pages/AntMedia.js
+++ b/react/src/pages/AntMedia.js
@@ -1797,13 +1797,16 @@ function AntMedia(props) {
} else if (error.indexOf("no_stream_exist") !== -1) {
setIsNoSreamExist(true);
} else if (error.indexOf("streamIdInUse") !== -1) {
- streamIdInUseCounter++;
- if (streamIdInUseCounter > 3) {
- console.log("This stream id is already in use. You may be logged in on another device.");
- setLeaveRoomWithError("Streaming is already active with your username. Please check that you're not using it in another browser tab.");
- setLeftTheRoom(true);
- setIsJoining(false);
- setIsReconnectionInProgress(false);
+ // if the stream id is in use when reconnection, don't display the error
+ if (!reconnecting) {
+ streamIdInUseCounter++;
+ if (streamIdInUseCounter > 3) {
+ console.log("This stream id is already in use. You may be logged in on another device.");
+ setLeaveRoomWithError("Streaming is already active with your username. Please check that you're not using it in another browser tab.");
+ setLeftTheRoom(true);
+ setIsJoining(false);
+ setIsReconnectionInProgress(false);
+ }
}
} else if (error.indexOf("data_channel_error") !== -1) {
errorMessage = "There was a error during data channel communication";
@@ -3398,6 +3401,7 @@ function AntMedia(props) {
pinVideo={(streamId) => pinVideo(streamId)}
isAdmin={isAdmin}
publishStreamId={publishStreamId}
+ role={role}
/>
) : (
<>
@@ -3451,7 +3455,7 @@ function AntMedia(props) {
handleParticipantListOpen={(open) => handleParticipantListOpen(open)}
requestSpeakerList={requestSpeakerList}
handlePublisherRequestListOpen={(open) => setPublisherRequestListDrawerOpen(open)}
- handlePublisherRequest={()=>{}}
+ handlePublisherRequest={()=>handlePublisherRequest()}
setLeftTheRoom={(left) => setLeftTheRoom(left)}
addFakeParticipant={() => addFakeParticipant()}
removeFakeParticipant={() => removeFakeParticipant()}
@@ -3520,10 +3524,15 @@ function AntMedia(props) {
setPublisherRequestListDrawerOpen={(open) => setPublisherRequestListDrawerOpen(open)}
/>
approveBecomeSpeakerRequest(streamId)}
rejectBecomeSpeakerRequest={(streamId) => rejectBecomeSpeakerRequest(streamId)}
requestSpeakerList={requestSpeakerList}
publishStreamId={publishStreamId}
+ handleMessageDrawerOpen={(open) => handleMessageDrawerOpen(open)}
+ handleParticipantListOpen={(open) => handleParticipantListOpen(open)}
+ handleEffectsOpen={(open) => handleEffectsOpen(open)}
+ setPublisherRequestListDrawerOpen={(open) => setPublisherRequestListDrawerOpen(open)}
/>
>
)}
diff --git a/react/src/pages/MeetingRoom.js b/react/src/pages/MeetingRoom.js
index cb443b87..01952693 100644
--- a/react/src/pages/MeetingRoom.js
+++ b/react/src/pages/MeetingRoom.js
@@ -252,7 +252,7 @@ const MeetingRoom = React.memo((props) => {
requestSpeakerList={props?.requestSpeakerList}
publisherRequestListDrawerOpen={props?.publisherRequestListDrawerOpen}
handlePublisherRequestListOpen={props?.handlePublisherRequestListOpen}
- handlePublisherRequest={props?.handlePublisherRequest}
+ handlePublisherRequest={()=> {props?.handlePublisherRequest()}}
setLeftTheRoom={props?.setLeftTheRoom}
addFakeParticipant={props?.addFakeParticipant}
removeFakeParticipant={props?.removeFakeParticipant}
diff --git a/react/src/pages/WaitingRoom.js b/react/src/pages/WaitingRoom.js
index 5ef267c7..3f837147 100755
--- a/react/src/pages/WaitingRoom.js
+++ b/react/src/pages/WaitingRoom.js
@@ -70,9 +70,9 @@ function WaitingRoom(props) {
};
React.useEffect(() => {
- if (conference.role === WebinarRoles.TempListener) {
+ if (props?.role === WebinarRoles.TempListener) {
const tempLocalVideo = document.getElementById("localVideo");
- conference?.localVideoCreate(tempLocalVideo);
+ props?.localVideoCreate(tempLocalVideo);
console.log("TempListener local video created");
}
}, []);
@@ -319,7 +319,7 @@ function WaitingRoom(props) {
@@ -414,7 +414,7 @@ function WaitingRoom(props) {
"You can choose whether to open your camera and microphone before you get into room"
)}
- {conference.role === WebinarRoles.TempListener ? (
+ {props?.role === WebinarRoles.TempListener ? (
: null}
- {conference.role !== WebinarRoles.TempListener ? (
+ {props?.role !== WebinarRoles.TempListener ? (
diff --git a/test/test_webinar.py b/test/test_webinar.py
index 16662619..8e6d8321 100644
--- a/test/test_webinar.py
+++ b/test/test_webinar.py
@@ -141,7 +141,7 @@ def join_room_as_player(self, participant, room, skip_speed_test=False):
app = "/"+self.test_app_name
if self.url.endswith("localhost:3000"):
app = ""
- handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?playOnly=true&role=listener&streamName=" + participant + ("&enterDirectly=true" if skip_speed_test else ""))
+ handle = self.chrome.open_in_new_tab(self.url+app+"/"+room+"?playOnly=true&role=listener&streamName=" + participant + "&streamId=" + participant + ("&enterDirectly=true" if skip_speed_test else ""))
wait = self.chrome.get_wait()
@@ -184,6 +184,14 @@ def join_room_as_player(self, participant, room, skip_speed_test=False):
return handle
+ def accept_raising_hand_request(self, participant):
+ accept_button = self.chrome.get_element_with_retry(By.ID,"approve-become-speaker-"+participant)
+ self.chrome.click_element(accept_button)
+
+ def reject_raising_hand_request(self, participant):
+ reject_button = self.chrome.get_element_with_retry(By.ID,"reject-become-speaker-"+participant)
+ self.chrome.click_element(reject_button)
+
def add_presenter_to_listener_room(self, presenter):
add_button = self.chrome.get_element(By.ID,"add-presenter-"+presenter)
self.chrome.click_element(add_button)
@@ -752,6 +760,90 @@ def _test_admin_video_card_controls(self):
wait.until(lambda x: self.chrome.get_element_in_element(presenterA_video_card, By.XPATH, ".//button[@type='button' and @aria-label='turn-off-camera']") is not None)
+ self.chrome.close_all()
+
+ def get_request_publish_button(self):
+ rp_button = None
+ if(self.chrome.is_element_exist(By.ID, "request-publish-button")):
+ rp_button = self.chrome.get_element(By.ID, "request-publish-button")
+ else:
+ more_button = self.chrome.get_element_with_retry(By.ID, "more-button")
+ self.chrome.click_element(more_button)
+ rp_button = self.chrome.get_element_with_retry(By.ID, "more-options-request-publish-button")
+ return rp_button
+
+ def test_raising_hand(self):
+ # create a room and join as admin and 2 players
+ room = "room"+str(random.randint(100, 999))
+ handle_admin = self.join_room_as_admin("admin", room, True)
+ handle_player_A = self.join_room_as_player("playerA", room, False)
+ handle_player_B = self.join_room_as_player("playerB", room, False)
+
+ wait = self.chrome.get_wait()
+
+ # switch to playerA and raise hand
+ self.chrome.switch_to_tab(handle_player_A)
+
+ raise_hand_button = self.get_request_publish_button()
+ self.chrome.click_element(raise_hand_button)
+
+ # switch to admin and check if playerA is in the request list
+ self.chrome.switch_to_tab(handle_admin)
+
+ self.open_close_publisher_request_list_drawer()
+
+ time.sleep(15)
+
+ self.accept_raising_hand_request("playerA")
+
+ # switch to playerA and join the room
+ self.chrome.switch_to_tab(handle_player_A)
+
+ join_button = self.chrome.get_element_with_retry(By.ID,"room_join_button")
+ self.chrome.click_element(join_button)
+
+ time.sleep(5)
+ speedTestCircularProgress = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-circle-progress-bar", retries=20)
+ assert(speedTestCircularProgress.is_displayed())
+
+ time.sleep(5)
+
+ timeoutCounter = 0
+
+ isSpeedTestFinished = False
+ isSpeedTestFailed = False
+
+ while not isSpeedTestFailed and not isSpeedTestFinished and timeoutCounter < 100:
+ time.sleep(1)
+ timeoutCounter += 1
+ script = "return window.conference.speedTestObject;"
+ result_json = self.chrome.execute_script(script)
+ if result_json is not None:
+ isSpeedTestFinished = result_json["isfinished"]
+ isSpeedTestFailed = result_json["isfailed"]
+
+ speedTestModalJoinButton = self.chrome.get_element_with_retry(By.ID,"speed-test-modal-join-button")
+
+ self.chrome.print_ss_as_base64()
+
+ self.chrome.click_element(speedTestModalJoinButton)
+
+ time.sleep(5)
+
+ meeting_gallery = self.chrome.get_element_with_retry(By.ID,"meeting-gallery")
+
+ assert(meeting_gallery.is_displayed())
+
+ wait.until(lambda x: len(self.get_participants()) == 2)
+
+ # switch to admin
+ self.chrome.switch_to_tab(handle_admin)
+
+ wait.until(lambda x: len(self.get_participants()) == 2)
+
+ # switch to playerB
+ self.chrome.switch_to_tab(handle_player_B)
+
self.chrome.close_all()
if __name__ == '__main__':