Merge remote-tracking branch 'upstream/v2.7.x-release' into 27-dev-apr24
This commit is contained in:
commit
b121fcbd87
4
.github/workflows/automated-tests.yml
vendored
4
.github/workflows/automated-tests.yml
vendored
@ -43,6 +43,7 @@ jobs:
|
|||||||
- run: ./build/setup.sh bbb-record-core
|
- run: ./build/setup.sh bbb-record-core
|
||||||
- run: ./build/setup.sh bbb-web
|
- run: ./build/setup.sh bbb-web
|
||||||
- run: ./build/setup.sh bbb-webrtc-sfu
|
- run: ./build/setup.sh bbb-webrtc-sfu
|
||||||
|
- run: ./build/setup.sh bbb-webrtc-recorder
|
||||||
- run: ./build/setup.sh bigbluebutton
|
- run: ./build/setup.sh bigbluebutton
|
||||||
- run: tar cvf artifacts.tar artifacts/
|
- run: tar cvf artifacts.tar artifacts/
|
||||||
- name: Archive packages
|
- name: Archive packages
|
||||||
@ -182,6 +183,7 @@ jobs:
|
|||||||
cp /etc/bigbluebutton/bigbluebutton-release configs/bigbluebutton-release
|
cp /etc/bigbluebutton/bigbluebutton-release configs/bigbluebutton-release
|
||||||
cp /etc/bigbluebutton/turn-stun-servers.xml configs/turn-stun-servers.xml
|
cp /etc/bigbluebutton/turn-stun-servers.xml configs/turn-stun-servers.xml
|
||||||
cp /usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml configs/bbb-webrtc-sfu-default.yml
|
cp /usr/local/bigbluebutton/bbb-webrtc-sfu/config/default.yml configs/bbb-webrtc-sfu-default.yml
|
||||||
|
cp /etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml configs/bbb-webrtc-recorder-default.yml
|
||||||
cp /usr/share/bigbluebutton/nginx/sip.nginx configs/nginx_sip.nginx
|
cp /usr/share/bigbluebutton/nginx/sip.nginx configs/nginx_sip.nginx
|
||||||
cp /etc/hosts /configs/hosts
|
cp /etc/hosts /configs/hosts
|
||||||
chmod a+r -R configs
|
chmod a+r -R configs
|
||||||
@ -197,4 +199,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: bbb-logs
|
name: bbb-logs
|
||||||
path: ./bbb-logs.tar.gz
|
path: ./bbb-logs.tar.gz
|
||||||
|
@ -12,7 +12,7 @@ stages:
|
|||||||
|
|
||||||
# define which docker image to use for builds
|
# define which docker image to use for builds
|
||||||
default:
|
default:
|
||||||
image: gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:v2022-12-29-grails-524
|
image: gitlab.senfcall.de:5050/senfcall-public/docker-bbb-build:v2023-04-18
|
||||||
|
|
||||||
# This stage uses git to find out since when each package has been unmodified.
|
# This stage uses git to find out since when each package has been unmodified.
|
||||||
# it then checks an API endpoint on the package server to find out for which of
|
# it then checks an API endpoint on the package server to find out for which of
|
||||||
@ -47,11 +47,12 @@ get_external_dependencies:
|
|||||||
- bbb-etherpad
|
- bbb-etherpad
|
||||||
- bbb-webhooks
|
- bbb-webhooks
|
||||||
- bbb-webrtc-sfu
|
- bbb-webrtc-sfu
|
||||||
|
- bbb-webrtc-recorder
|
||||||
- freeswitch
|
- freeswitch
|
||||||
- bbb-pads
|
- bbb-pads
|
||||||
- bbb-playback
|
- bbb-playback
|
||||||
expire_in: 1h 30min
|
expire_in: 1h 30min
|
||||||
|
|
||||||
# template job for build step
|
# template job for build step
|
||||||
.build_job:
|
.build_job:
|
||||||
stage: build
|
stage: build
|
||||||
@ -170,6 +171,11 @@ bbb-webrtc-sfu-build:
|
|||||||
script:
|
script:
|
||||||
- build/setup-inside-docker.sh bbb-webrtc-sfu
|
- build/setup-inside-docker.sh bbb-webrtc-sfu
|
||||||
|
|
||||||
|
bbb-webrtc-recorder-build:
|
||||||
|
extends: .build_job
|
||||||
|
script:
|
||||||
|
- build/setup-inside-docker.sh bbb-webrtc-recorder
|
||||||
|
|
||||||
bigbluebutton-build:
|
bigbluebutton-build:
|
||||||
extends: .build_job
|
extends: .build_job
|
||||||
script:
|
script:
|
||||||
@ -180,12 +186,12 @@ push_packages:
|
|||||||
stage: push packages
|
stage: push packages
|
||||||
script: build/push_packages.sh
|
script: build/push_packages.sh
|
||||||
resource_group: push_packages
|
resource_group: push_packages
|
||||||
|
|
||||||
# uncomment the lines below if you want one final
|
# uncomment the lines below if you want one final
|
||||||
# "artifacts" dir with all packages (increases runtime, fills up space on gitlab server)
|
# "artifacts" dir with all packages (increases runtime, fills up space on gitlab server)
|
||||||
#artifacts:
|
#artifacts:
|
||||||
# paths:
|
# paths:
|
||||||
# - artifacts/*
|
# - artifacts/*
|
||||||
# expire_in: 2 days
|
# expire_in: 2 days
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import org.bigbluebutton.SystemConfiguration
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.running.RunningMeeting
|
import org.bigbluebutton.core.running.RunningMeeting
|
||||||
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.RunningMeetings
|
import org.bigbluebutton.core2.RunningMeetings
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
import org.bigbluebutton.service.HealthzService
|
import org.bigbluebutton.service.HealthzService
|
||||||
@ -183,6 +184,9 @@ class BigBlueButtonActor(
|
|||||||
// Stop the meeting actor.
|
// Stop the meeting actor.
|
||||||
context.stop(m.actorRef)
|
context.stop(m.actorRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Remove ColorPicker idx of the meeting
|
||||||
|
ColorPicker.reset(m.props.meetingProp.intId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package org.bigbluebutton.core.apps.users
|
||||||
|
|
||||||
|
import org.bigbluebutton.common2.msgs._
|
||||||
|
import org.bigbluebutton.core.apps.RightsManagementTrait
|
||||||
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
|
||||||
|
trait ChangeUserMobileFlagReqMsgHdlr extends RightsManagementTrait {
|
||||||
|
this: UsersApp =>
|
||||||
|
|
||||||
|
val liveMeeting: LiveMeeting
|
||||||
|
val outGW: OutMsgRouter
|
||||||
|
|
||||||
|
def handleChangeUserMobileFlagReqMsg(msg: ChangeUserMobileFlagReqMsg): Unit = {
|
||||||
|
log.info("handleChangeUserMobileFlagReqMsg: mobile={} userId={}", msg.body.mobile, msg.body.userId)
|
||||||
|
|
||||||
|
def broadcastUserMobileChanged(user: UserState, mobile: Boolean): Unit = {
|
||||||
|
val routingChange = Routing.addMsgToClientRouting(
|
||||||
|
MessageTypes.BROADCAST_TO_MEETING,
|
||||||
|
liveMeeting.props.meetingProp.intId, user.intId
|
||||||
|
)
|
||||||
|
val envelopeChange = BbbCoreEnvelope(UserMobileFlagChangedEvtMsg.NAME, routingChange)
|
||||||
|
val headerChange = BbbClientMsgHeader(UserMobileFlagChangedEvtMsg.NAME, liveMeeting.props.meetingProp.intId,
|
||||||
|
user.intId)
|
||||||
|
|
||||||
|
val bodyChange = UserMobileFlagChangedEvtMsgBody(user.intId, mobile)
|
||||||
|
val eventChange = UserMobileFlagChangedEvtMsg(headerChange, bodyChange)
|
||||||
|
val msgEventChange = BbbCommonEnvCoreMsg(envelopeChange, eventChange)
|
||||||
|
outGW.send(msgEventChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||||
|
} yield {
|
||||||
|
if (user.mobile != msg.body.mobile) {
|
||||||
|
val userMobile = Users2x.setMobile(liveMeeting.users2x, user)
|
||||||
|
broadcastUserMobileChanged(userMobile, msg.body.mobile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ package org.bigbluebutton.core.apps.users
|
|||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||||
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||||
|
|
||||||
trait RegisterUserReqMsgHdlr {
|
trait RegisterUserReqMsgHdlr {
|
||||||
@ -56,7 +57,7 @@ trait RegisterUserReqMsgHdlr {
|
|||||||
|
|
||||||
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
||||||
msg.body.name, msg.body.role, msg.body.authToken,
|
msg.body.name, msg.body.role, msg.body.authToken,
|
||||||
msg.body.avatarURL, msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false)
|
msg.body.avatarURL, ColorPicker.nextColor(liveMeeting.props.meetingProp.intId), msg.body.guest, msg.body.authed, guestStatus, msg.body.excludeFromDashboard, false)
|
||||||
|
|
||||||
checkUserConcurrentAccesses(regUser)
|
checkUserConcurrentAccesses(regUser)
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ trait RegisterUserReqMsgHdlr {
|
|||||||
val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW)
|
val g = GuestApprovedVO(regUser.id, GuestStatus.ALLOW)
|
||||||
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)
|
UsersApp.approveOrRejectGuest(liveMeeting, outGW, g, SystemUser.ID)
|
||||||
case GuestStatus.WAIT =>
|
case GuestStatus.WAIT =>
|
||||||
val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.authed, regUser.registeredOn)
|
val guest = GuestWaiting(regUser.id, regUser.name, regUser.role, regUser.guest, regUser.avatarURL, regUser.color, regUser.authed, regUser.registeredOn)
|
||||||
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
|
addGuestToWaitingForApproval(guest, liveMeeting.guestsWaiting)
|
||||||
notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
|
notifyModeratorsOfGuestWaiting(Vector(guest), liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
|
||||||
val notifyEvent = MsgBuilder.buildNotifyRoleInMeetingEvtMsg(
|
val notifyEvent = MsgBuilder.buildNotifyRoleInMeetingEvtMsg(
|
||||||
|
@ -19,8 +19,8 @@ trait UserConnectedToGlobalAudioMsgHdlr {
|
|||||||
val header = BbbClientMsgHeader(UserJoinedVoiceConfToClientEvtMsg.NAME, props.meetingProp.intId, vu.intId)
|
val header = BbbClientMsgHeader(UserJoinedVoiceConfToClientEvtMsg.NAME, props.meetingProp.intId, vu.intId)
|
||||||
|
|
||||||
val body = UserJoinedVoiceConfToClientEvtMsgBody(voiceConf = msg.header.voiceConf, intId = vu.intId, voiceUserId = vu.intId,
|
val body = UserJoinedVoiceConfToClientEvtMsgBody(voiceConf = msg.header.voiceConf, intId = vu.intId, voiceUserId = vu.intId,
|
||||||
callingWith = vu.callingWith, callerName = vu.callerName,
|
callingWith = vu.callingWith, callerName = vu.callerName, callerNum = vu.callerNum, color = vu.color,
|
||||||
callerNum = vu.callerNum, muted = true, talking = false, listenOnly = true)
|
muted = true, talking = false, listenOnly = true)
|
||||||
val event = UserJoinedVoiceConfToClientEvtMsg(header, body)
|
val event = UserJoinedVoiceConfToClientEvtMsg(header, body)
|
||||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||||
outGW.send(msgEvent)
|
outGW.send(msgEvent)
|
||||||
@ -36,6 +36,7 @@ trait UserConnectedToGlobalAudioMsgHdlr {
|
|||||||
callingWith = "flash",
|
callingWith = "flash",
|
||||||
callerName = user.name,
|
callerName = user.name,
|
||||||
callerNum = user.name,
|
callerNum = user.name,
|
||||||
|
color = user.color,
|
||||||
muted = true,
|
muted = true,
|
||||||
talking = false,
|
talking = false,
|
||||||
listenOnly = true,
|
listenOnly = true,
|
||||||
|
@ -158,6 +158,7 @@ class UsersApp(
|
|||||||
with SelectRandomViewerReqMsgHdlr
|
with SelectRandomViewerReqMsgHdlr
|
||||||
with AssignPresenterReqMsgHdlr
|
with AssignPresenterReqMsgHdlr
|
||||||
with ChangeUserPinStateReqMsgHdlr
|
with ChangeUserPinStateReqMsgHdlr
|
||||||
|
with ChangeUserMobileFlagReqMsgHdlr
|
||||||
with EjectUserFromMeetingCmdMsgHdlr
|
with EjectUserFromMeetingCmdMsgHdlr
|
||||||
with EjectUserFromMeetingSysMsgHdlr
|
with EjectUserFromMeetingSysMsgHdlr
|
||||||
with MuteUserCmdMsgHdlr {
|
with MuteUserCmdMsgHdlr {
|
||||||
|
@ -130,7 +130,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
|||||||
def sendAllVoiceUsersInMeeting(requesterId: String, voiceUsers: VoiceUsers, meetingId: String): Unit = {
|
def sendAllVoiceUsersInMeeting(requesterId: String, voiceUsers: VoiceUsers, meetingId: String): Unit = {
|
||||||
val vu = VoiceUsers.findAll(voiceUsers).map { u =>
|
val vu = VoiceUsers.findAll(voiceUsers).map { u =>
|
||||||
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
|
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
|
||||||
callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
|
callerNum = u.callerNum, color = u.color, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu)
|
val event = MsgBuilder.buildGetVoiceUsersMeetingRespMsg(meetingId, requesterId, vu)
|
||||||
|
@ -14,7 +14,7 @@ trait SyncGetVoiceUsersMsgHdlr {
|
|||||||
def buildSyncGetVoiceUsersRespMsg(): BbbCommonEnvCoreMsg = {
|
def buildSyncGetVoiceUsersRespMsg(): BbbCommonEnvCoreMsg = {
|
||||||
val voiceUsers = VoiceUsers.findAll(liveMeeting.voiceUsers).map { u =>
|
val voiceUsers = VoiceUsers.findAll(liveMeeting.voiceUsers).map { u =>
|
||||||
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
|
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
|
||||||
callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
|
callerNum = u.callerNum, color = u.color, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
|
val routing = Routing.addMsgToHtml5InstanceIdRouting(liveMeeting.props.meetingProp.intId, liveMeeting.props.systemProps.html5InstanceId.toString)
|
||||||
|
@ -5,7 +5,7 @@ import org.bigbluebutton.common2.msgs._
|
|||||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
|
|
||||||
trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
||||||
@ -19,6 +19,8 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
val guestPolicy = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting)
|
val guestPolicy = GuestsWaiting.getGuestPolicy(liveMeeting.guestsWaiting)
|
||||||
val isDialInUser = msg.body.intId.startsWith(IntIdPrefixType.DIAL_IN)
|
val isDialInUser = msg.body.intId.startsWith(IntIdPrefixType.DIAL_IN)
|
||||||
|
|
||||||
|
val userColor = ColorPicker.nextColor(liveMeeting.props.meetingProp.intId)
|
||||||
|
|
||||||
def notifyModeratorsOfGuestWaiting(guest: GuestWaiting, users: Users2x, meetingId: String): Unit = {
|
def notifyModeratorsOfGuestWaiting(guest: GuestWaiting, users: Users2x, meetingId: String): Unit = {
|
||||||
val moderators = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE)
|
val moderators = Users2x.findAll(users).filter(p => p.role == Roles.MODERATOR_ROLE)
|
||||||
moderators foreach { mod =>
|
moderators foreach { mod =>
|
||||||
@ -32,7 +34,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
|
|
||||||
def registerUserInRegisteredUsers() = {
|
def registerUserInRegisteredUsers() = {
|
||||||
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
|
val regUser = RegisteredUsers.create(msg.body.intId, msg.body.voiceUserId,
|
||||||
msg.body.callerIdName, Roles.VIEWER_ROLE, "",
|
msg.body.callerIdName, Roles.VIEWER_ROLE, "", userColor,
|
||||||
"", true, true, GuestStatus.WAIT, true, false)
|
"", true, true, GuestStatus.WAIT, true, false)
|
||||||
RegisteredUsers.add(liveMeeting.registeredUsers, regUser)
|
RegisteredUsers.add(liveMeeting.registeredUsers, regUser)
|
||||||
}
|
}
|
||||||
@ -48,9 +50,11 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
guestStatus = GuestStatus.WAIT,
|
guestStatus = GuestStatus.WAIT,
|
||||||
emoji = "none",
|
emoji = "none",
|
||||||
pin = false,
|
pin = false,
|
||||||
|
mobile = false,
|
||||||
presenter = false,
|
presenter = false,
|
||||||
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
||||||
avatar = "",
|
avatar = "",
|
||||||
|
color = userColor,
|
||||||
clientType = "",
|
clientType = "",
|
||||||
pickExempted = false,
|
pickExempted = false,
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
@ -60,7 +64,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
|
|
||||||
def registerUserAsGuest() = {
|
def registerUserAsGuest() = {
|
||||||
if (GuestsWaiting.findWithIntId(liveMeeting.guestsWaiting, msg.body.intId) == None) {
|
if (GuestsWaiting.findWithIntId(liveMeeting.guestsWaiting, msg.body.intId) == None) {
|
||||||
val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", true, System.currentTimeMillis())
|
val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", userColor, true, System.currentTimeMillis())
|
||||||
GuestsWaiting.add(liveMeeting.guestsWaiting, guest)
|
GuestsWaiting.add(liveMeeting.guestsWaiting, guest)
|
||||||
notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
|
notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId)
|
||||||
|
|
||||||
@ -84,6 +88,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
|
|||||||
msg.body.callingWith,
|
msg.body.callingWith,
|
||||||
msg.body.callerIdName,
|
msg.body.callerIdName,
|
||||||
msg.body.callerIdNum,
|
msg.body.callerIdNum,
|
||||||
|
userColor,
|
||||||
msg.body.muted,
|
msg.body.muted,
|
||||||
msg.body.talking,
|
msg.body.talking,
|
||||||
"freeswitch"
|
"freeswitch"
|
||||||
|
@ -7,9 +7,10 @@ import org.bigbluebutton.core.bus.InternalEventBus
|
|||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||||
import org.bigbluebutton.common2.msgs._
|
import org.bigbluebutton.common2.msgs._
|
||||||
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
|
import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter}
|
||||||
import org.bigbluebutton.core.models._
|
import org.bigbluebutton.core.models._
|
||||||
import org.bigbluebutton.core.apps.users.UsersApp
|
import org.bigbluebutton.core.apps.users.UsersApp
|
||||||
|
import org.bigbluebutton.core.util.ColorPicker
|
||||||
|
|
||||||
object VoiceApp extends SystemConfiguration {
|
object VoiceApp extends SystemConfiguration {
|
||||||
|
|
||||||
@ -164,6 +165,7 @@ object VoiceApp extends SystemConfiguration {
|
|||||||
cvu.callingWith,
|
cvu.callingWith,
|
||||||
cvu.callerIdName,
|
cvu.callerIdName,
|
||||||
cvu.callerIdNum,
|
cvu.callerIdNum,
|
||||||
|
ColorPicker.nextColor(liveMeeting.props.meetingProp.intId),
|
||||||
cvu.muted,
|
cvu.muted,
|
||||||
cvu.talking,
|
cvu.talking,
|
||||||
cvu.calledInto
|
cvu.calledInto
|
||||||
@ -213,6 +215,7 @@ object VoiceApp extends SystemConfiguration {
|
|||||||
callingWith: String,
|
callingWith: String,
|
||||||
callerIdName: String,
|
callerIdName: String,
|
||||||
callerIdNum: String,
|
callerIdNum: String,
|
||||||
|
color: String,
|
||||||
muted: Boolean,
|
muted: Boolean,
|
||||||
talking: Boolean,
|
talking: Boolean,
|
||||||
callingInto: String
|
callingInto: String
|
||||||
@ -240,6 +243,7 @@ object VoiceApp extends SystemConfiguration {
|
|||||||
voiceUserState.voiceUserId,
|
voiceUserState.voiceUserId,
|
||||||
voiceUserState.callerName,
|
voiceUserState.callerName,
|
||||||
voiceUserState.callerNum,
|
voiceUserState.callerNum,
|
||||||
|
voiceUserState.color,
|
||||||
voiceUserState.muted,
|
voiceUserState.muted,
|
||||||
voiceUserState.talking,
|
voiceUserState.talking,
|
||||||
voiceUserState.callingWith,
|
voiceUserState.callingWith,
|
||||||
@ -267,6 +271,7 @@ object VoiceApp extends SystemConfiguration {
|
|||||||
callingWith,
|
callingWith,
|
||||||
callerIdName,
|
callerIdName,
|
||||||
callerIdNum,
|
callerIdNum,
|
||||||
|
color,
|
||||||
muted,
|
muted,
|
||||||
talking,
|
talking,
|
||||||
listenOnly = isListenOnly,
|
listenOnly = isListenOnly,
|
||||||
|
@ -68,7 +68,7 @@ class GuestsWaiting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, authenticated: Boolean, registeredOn: Long)
|
case class GuestWaiting(intId: String, name: String, role: String, guest: Boolean, avatar: String, color: String, authenticated: Boolean, registeredOn: Long)
|
||||||
case class GuestPolicy(policy: String, setBy: String)
|
case class GuestPolicy(policy: String, setBy: String)
|
||||||
|
|
||||||
object GuestPolicyType {
|
object GuestPolicyType {
|
||||||
|
@ -5,7 +5,7 @@ import org.bigbluebutton.core.domain.BreakoutRoom2x
|
|||||||
|
|
||||||
object RegisteredUsers {
|
object RegisteredUsers {
|
||||||
def create(userId: String, extId: String, name: String, roles: String,
|
def create(userId: String, extId: String, name: String, roles: String,
|
||||||
token: String, avatar: String, guest: Boolean, authenticated: Boolean,
|
token: String, avatar: String, color: String, guest: Boolean, authenticated: Boolean,
|
||||||
guestStatus: String, excludeFromDashboard: Boolean, loggedOut: Boolean): RegisteredUser = {
|
guestStatus: String, excludeFromDashboard: Boolean, loggedOut: Boolean): RegisteredUser = {
|
||||||
new RegisteredUser(
|
new RegisteredUser(
|
||||||
userId,
|
userId,
|
||||||
@ -14,6 +14,7 @@ object RegisteredUsers {
|
|||||||
roles,
|
roles,
|
||||||
token,
|
token,
|
||||||
avatar,
|
avatar,
|
||||||
|
color,
|
||||||
guest,
|
guest,
|
||||||
authenticated,
|
authenticated,
|
||||||
guestStatus,
|
guestStatus,
|
||||||
@ -191,6 +192,7 @@ case class RegisteredUser(
|
|||||||
role: String,
|
role: String,
|
||||||
authToken: String,
|
authToken: String,
|
||||||
avatarURL: String,
|
avatarURL: String,
|
||||||
|
color: String,
|
||||||
guest: Boolean,
|
guest: Boolean,
|
||||||
authed: Boolean,
|
authed: Boolean,
|
||||||
guestStatus: String,
|
guestStatus: String,
|
||||||
|
@ -107,6 +107,12 @@ object Users2x {
|
|||||||
newUserState
|
newUserState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def setMobile(users: Users2x, u: UserState): UserState = {
|
||||||
|
val newUserState = modify(u)(_.mobile).setTo(true)
|
||||||
|
users.save(newUserState)
|
||||||
|
newUserState
|
||||||
|
}
|
||||||
|
|
||||||
def ejectFromMeeting(users: Users2x, intId: String): Option[UserState] = {
|
def ejectFromMeeting(users: Users2x, intId: String): Option[UserState] = {
|
||||||
for {
|
for {
|
||||||
_ <- users.remove(intId)
|
_ <- users.remove(intId)
|
||||||
@ -354,12 +360,14 @@ case class UserState(
|
|||||||
role: String,
|
role: String,
|
||||||
guest: Boolean,
|
guest: Boolean,
|
||||||
pin: Boolean,
|
pin: Boolean,
|
||||||
|
mobile: Boolean,
|
||||||
authed: Boolean,
|
authed: Boolean,
|
||||||
guestStatus: String,
|
guestStatus: String,
|
||||||
emoji: String,
|
emoji: String,
|
||||||
locked: Boolean,
|
locked: Boolean,
|
||||||
presenter: Boolean,
|
presenter: Boolean,
|
||||||
avatar: String,
|
avatar: String,
|
||||||
|
color: String,
|
||||||
roleChangedOn: Long = System.currentTimeMillis(),
|
roleChangedOn: Long = System.currentTimeMillis(),
|
||||||
lastActivityTime: Long = System.currentTimeMillis(),
|
lastActivityTime: Long = System.currentTimeMillis(),
|
||||||
lastInactivityInspect: Long = 0,
|
lastInactivityInspect: Long = 0,
|
||||||
|
@ -174,6 +174,7 @@ case class VoiceUserState(
|
|||||||
callingWith: String,
|
callingWith: String,
|
||||||
callerName: String,
|
callerName: String,
|
||||||
callerNum: String,
|
callerNum: String,
|
||||||
|
color: String,
|
||||||
muted: Boolean,
|
muted: Boolean,
|
||||||
talking: Boolean,
|
talking: Boolean,
|
||||||
listenOnly: Boolean,
|
listenOnly: Boolean,
|
||||||
|
@ -109,6 +109,8 @@ class ReceivedJsonMsgHandlerActor(
|
|||||||
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
|
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
|
||||||
case ChangeUserPinStateReqMsg.NAME =>
|
case ChangeUserPinStateReqMsg.NAME =>
|
||||||
routeGenericMsg[ChangeUserPinStateReqMsg](envelope, jsonNode)
|
routeGenericMsg[ChangeUserPinStateReqMsg](envelope, jsonNode)
|
||||||
|
case ChangeUserMobileFlagReqMsg.NAME =>
|
||||||
|
routeGenericMsg[ChangeUserMobileFlagReqMsg](envelope, jsonNode)
|
||||||
case SelectRandomViewerReqMsg.NAME =>
|
case SelectRandomViewerReqMsg.NAME =>
|
||||||
routeGenericMsg[SelectRandomViewerReqMsg](envelope, jsonNode)
|
routeGenericMsg[SelectRandomViewerReqMsg](envelope, jsonNode)
|
||||||
|
|
||||||
|
@ -63,9 +63,11 @@ trait HandlerHelpers extends SystemConfiguration {
|
|||||||
guestStatus = regUser.guestStatus,
|
guestStatus = regUser.guestStatus,
|
||||||
emoji = "none",
|
emoji = "none",
|
||||||
pin = false,
|
pin = false,
|
||||||
|
mobile = false,
|
||||||
presenter = false,
|
presenter = false,
|
||||||
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
||||||
avatar = regUser.avatarURL,
|
avatar = regUser.avatarURL,
|
||||||
|
color = regUser.color,
|
||||||
clientType = clientType,
|
clientType = clientType,
|
||||||
pickExempted = false,
|
pickExempted = false,
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
|
@ -384,10 +384,11 @@ class MeetingActor(
|
|||||||
case m: RecordAndClearPreviousMarkersCmdMsg =>
|
case m: RecordAndClearPreviousMarkersCmdMsg =>
|
||||||
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
|
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
|
||||||
updateUserLastActivity(m.body.setBy)
|
updateUserLastActivity(m.body.setBy)
|
||||||
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||||
case m: SelectRandomViewerReqMsg => usersApp.handleSelectRandomViewerReqMsg(m)
|
case m: SelectRandomViewerReqMsg => usersApp.handleSelectRandomViewerReqMsg(m)
|
||||||
case m: ChangeUserPinStateReqMsg => usersApp.handleChangeUserPinStateReqMsg(m)
|
case m: ChangeUserPinStateReqMsg => usersApp.handleChangeUserPinStateReqMsg(m)
|
||||||
|
case m: ChangeUserMobileFlagReqMsg => usersApp.handleChangeUserMobileFlagReqMsg(m)
|
||||||
|
|
||||||
// Client requested to eject user
|
// Client requested to eject user
|
||||||
case m: EjectUserFromMeetingCmdMsg =>
|
case m: EjectUserFromMeetingCmdMsg =>
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package org.bigbluebutton.core.util
|
||||||
|
|
||||||
|
object ColorPicker {
|
||||||
|
private val colors = List("#7b1fa2", "#6a1b9a", "#4a148c", "#5e35b1", "#512da8", "#4527a0", "#311b92",
|
||||||
|
"#3949ab", "#303f9f", "#283593", "#1a237e", "#1976d2", "#1565c0", "#0d47a1", "#0277bd", "#01579b")
|
||||||
|
private var meetingCurrIdx: Map[String, Int] = Map()
|
||||||
|
|
||||||
|
def nextColor(meetingId: String): String = {
|
||||||
|
val currentIdx = meetingCurrIdx.getOrElse(meetingId, 0)
|
||||||
|
|
||||||
|
val color = colors(currentIdx)
|
||||||
|
meetingCurrIdx += meetingId -> (currentIdx + 1) % colors.length
|
||||||
|
color
|
||||||
|
}
|
||||||
|
|
||||||
|
def reset(meetingId: String): Unit = {
|
||||||
|
meetingCurrIdx -= meetingId
|
||||||
|
}
|
||||||
|
}
|
@ -13,5 +13,6 @@ object RandomStringGenerator {
|
|||||||
// Generate a random alphabnumeric string of length n
|
// Generate a random alphabnumeric string of length n
|
||||||
def randomAlphanumericString(n: Int) =
|
def randomAlphanumericString(n: Int) =
|
||||||
randomString("abcdefghijklmnopqrstuvwxyz0123456789")(n)
|
randomString("abcdefghijklmnopqrstuvwxyz0123456789")(n)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging {
|
|||||||
case m: UserDisconnectedFromGlobalAudioMsg => logMessage(msg)
|
case m: UserDisconnectedFromGlobalAudioMsg => logMessage(msg)
|
||||||
case m: AssignPresenterReqMsg => logMessage(msg)
|
case m: AssignPresenterReqMsg => logMessage(msg)
|
||||||
case m: ChangeUserPinStateReqMsg => logMessage(msg)
|
case m: ChangeUserPinStateReqMsg => logMessage(msg)
|
||||||
|
case m: ChangeUserMobileFlagReqMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => logMessage(msg)
|
||||||
case m: ScreenshareRtmpBroadcastStartedEvtMsg => logMessage(msg)
|
case m: ScreenshareRtmpBroadcastStartedEvtMsg => logMessage(msg)
|
||||||
|
@ -42,6 +42,7 @@ trait GuestsWaitingApprovedMsgHdlr extends HandlerHelpers with RightsManagementT
|
|||||||
"none",
|
"none",
|
||||||
dialInUser.name,
|
dialInUser.name,
|
||||||
dialInUser.name,
|
dialInUser.name,
|
||||||
|
dialInUser.color,
|
||||||
MeetingStatus2x.isMeetingMuted(liveMeeting.status),
|
MeetingStatus2x.isMeetingMuted(liveMeeting.status),
|
||||||
false,
|
false,
|
||||||
"freeswitch"
|
"freeswitch"
|
||||||
|
@ -78,7 +78,7 @@ object MsgBuilder {
|
|||||||
val envelope = BbbCoreEnvelope(GetGuestsWaitingApprovalRespMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(GetGuestsWaitingApprovalRespMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(GetGuestsWaitingApprovalRespMsg.NAME, meetingId, userId)
|
val header = BbbClientMsgHeader(GetGuestsWaitingApprovalRespMsg.NAME, meetingId, userId)
|
||||||
|
|
||||||
val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.authenticated, g.registeredOn))
|
val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.color, g.authenticated, g.registeredOn))
|
||||||
val body = GetGuestsWaitingApprovalRespMsgBody(guestsWaiting)
|
val body = GetGuestsWaitingApprovalRespMsgBody(guestsWaiting)
|
||||||
val event = GetGuestsWaitingApprovalRespMsg(header, body)
|
val event = GetGuestsWaitingApprovalRespMsg(header, body)
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ object MsgBuilder {
|
|||||||
val envelope = BbbCoreEnvelope(GuestsWaitingForApprovalEvtMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(GuestsWaitingForApprovalEvtMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(GuestsWaitingForApprovalEvtMsg.NAME, meetingId, userId)
|
val header = BbbClientMsgHeader(GuestsWaitingForApprovalEvtMsg.NAME, meetingId, userId)
|
||||||
|
|
||||||
val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.authenticated, g.registeredOn))
|
val guestsWaiting = guests.map(g => GuestWaitingVO(g.intId, g.name, g.role, g.guest, g.avatar, g.color, g.authenticated, g.registeredOn))
|
||||||
val body = GuestsWaitingForApprovalEvtMsgBody(guestsWaiting)
|
val body = GuestsWaitingForApprovalEvtMsgBody(guestsWaiting)
|
||||||
val event = GuestsWaitingForApprovalEvtMsg(header, body)
|
val event = GuestsWaitingForApprovalEvtMsg(header, body)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ object UserJoinedMeetingEvtMsgBuilder {
|
|||||||
guestStatus = userState.guestStatus,
|
guestStatus = userState.guestStatus,
|
||||||
emoji = userState.emoji,
|
emoji = userState.emoji,
|
||||||
pin = userState.pin,
|
pin = userState.pin,
|
||||||
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar,
|
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar, color = userState.color,
|
||||||
clientType = userState.clientType)
|
clientType = userState.clientType)
|
||||||
|
|
||||||
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)
|
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)
|
||||||
|
@ -20,13 +20,13 @@ trait FakeTestData {
|
|||||||
val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false,
|
val guest1 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.WEBRTC, muted = false,
|
||||||
talking = false, listenOnly = false)
|
talking = false, listenOnly = false)
|
||||||
Users2x.add(liveMeeting.users2x, guest1)
|
Users2x.add(liveMeeting.users2x, guest1)
|
||||||
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", guest1.authed, System.currentTimeMillis())
|
val guestWait1 = GuestWaiting(guest1.intId, guest1.name, guest1.role, guest1.guest, "", "#ff6242", guest1.authed, System.currentTimeMillis())
|
||||||
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1)
|
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait1)
|
||||||
|
|
||||||
val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false,
|
val guest2 = createUserVoiceAndCam(liveMeeting, Roles.VIEWER_ROLE, guest = true, authed = true, CallingWith.FLASH, muted = false,
|
||||||
talking = false, listenOnly = false)
|
talking = false, listenOnly = false)
|
||||||
Users2x.add(liveMeeting.users2x, guest2)
|
Users2x.add(liveMeeting.users2x, guest2)
|
||||||
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", guest2.authed, System.currentTimeMillis())
|
val guestWait2 = GuestWaiting(guest2.intId, guest2.name, guest2.role, guest2.guest, "", "#ff6242", guest2.authed, System.currentTimeMillis())
|
||||||
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
|
GuestsWaiting.add(liveMeeting.guestsWaiting, guestWait2)
|
||||||
|
|
||||||
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
val vu1 = FakeUserGenerator.createFakeVoiceOnlyUser(CallingWith.PHONE, muted = false, talking = false, listenOnly = false)
|
||||||
@ -67,8 +67,8 @@ trait FakeTestData {
|
|||||||
|
|
||||||
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
|
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
|
||||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
|
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, pin = false,
|
||||||
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
mobile = false, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||||
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown",
|
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242", clientType = "unknown",
|
||||||
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
|
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,9 +52,10 @@ object FakeUserGenerator {
|
|||||||
val authToken = RandomStringGenerator.randomAlphanumericString(16)
|
val authToken = RandomStringGenerator.randomAlphanumericString(16)
|
||||||
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
|
val avatarURL = "https://www." + RandomStringGenerator.randomAlphanumericString(32) + ".com/" +
|
||||||
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
RandomStringGenerator.randomAlphanumericString(10) + ".png"
|
||||||
|
val color = "#ff6242"
|
||||||
|
|
||||||
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
val ru = RegisteredUsers.create(userId = id, extId, name, role,
|
||||||
authToken, avatarURL, guest, authed, guestStatus = GuestStatus.ALLOW, false, false)
|
authToken, avatarURL, color, guest, authed, guestStatus = GuestStatus.ALLOW, false, false)
|
||||||
RegisteredUsers.add(users, ru)
|
RegisteredUsers.add(users, ru)
|
||||||
ru
|
ru
|
||||||
}
|
}
|
||||||
@ -64,7 +65,7 @@ object FakeUserGenerator {
|
|||||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||||
val lastFloorTime = System.currentTimeMillis().toString();
|
val lastFloorTime = System.currentTimeMillis().toString();
|
||||||
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
||||||
callerNum = user.name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
callerNum = user.name, "#ff6242", muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
||||||
@ -74,7 +75,7 @@ object FakeUserGenerator {
|
|||||||
val name = getRandomElement(firstNames, random) + " " + getRandomElement(lastNames, random)
|
val name = getRandomElement(firstNames, random) + " " + getRandomElement(lastNames, random)
|
||||||
val lastFloorTime = System.currentTimeMillis().toString();
|
val lastFloorTime = System.currentTimeMillis().toString();
|
||||||
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
||||||
callerNum = name, muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
callerNum = name, "#ff6242", muted, talking, listenOnly, "freeswitch", System.currentTimeMillis(), floor, lastFloorTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
|
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
|
||||||
|
@ -24,7 +24,7 @@ object TestDataGen {
|
|||||||
listenOnly: Boolean): VoiceUserState = {
|
listenOnly: Boolean): VoiceUserState = {
|
||||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||||
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
VoiceUserState(intId = user.id, voiceUserId = voiceUserId, callingWith, callerName = user.name,
|
||||||
callerNum = user.name, muted, talking, listenOnly)
|
callerNum = user.name, "#ff6242", muted, talking, listenOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
def createFakeVoiceOnlyUser(callingWith: String, muted: Boolean, talking: Boolean,
|
||||||
@ -32,7 +32,7 @@ object TestDataGen {
|
|||||||
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
val voiceUserId = RandomStringGenerator.randomAlphanumericString(8)
|
||||||
val intId = "v_" + RandomStringGenerator.randomAlphanumericString(16)
|
val intId = "v_" + RandomStringGenerator.randomAlphanumericString(16)
|
||||||
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
VoiceUserState(intId, voiceUserId = voiceUserId, callingWith, callerName = name,
|
||||||
callerNum = name, muted, talking, listenOnly)
|
callerNum = name, "#ff6242", muted, talking, listenOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
|
def createFakeWebcamStreamFor(userId: String, subscribers: Set[String]): WebcamStream = {
|
||||||
@ -43,8 +43,8 @@ object TestDataGen {
|
|||||||
def createUserFor(liveMeeting: LiveMeeting, regUser: RegisteredUser, presenter: Boolean): UserState = {
|
def createUserFor(liveMeeting: LiveMeeting, regUser: RegisteredUser, presenter: Boolean): UserState = {
|
||||||
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
val u = UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||||
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||||
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown",
|
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, color = "#ff6242",
|
||||||
userLeftFlag = UserLeftFlag(false, 0))
|
clientType = "unknown", userLeftFlag = UserLeftFlag(false, 0))
|
||||||
Users2x.add(liveMeeting.users2x, u)
|
Users2x.add(liveMeeting.users2x, u)
|
||||||
u
|
u
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ case class GetGuestsWaitingApprovalRespMsg(
|
|||||||
body: GetGuestsWaitingApprovalRespMsgBody
|
body: GetGuestsWaitingApprovalRespMsgBody
|
||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class GetGuestsWaitingApprovalRespMsgBody(guests: Vector[GuestWaitingVO])
|
case class GetGuestsWaitingApprovalRespMsgBody(guests: Vector[GuestWaitingVO])
|
||||||
case class GuestWaitingVO(intId: String, name: String, role: String, guest: Boolean, avatar: String, authenticated: Boolean, registeredOn: Long)
|
case class GuestWaitingVO(intId: String, name: String, role: String, guest: Boolean, avatar: String, color: String, authenticated: Boolean, registeredOn: Long)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message sent to client for list of guest waiting for approval. This is sent when
|
* Message sent to client for list of guest waiting for approval. This is sent when
|
||||||
|
@ -88,11 +88,22 @@ case class UserJoinedMeetingEvtMsg(
|
|||||||
header: BbbClientMsgHeader,
|
header: BbbClientMsgHeader,
|
||||||
body: UserJoinedMeetingEvtMsgBody
|
body: UserJoinedMeetingEvtMsgBody
|
||||||
) extends BbbCoreMsg
|
) extends BbbCoreMsg
|
||||||
case class UserJoinedMeetingEvtMsgBody(intId: String, extId: String, name: String, role: String,
|
case class UserJoinedMeetingEvtMsgBody(
|
||||||
guest: Boolean, authed: Boolean, guestStatus: String,
|
intId: String,
|
||||||
emoji: String,
|
extId: String,
|
||||||
pin: Boolean,
|
name: String,
|
||||||
presenter: Boolean, locked: Boolean, avatar: String, clientType: String)
|
role: String,
|
||||||
|
guest: Boolean,
|
||||||
|
authed: Boolean,
|
||||||
|
guestStatus: String,
|
||||||
|
emoji: String,
|
||||||
|
pin: Boolean,
|
||||||
|
presenter: Boolean,
|
||||||
|
locked: Boolean,
|
||||||
|
avatar: String,
|
||||||
|
color: String,
|
||||||
|
clientType: String
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent by client to get all users in a meeting.
|
* Sent by client to get all users in a meeting.
|
||||||
@ -189,6 +200,20 @@ object UserEmojiChangedEvtMsg { val NAME = "UserEmojiChangedEvtMsg" }
|
|||||||
case class UserEmojiChangedEvtMsg(header: BbbClientMsgHeader, body: UserEmojiChangedEvtMsgBody) extends BbbCoreMsg
|
case class UserEmojiChangedEvtMsg(header: BbbClientMsgHeader, body: UserEmojiChangedEvtMsgBody) extends BbbCoreMsg
|
||||||
case class UserEmojiChangedEvtMsgBody(userId: String, emoji: String)
|
case class UserEmojiChangedEvtMsgBody(userId: String, emoji: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent from client about a user mobile flag.
|
||||||
|
*/
|
||||||
|
object ChangeUserMobileFlagReqMsg { val NAME = "ChangeUserMobileFlagReqMsg" }
|
||||||
|
case class ChangeUserMobileFlagReqMsg(header: BbbClientMsgHeader, body: ChangeUserMobileFlagReqMsgBody) extends StandardMsg
|
||||||
|
case class ChangeUserMobileFlagReqMsgBody(userId: String, mobile: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent to all clients about a user mobile flag.
|
||||||
|
*/
|
||||||
|
object UserMobileFlagChangedEvtMsg { val NAME = "UserMobileFlagChangedEvtMsg" }
|
||||||
|
case class UserMobileFlagChangedEvtMsg(header: BbbClientMsgHeader, body: UserMobileFlagChangedEvtMsgBody) extends BbbCoreMsg
|
||||||
|
case class UserMobileFlagChangedEvtMsgBody(userId: String, mobile: Boolean)
|
||||||
|
|
||||||
object AssignPresenterReqMsg { val NAME = "AssignPresenterReqMsg" }
|
object AssignPresenterReqMsg { val NAME = "AssignPresenterReqMsg" }
|
||||||
case class AssignPresenterReqMsg(header: BbbClientMsgHeader, body: AssignPresenterReqMsgBody) extends StandardMsg
|
case class AssignPresenterReqMsg(header: BbbClientMsgHeader, body: AssignPresenterReqMsgBody) extends StandardMsg
|
||||||
case class AssignPresenterReqMsgBody(requesterId: String, newPresenterId: String, newPresenterName: String, assignedBy: String)
|
case class AssignPresenterReqMsgBody(requesterId: String, newPresenterId: String, newPresenterName: String, assignedBy: String)
|
||||||
@ -348,7 +373,7 @@ object GetVoiceUsersMeetingRespMsg {
|
|||||||
case class GetVoiceUsersMeetingRespMsg(header: BbbClientMsgHeader, body: GetVoiceUsersMeetingRespMsgBody) extends BbbCoreMsg
|
case class GetVoiceUsersMeetingRespMsg(header: BbbClientMsgHeader, body: GetVoiceUsersMeetingRespMsgBody) extends BbbCoreMsg
|
||||||
case class GetVoiceUsersMeetingRespMsgBody(users: Vector[VoiceConfUser])
|
case class GetVoiceUsersMeetingRespMsgBody(users: Vector[VoiceConfUser])
|
||||||
case class VoiceConfUser(intId: String, voiceUserId: String, callingWith: String, callerName: String,
|
case class VoiceConfUser(intId: String, voiceUserId: String, callingWith: String, callerName: String,
|
||||||
callerNum: String, muted: Boolean, talking: Boolean, listenOnly: Boolean)
|
callerNum: String, color: String, muted: Boolean, talking: Boolean, listenOnly: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sent from client to add user to the presenter group of a meeting.
|
* Sent from client to add user to the presenter group of a meeting.
|
||||||
|
@ -408,7 +408,7 @@ case class UserJoinedVoiceConfEvtMsgBody(voiceConf: String, voiceUserId: String,
|
|||||||
object UserJoinedVoiceConfToClientEvtMsg { val NAME = "UserJoinedVoiceConfToClientEvtMsg" }
|
object UserJoinedVoiceConfToClientEvtMsg { val NAME = "UserJoinedVoiceConfToClientEvtMsg" }
|
||||||
case class UserJoinedVoiceConfToClientEvtMsg(header: BbbClientMsgHeader, body: UserJoinedVoiceConfToClientEvtMsgBody) extends BbbCoreMsg
|
case class UserJoinedVoiceConfToClientEvtMsg(header: BbbClientMsgHeader, body: UserJoinedVoiceConfToClientEvtMsgBody) extends BbbCoreMsg
|
||||||
case class UserJoinedVoiceConfToClientEvtMsgBody(voiceConf: String, intId: String, voiceUserId: String, callerName: String,
|
case class UserJoinedVoiceConfToClientEvtMsgBody(voiceConf: String, intId: String, voiceUserId: String, callerName: String,
|
||||||
callerNum: String, muted: Boolean,
|
callerNum: String, color: String, muted: Boolean,
|
||||||
talking: Boolean, callingWith: String, listenOnly: Boolean)
|
talking: Boolean, callingWith: String, listenOnly: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -ex
|
set -ex
|
||||||
RELEASE=4.0.1
|
RELEASE=4.0.2
|
||||||
cat <<MSG
|
cat <<MSG
|
||||||
This tool downloads prebuilt packages built on Github Actions
|
This tool downloads prebuilt packages built on Github Actions
|
||||||
The corresponding source can be browsed at https://github.com/bigbluebutton/bbb-presentation-video/tree/${RELEASE}
|
The corresponding source can be browsed at https://github.com/bigbluebutton/bbb-presentation-video/tree/${RELEASE}
|
||||||
|
1
bbb-webrtc-recorder.placeholder.sh
Executable file
1
bbb-webrtc-recorder.placeholder.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
git clone --branch v0.2.0 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-recorder bbb-webrtc-recorder
|
@ -1 +1 @@
|
|||||||
git clone --branch v2.10.0-alpha.1 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
git clone --branch v2.10.0-alpha.2 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
|
||||||
|
@ -44,9 +44,10 @@ fi
|
|||||||
|
|
||||||
HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $tmpfile $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
|
HOST=$(cat $SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties $tmpfile $BBB_WEB_ETC_CONFIG | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}' | tail -n 1)
|
||||||
|
|
||||||
HTML5_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml
|
HTML5_CONFIG=/etc/bigbluebutton/bbb-html5.yml
|
||||||
BBB_WEB_CONFIG=$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties
|
if [ ! -f "${HTML5_CONFIG}" ]; then
|
||||||
|
touch $HTML5_CONFIG
|
||||||
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
# Enable Looging of the HTML5 client for debugging
|
# Enable Looging of the HTML5 client for debugging
|
||||||
|
@ -154,6 +154,14 @@ get_bbb_web_config_value() {
|
|||||||
|
|
||||||
RECORD_CONFIG=/usr/local/bigbluebutton/core/scripts/bigbluebutton.yml
|
RECORD_CONFIG=/usr/local/bigbluebutton/core/scripts/bigbluebutton.yml
|
||||||
|
|
||||||
|
WEBRTC_RECORDER_DEFAULT_CONFIG=/etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml
|
||||||
|
WEBRTC_RECORDER_ETC_CONFIG=/etc/bigbluebutton/bbb-webrtc-recorder.yml
|
||||||
|
if [ -f $WEBRTC_RECORDER_ETC_CONFIG ]; then
|
||||||
|
WEBRTC_RECORDER_CONFIG=$(yq m -x $WEBRTC_RECORDER_DEFAULT_CONFIG $WEBRTC_RECORDER_ETC_CONFIG)
|
||||||
|
else
|
||||||
|
WEBRTC_RECORDER_CONFIG=$(yq r $WEBRTC_RECORDER_DEFAULT_CONFIG)
|
||||||
|
fi
|
||||||
|
|
||||||
HTML5_DEFAULT_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml
|
HTML5_DEFAULT_CONFIG=/usr/share/meteor/bundle/programs/server/assets/app/config/settings.yml
|
||||||
HTML5_ETC_CONFIG=/etc/bigbluebutton/bbb-html5.yml
|
HTML5_ETC_CONFIG=/etc/bigbluebutton/bbb-html5.yml
|
||||||
if [ -f $HTML5_ETC_CONFIG ]; then
|
if [ -f $HTML5_ETC_CONFIG ]; then
|
||||||
@ -407,16 +415,7 @@ display_bigbluebutton_status () {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
|
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
|
||||||
units="$units mongod bbb-html5 bbb-webrtc-sfu kurento-media-server"
|
units="$units mongod bbb-html5"
|
||||||
|
|
||||||
for i in `seq 8888 8890`; do
|
|
||||||
# check if multi-kurento setup is configured
|
|
||||||
if [ -f /usr/lib/systemd/system/kurento-media-server-${i}.service ]; then
|
|
||||||
if systemctl is-enabled kurento-media-server-${i}.service > /dev/null; then
|
|
||||||
units="$units kurento-media-server-${i}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
source /usr/share/meteor/bundle/bbb-html5-with-roles.conf
|
source /usr/share/meteor/bundle/bbb-html5-with-roles.conf
|
||||||
|
|
||||||
@ -433,6 +432,27 @@ display_bigbluebutton_status () {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -f /usr/lib/systemd/system/bbb-webrtc-sfu.service ]; then
|
||||||
|
units="$units bbb-webrtc-sfu"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /usr/lib/systemd/system/bbb-webrtc-recorder.service ]; then
|
||||||
|
units="$units bbb-webrtc-recorder"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /usr/lib/systemd/system/kurento-media-server.service ]; then
|
||||||
|
units="$units kurento-media-server"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in `seq 8888 8890`; do
|
||||||
|
# check if multi-kurento setup is configured
|
||||||
|
if [ -f /usr/lib/systemd/system/kurento-media-server-${i}.service ]; then
|
||||||
|
if systemctl is-enabled kurento-media-server-${i}.service > /dev/null; then
|
||||||
|
units="$units kurento-media-server-${i}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [ -f /usr/share/etherpad-lite/settings.json ]; then
|
if [ -f /usr/share/etherpad-lite/settings.json ]; then
|
||||||
units="$units etherpad"
|
units="$units etherpad"
|
||||||
fi
|
fi
|
||||||
@ -706,6 +726,9 @@ if [[ $PORT_RANGE ]]; then
|
|||||||
yq w -i $WEBRTC_SFU_ETC_CONFIG mediasoup.worker.rtcMinPort $START_PORT
|
yq w -i $WEBRTC_SFU_ETC_CONFIG mediasoup.worker.rtcMinPort $START_PORT
|
||||||
yq w -i $WEBRTC_SFU_ETC_CONFIG mediasoup.worker.rtcMaxPort $END_PORT
|
yq w -i $WEBRTC_SFU_ETC_CONFIG mediasoup.worker.rtcMaxPort $END_PORT
|
||||||
|
|
||||||
|
yq w -i $WEBRTC_RECORDER_DEFAULT_CONFIG webrtc.rtcMinPort $START_PORT
|
||||||
|
yq w -i $WEBRTC_RECORDER_DEFAULT_CONFIG webrtc.rtcMaxPort $END_PORT
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "BigBlueButton's UDP port range is now $START_PORT-$END_PORT"
|
echo "BigBlueButton's UDP port range is now $START_PORT-$END_PORT"
|
||||||
echo
|
echo
|
||||||
@ -744,14 +767,18 @@ check_configuration() {
|
|||||||
# Look for properties with no values set
|
# Look for properties with no values set
|
||||||
#
|
#
|
||||||
CONFIG_FILES="$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties"
|
CONFIG_FILES="$SERVLET_DIR/WEB-INF/classes/bigbluebutton.properties"
|
||||||
|
ignore_configs_args=()
|
||||||
|
ignore_configs_args+=(-e "redis.pass")
|
||||||
|
ignore_configs_args+=(-e "redisPassword")
|
||||||
|
ignore_configs_args+=(-e "disabledFeatures")
|
||||||
|
|
||||||
for file in $CONFIG_FILES ; do
|
for file in $CONFIG_FILES ; do
|
||||||
if [ ! -f $file ]; then
|
if [ ! -f $file ]; then
|
||||||
echo "# Error: File not found: $file"
|
echo "# Error: File not found: $file"
|
||||||
else
|
else
|
||||||
if cat $file | grep -v redis.pass | grep -v redisPassword | grep -v ^# | grep -q "^[^=]*=[ ]*$"; then
|
if cat $file | grep -v "${ignore_configs_args[@]}" | grep -v ^# | grep -q "^[^=]*=[ ]*$"; then
|
||||||
echo "# The following properties in $file have no value:"
|
echo "# The following properties in $file have no value:"
|
||||||
echo "# $(grep '^[^=#]*=[ ]*$' $file | grep -v redis.pass | grep -v redisPassword | sed 's/=//g')"
|
echo "# $(grep '^[^=#]*=[ ]*$' $file | grep -v "${ignore_configs_args[@]}" | sed 's/=//g')"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@ -1393,6 +1420,8 @@ if [ $CHECK ]; then
|
|||||||
echo " kurento: $(awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /minPort/) print $2}' /etc/kurento/modules/kurento/BaseRtpEndpoint.conf.ini)-$(awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /maxPort/) print $2}' /etc/kurento/modules/kurento/BaseRtpEndpoint.conf.ini)"
|
echo " kurento: $(awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /minPort/) print $2}' /etc/kurento/modules/kurento/BaseRtpEndpoint.conf.ini)-$(awk -F '=' '{if (! ($0 ~ /^;/) && $0 ~ /maxPort/) print $2}' /etc/kurento/modules/kurento/BaseRtpEndpoint.conf.ini)"
|
||||||
|
|
||||||
echo " bbb-webrtc-sfu: $(echo "$WEBRTC_SFU_CONFIG" | yq r - mediasoup.worker.rtcMinPort)-$(echo "$WEBRTC_SFU_CONFIG" | yq r - mediasoup.worker.rtcMaxPort)"
|
echo " bbb-webrtc-sfu: $(echo "$WEBRTC_SFU_CONFIG" | yq r - mediasoup.worker.rtcMinPort)-$(echo "$WEBRTC_SFU_CONFIG" | yq r - mediasoup.worker.rtcMaxPort)"
|
||||||
|
echo " bbb-webrtc-recorder: $(echo "$WEBRTC_RECORDER_CONFIG" | yq r - webrtc.rtcMinPort)-$(echo "$WEBRTC_RECORDER_CONFIG" | yq r - webrtc.rtcMaxPort)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then
|
# if [ -f ${LTI_DIR}/WEB-INF/classes/lti-config.properties ]; then
|
||||||
@ -1427,10 +1456,20 @@ if [ $CHECK ]; then
|
|||||||
echo " kurento.ip: $(echo "$WEBRTC_SFU_CONFIG" | yq r - kurento[0].ip)"
|
echo " kurento.ip: $(echo "$WEBRTC_SFU_CONFIG" | yq r - kurento[0].ip)"
|
||||||
echo " kurento.url: $(echo "$WEBRTC_SFU_CONFIG" | yq r - kurento[0].url)"
|
echo " kurento.url: $(echo "$WEBRTC_SFU_CONFIG" | yq r - kurento[0].url)"
|
||||||
echo " freeswitch.sip_ip: $(echo "$WEBRTC_SFU_CONFIG" | yq r - freeswitch.sip_ip)"
|
echo " freeswitch.sip_ip: $(echo "$WEBRTC_SFU_CONFIG" | yq r - freeswitch.sip_ip)"
|
||||||
|
echo " recordingAdapter: $(echo "$WEBRTC_SFU_CONFIG" | yq r - recordingAdapter)"
|
||||||
echo " recordScreenSharing: $(echo "$WEBRTC_SFU_CONFIG" | yq r - recordScreenSharing)"
|
echo " recordScreenSharing: $(echo "$WEBRTC_SFU_CONFIG" | yq r - recordScreenSharing)"
|
||||||
echo " recordWebcams: $(echo "$WEBRTC_SFU_CONFIG" | yq r - recordWebcams)"
|
echo " recordWebcams: $(echo "$WEBRTC_SFU_CONFIG" | yq r - recordWebcams)"
|
||||||
echo " codec_video_main: $(echo "$WEBRTC_SFU_CONFIG" | yq r - conference-media-specs.codec_video_main)"
|
echo " codec_video_main: $(echo "$WEBRTC_SFU_CONFIG" | yq r - conference-media-specs.codec_video_main)"
|
||||||
echo " codec_video_content: $(echo "$WEBRTC_SFU_CONFIG" | yq r - conference-media-specs.codec_video_content)"
|
echo " codec_video_content: $(echo "$WEBRTC_SFU_CONFIG" | yq r - conference-media-specs.codec_video_content)"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$WEBRTC_RECORDER_CONFIG" ]; then
|
||||||
|
echo
|
||||||
|
echo "/etc/bbb-webrtc-recorder/bbb-webrtc-recorder.yml (bbb-webrtc-recorder)"
|
||||||
|
echo "/etc/bigbluebutton/bbb-webrtc-recorder.yml (bbb-webrtc-recorder - override)"
|
||||||
|
echo " debug: $(echo "$WEBRTC_RECORDER_CONFIG" | yq r - debug)"
|
||||||
|
echo " recorder.directory: $(echo "$WEBRTC_RECORDER_CONFIG" | yq r - recorder.directory)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$HTML5_CONFIG" ]; then
|
if [ -n "$HTML5_CONFIG" ]; then
|
||||||
|
@ -108,6 +108,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|||||||
background-color: rgba(66, 133, 244, 1) !important;
|
background-color: rgba(66, 133, 244, 1) !important;
|
||||||
color: #FFF !important;
|
color: #FFF !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-out {
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('gesturestart', function (e) {
|
document.addEventListener('gesturestart', function (e) {
|
||||||
@ -149,4 +157,5 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|||||||
<span id="destination"></span>
|
<span id="destination"></span>
|
||||||
<audio id="remote-media" autoplay>
|
<audio id="remote-media" autoplay>
|
||||||
</audio>
|
</audio>
|
||||||
|
<div id="modals-container"></div>
|
||||||
</body>
|
</body>
|
||||||
|
@ -348,3 +348,9 @@
|
|||||||
.icon-bbb-closed_caption_stop:before {
|
.icon-bbb-closed_caption_stop:before {
|
||||||
content: "\e966";
|
content: "\e966";
|
||||||
}
|
}
|
||||||
|
.icon-bbb-link:before {
|
||||||
|
content: "\e967";
|
||||||
|
}
|
||||||
|
.icon-bbb-manage_layout:before {
|
||||||
|
content: "\e968";
|
||||||
|
}
|
||||||
|
@ -38,3 +38,39 @@
|
|||||||
border: none;
|
border: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prevents that an element within app shows over a modal */
|
||||||
|
#app {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-low {
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-medium {
|
||||||
|
z-index: 1002;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-high {
|
||||||
|
z-index: 1003;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Within a same priority, hide all but first (FIFO) */
|
||||||
|
.modal-low ~ .modal-low,
|
||||||
|
.modal-medium ~ .modal-medium,
|
||||||
|
.modal-high ~ .modal-high {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide all low priority modals when a medium or high priority modals are displayed */
|
||||||
|
#modals-container:has(.modal-medium) .modal-low,
|
||||||
|
#modals-container:has(.modal-high) .modal-low {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide all medium priority modals when a high priority modal is displayed */
|
||||||
|
#modals-container:has(.modal-high) .modal-medium {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@ -758,9 +758,18 @@ class SIPSession {
|
|||||||
|
|
||||||
const setupRemoteMedia = () => {
|
const setupRemoteMedia = () => {
|
||||||
const mediaElement = document.querySelector(MEDIA_TAG);
|
const mediaElement = document.querySelector(MEDIA_TAG);
|
||||||
|
const { sdp } = this.currentSession.sessionDescriptionHandler
|
||||||
|
.peerConnection.remoteDescription;
|
||||||
|
|
||||||
|
logger.info({
|
||||||
|
logCode: 'sip_js_session_setup_remote_media',
|
||||||
|
extraInfo: {
|
||||||
|
callerIdName: this.user.callerIdName,
|
||||||
|
sdp,
|
||||||
|
},
|
||||||
|
}, 'Audio call - setup remote media');
|
||||||
|
|
||||||
this.remoteStream = new MediaStream();
|
this.remoteStream = new MediaStream();
|
||||||
|
|
||||||
this.currentSession.sessionDescriptionHandler
|
this.currentSession.sessionDescriptionHandler
|
||||||
.peerConnection.getReceivers().forEach((receiver) => {
|
.peerConnection.getReceivers().forEach((receiver) => {
|
||||||
if (receiver.track) {
|
if (receiver.track) {
|
||||||
@ -792,22 +801,15 @@ class SIPSession {
|
|||||||
fsReady,
|
fsReady,
|
||||||
},
|
},
|
||||||
}, 'Audio call - check if ICE is finished and FreeSWITCH is ready');
|
}, 'Audio call - check if ICE is finished and FreeSWITCH is ready');
|
||||||
if (iceCompleted && fsReady) {
|
|
||||||
|
if (iceCompleted) {
|
||||||
this.webrtcConnected = true;
|
this.webrtcConnected = true;
|
||||||
setupRemoteMedia();
|
setupRemoteMedia();
|
||||||
|
}
|
||||||
|
|
||||||
const { sdp } = this.currentSession.sessionDescriptionHandler
|
if (fsReady) {
|
||||||
.peerConnection.remoteDescription;
|
|
||||||
|
|
||||||
logger.info({
|
|
||||||
logCode: 'sip_js_session_setup_remote_media',
|
|
||||||
extraInfo: {
|
|
||||||
callerIdName: this.user.callerIdName,
|
|
||||||
sdp,
|
|
||||||
},
|
|
||||||
}, 'Audio call - setup remote media');
|
|
||||||
|
|
||||||
this.callback({ status: this.baseCallStates.started, bridge: this.bridgeName });
|
this.callback({ status: this.baseCallStates.started, bridge: this.bridgeName });
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import stringHash from 'string-hash';
|
|
||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import GuestUsers from '/imports/api/guest-users/';
|
import GuestUsers from '/imports/api/guest-users/';
|
||||||
import updatePositionInWaitingQueue from '../methods/updatePositionInWaitingQueue';
|
import updatePositionInWaitingQueue from '../methods/updatePositionInWaitingQueue';
|
||||||
|
|
||||||
const COLOR_LIST = [
|
|
||||||
'#7b1fa2', '#6a1b9a', '#4a148c', '#5e35b1', '#512da8', '#4527a0',
|
|
||||||
'#311b92', '#3949ab', '#303f9f', '#283593', '#1a237e', '#1976d2', '#1565c0',
|
|
||||||
'#0d47a1', '#0277bd', '#01579b',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function handleGuestsWaitingForApproval({ body }, meetingId) {
|
export default async function handleGuestsWaitingForApproval({ body }, meetingId) {
|
||||||
const { guests } = body;
|
const { guests } = body;
|
||||||
check(guests, Array);
|
check(guests, Array);
|
||||||
@ -27,7 +20,6 @@ export default async function handleGuestsWaitingForApproval({ body }, meetingId
|
|||||||
meetingId,
|
meetingId,
|
||||||
loginTime: guest.registeredOn,
|
loginTime: guest.registeredOn,
|
||||||
privateGuestLobbyMessage: '',
|
privateGuestLobbyMessage: '',
|
||||||
color: COLOR_LIST[stringHash(guest.intId) % COLOR_LIST.length],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (insertedId) {
|
if (insertedId) {
|
||||||
|
@ -42,7 +42,7 @@ export default async function changeLayout(payload) {
|
|||||||
pushLayout: Boolean,
|
pushLayout: Boolean,
|
||||||
presentationIsOpen: Boolean,
|
presentationIsOpen: Boolean,
|
||||||
isResizing: Boolean,
|
isResizing: Boolean,
|
||||||
cameraPosition: String,
|
cameraPosition: Match.Maybe(String),
|
||||||
focusedCamera: String,
|
focusedCamera: String,
|
||||||
presentationVideoRate: Number,
|
presentationVideoRate: Number,
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,7 @@ export default function setPushLayout(payload) {
|
|||||||
check(requesterUserId, String);
|
check(requesterUserId, String);
|
||||||
|
|
||||||
check(payload, {
|
check(payload, {
|
||||||
pushLayout: Boolean,
|
pushLayout: Match.Maybe(Boolean),
|
||||||
});
|
});
|
||||||
|
|
||||||
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||||
|
@ -8,6 +8,7 @@ import handleEmojiStatus from './handlers/emojiStatus';
|
|||||||
import handleChangeRole from './handlers/changeRole';
|
import handleChangeRole from './handlers/changeRole';
|
||||||
import handleUserPinChanged from './handlers/userPinChanged';
|
import handleUserPinChanged from './handlers/userPinChanged';
|
||||||
import handleUserInactivityInspect from './handlers/userInactivityInspect';
|
import handleUserInactivityInspect from './handlers/userInactivityInspect';
|
||||||
|
import handleChangeMobileFlag from '/imports/api/users/server/handlers/changeMobileFlag';
|
||||||
|
|
||||||
RedisPubSub.on('PresenterAssignedEvtMsg', handlePresenterAssigned);
|
RedisPubSub.on('PresenterAssignedEvtMsg', handlePresenterAssigned);
|
||||||
RedisPubSub.on('UserJoinedMeetingEvtMsg', handleUserJoined);
|
RedisPubSub.on('UserJoinedMeetingEvtMsg', handleUserJoined);
|
||||||
@ -15,6 +16,7 @@ RedisPubSub.on('UserLeftMeetingEvtMsg', handleRemoveUser);
|
|||||||
RedisPubSub.on('ValidateAuthTokenRespMsg', handleValidateAuthToken);
|
RedisPubSub.on('ValidateAuthTokenRespMsg', handleValidateAuthToken);
|
||||||
RedisPubSub.on('UserEmojiChangedEvtMsg', handleEmojiStatus);
|
RedisPubSub.on('UserEmojiChangedEvtMsg', handleEmojiStatus);
|
||||||
RedisPubSub.on('UserRoleChangedEvtMsg', handleChangeRole);
|
RedisPubSub.on('UserRoleChangedEvtMsg', handleChangeRole);
|
||||||
|
RedisPubSub.on('UserMobileFlagChangedEvtMsg', handleChangeMobileFlag);
|
||||||
RedisPubSub.on('UserLeftFlagUpdatedEvtMsg', handleUserLeftFlagUpdated);
|
RedisPubSub.on('UserLeftFlagUpdatedEvtMsg', handleUserLeftFlagUpdated);
|
||||||
RedisPubSub.on('UserPinStateChangedEvtMsg', handleUserPinChanged);
|
RedisPubSub.on('UserPinStateChangedEvtMsg', handleUserPinChanged);
|
||||||
RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect);
|
RedisPubSub.on('UserInactivityInspectMsg', handleUserInactivityInspect);
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import { check } from 'meteor/check';
|
||||||
|
import setMobile from '/imports/api/users/server/modifiers/setMobile';
|
||||||
|
|
||||||
|
export default async function handleChangeMobileFlag(payload, meetingId) {
|
||||||
|
check(payload.body, Object);
|
||||||
|
check(meetingId, String);
|
||||||
|
|
||||||
|
const { userId: requesterUserId, mobile } = payload.body;
|
||||||
|
|
||||||
|
if (mobile) {
|
||||||
|
await setMobile(meetingId, requesterUserId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,27 @@
|
|||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import setMobile from '../modifiers/setMobile';
|
|
||||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||||
|
import RedisPubSub from '/imports/startup/server/redis';
|
||||||
|
|
||||||
export default async function setMobileUser() {
|
export default async function setMobileUser() {
|
||||||
try {
|
try {
|
||||||
|
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||||
|
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||||
|
const EVENT_NAME = 'ChangeUserMobileFlagReqMsg';
|
||||||
|
|
||||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||||
|
|
||||||
check(meetingId, String);
|
check(meetingId, String);
|
||||||
check(requesterUserId, String);
|
check(requesterUserId, String);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
userId: requesterUserId,
|
||||||
|
mobile: true,
|
||||||
|
};
|
||||||
|
|
||||||
Logger.verbose(`Mobile user ${requesterUserId} from meeting ${meetingId}`);
|
Logger.verbose(`Mobile user ${requesterUserId} from meeting ${meetingId}`);
|
||||||
|
|
||||||
await setMobile(meetingId, requesterUserId);
|
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.error(`Exception while invoking method setMobileUser ${err.stack}`);
|
Logger.error(`Exception while invoking method setMobileUser ${err.stack}`);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export default async function addDialInUser(meetingId, voiceUser) {
|
|||||||
const USER_CONFIG = Meteor.settings.public.user;
|
const USER_CONFIG = Meteor.settings.public.user;
|
||||||
const ROLE_VIEWER = USER_CONFIG.role_viewer;
|
const ROLE_VIEWER = USER_CONFIG.role_viewer;
|
||||||
|
|
||||||
const { intId, callerName } = voiceUser;
|
const { intId, callerName, color } = voiceUser;
|
||||||
|
|
||||||
const voiceOnlyUser = {
|
const voiceOnlyUser = {
|
||||||
intId,
|
intId,
|
||||||
@ -23,6 +23,7 @@ export default async function addDialInUser(meetingId, voiceUser) {
|
|||||||
presenter: false,
|
presenter: false,
|
||||||
locked: false, // TODO
|
locked: false, // TODO
|
||||||
avatar: '',
|
avatar: '',
|
||||||
|
color,
|
||||||
pin: false,
|
pin: false,
|
||||||
clientType: 'dial-in-user',
|
clientType: 'dial-in-user',
|
||||||
};
|
};
|
||||||
|
@ -4,18 +4,11 @@ import Users from '/imports/api/users';
|
|||||||
import Meetings from '/imports/api/meetings';
|
import Meetings from '/imports/api/meetings';
|
||||||
import VoiceUsers from '/imports/api/voice-users/';
|
import VoiceUsers from '/imports/api/voice-users/';
|
||||||
import addUserPsersistentData from '/imports/api/users-persistent-data/server/modifiers/addUserPersistentData';
|
import addUserPsersistentData from '/imports/api/users-persistent-data/server/modifiers/addUserPersistentData';
|
||||||
import stringHash from 'string-hash';
|
|
||||||
import flat from 'flat';
|
import flat from 'flat';
|
||||||
import { lowercaseTrim } from '/imports/utils/string-utils';
|
import { lowercaseTrim } from '/imports/utils/string-utils';
|
||||||
|
|
||||||
import addVoiceUser from '/imports/api/voice-users/server/modifiers/addVoiceUser';
|
import addVoiceUser from '/imports/api/voice-users/server/modifiers/addVoiceUser';
|
||||||
|
|
||||||
const COLOR_LIST = [
|
|
||||||
'#7b1fa2', '#6a1b9a', '#4a148c', '#5e35b1', '#512da8', '#4527a0',
|
|
||||||
'#311b92', '#3949ab', '#303f9f', '#283593', '#1a237e', '#1976d2', '#1565c0',
|
|
||||||
'#0d47a1', '#0277bd', '#01579b',
|
|
||||||
];
|
|
||||||
|
|
||||||
export default async function addUser(meetingId, userData) {
|
export default async function addUser(meetingId, userData) {
|
||||||
const user = userData;
|
const user = userData;
|
||||||
|
|
||||||
@ -34,6 +27,7 @@ export default async function addUser(meetingId, userData) {
|
|||||||
presenter: Boolean,
|
presenter: Boolean,
|
||||||
locked: Boolean,
|
locked: Boolean,
|
||||||
avatar: String,
|
avatar: String,
|
||||||
|
color: String,
|
||||||
pin: Boolean,
|
pin: Boolean,
|
||||||
clientType: String,
|
clientType: String,
|
||||||
});
|
});
|
||||||
@ -46,14 +40,9 @@ export default async function addUser(meetingId, userData) {
|
|||||||
};
|
};
|
||||||
const Meeting = await Meetings.findOneAsync({ meetingId });
|
const Meeting = await Meetings.findOneAsync({ meetingId });
|
||||||
|
|
||||||
/* While the akka-apps dont generate a color we just pick one
|
|
||||||
from a list based on the userId */
|
|
||||||
const color = COLOR_LIST[stringHash(user.intId) % COLOR_LIST.length];
|
|
||||||
|
|
||||||
const userInfos = {
|
const userInfos = {
|
||||||
meetingId,
|
meetingId,
|
||||||
sortName: lowercaseTrim(user.name),
|
sortName: lowercaseTrim(user.name),
|
||||||
color,
|
|
||||||
speechLocale: '',
|
speechLocale: '',
|
||||||
mobile: false,
|
mobile: false,
|
||||||
breakoutProps: {
|
breakoutProps: {
|
||||||
@ -81,6 +70,7 @@ export default async function addUser(meetingId, userData) {
|
|||||||
intId: userId,
|
intId: userId,
|
||||||
callerName: user.name,
|
callerName: user.name,
|
||||||
callerNum: '',
|
callerNum: '',
|
||||||
|
color: user.color,
|
||||||
muted: false,
|
muted: false,
|
||||||
talking: false,
|
talking: false,
|
||||||
callingWith: '',
|
callingWith: '',
|
||||||
|
@ -40,6 +40,7 @@ export default async function handleGetVoiceUsers({ body }, meetingId) {
|
|||||||
callerName: user.callerName,
|
callerName: user.callerName,
|
||||||
callerNum: user.callerNum,
|
callerNum: user.callerNum,
|
||||||
muted: user.muted,
|
muted: user.muted,
|
||||||
|
color: user.color,
|
||||||
talking: user.talking,
|
talking: user.talking,
|
||||||
callingWith: user.callingWith,
|
callingWith: user.callingWith,
|
||||||
listenOnly: user.listenOnly,
|
listenOnly: user.listenOnly,
|
||||||
|
@ -3,7 +3,6 @@ import Users from '/imports/api/users';
|
|||||||
import addDialInUser from '/imports/api/users/server/modifiers/addDialInUser';
|
import addDialInUser from '/imports/api/users/server/modifiers/addDialInUser';
|
||||||
import addVoiceUser from '../modifiers/addVoiceUser';
|
import addVoiceUser from '../modifiers/addVoiceUser';
|
||||||
|
|
||||||
|
|
||||||
export default async function handleJoinVoiceUser({ body }, meetingId) {
|
export default async function handleJoinVoiceUser({ body }, meetingId) {
|
||||||
const voiceUser = body;
|
const voiceUser = body;
|
||||||
voiceUser.joined = true;
|
voiceUser.joined = true;
|
||||||
@ -15,6 +14,7 @@ export default async function handleJoinVoiceUser({ body }, meetingId) {
|
|||||||
voiceUserId: String,
|
voiceUserId: String,
|
||||||
callerName: String,
|
callerName: String,
|
||||||
callerNum: String,
|
callerNum: String,
|
||||||
|
color: String,
|
||||||
muted: Boolean,
|
muted: Boolean,
|
||||||
talking: Boolean,
|
talking: Boolean,
|
||||||
callingWith: String,
|
callingWith: String,
|
||||||
|
@ -5,7 +5,6 @@ import removeVoiceUser from '../modifiers/removeVoiceUser';
|
|||||||
import updateVoiceUser from '../modifiers/updateVoiceUser';
|
import updateVoiceUser from '../modifiers/updateVoiceUser';
|
||||||
import addVoiceUser from '../modifiers/addVoiceUser';
|
import addVoiceUser from '../modifiers/addVoiceUser';
|
||||||
|
|
||||||
|
|
||||||
export default async function handleVoiceUsers({ header, body }) {
|
export default async function handleVoiceUsers({ header, body }) {
|
||||||
const { voiceUsers } = body;
|
const { voiceUsers } = body;
|
||||||
const { meetingId } = header;
|
const { meetingId } = header;
|
||||||
@ -38,6 +37,7 @@ export default async function handleVoiceUsers({ header, body }) {
|
|||||||
intId: voice.intId,
|
intId: voice.intId,
|
||||||
callerName: voice.callerName,
|
callerName: voice.callerName,
|
||||||
callerNum: voice.callerNum,
|
callerNum: voice.callerNum,
|
||||||
|
color: voice.color,
|
||||||
muted: voice.muted,
|
muted: voice.muted,
|
||||||
talking: voice.talking,
|
talking: voice.talking,
|
||||||
callingWith: voice.callingWith,
|
callingWith: voice.callingWith,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import VoiceUsers from '/imports/api/voice-users';
|
import VoiceUsers from '/imports/api/voice-users';
|
||||||
import Users from '/imports/api/users';
|
|
||||||
import flat from 'flat';
|
import flat from 'flat';
|
||||||
|
|
||||||
export default async function addVoiceUser(meetingId, voiceUser) {
|
export default async function addVoiceUser(meetingId, voiceUser) {
|
||||||
@ -11,6 +10,7 @@ export default async function addVoiceUser(meetingId, voiceUser) {
|
|||||||
intId: String,
|
intId: String,
|
||||||
callerName: String,
|
callerName: String,
|
||||||
callerNum: String,
|
callerNum: String,
|
||||||
|
color: String,
|
||||||
muted: Boolean,
|
muted: Boolean,
|
||||||
talking: Boolean,
|
talking: Boolean,
|
||||||
callingWith: String,
|
callingWith: String,
|
||||||
@ -27,19 +27,12 @@ export default async function addVoiceUser(meetingId, voiceUser) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const modifier = {
|
const modifier = {
|
||||||
$set: Object.assign(
|
$set: {
|
||||||
{ meetingId, spoke: talking },
|
meetingId,
|
||||||
flat(voiceUser),
|
spoke: talking,
|
||||||
),
|
...flat(voiceUser),
|
||||||
};
|
|
||||||
|
|
||||||
const user = await Users.findOneAsync({ meetingId, userId: intId }, {
|
|
||||||
fields: {
|
|
||||||
color: 1,
|
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
if (user) modifier.$set.color = user.color;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { numberAffected } = await VoiceUsers.upsertAsync(selector, modifier);
|
const { numberAffected } = await VoiceUsers.upsertAsync(selector, modifier);
|
||||||
|
@ -2,8 +2,6 @@ import VoiceUsers from '/imports/api/voice-users';
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||||
import ejectUserFromVoice from './methods/ejectUserFromVoice';
|
|
||||||
import { debounce } from 'radash';
|
|
||||||
|
|
||||||
async function voiceUser() {
|
async function voiceUser() {
|
||||||
const tokenValidation = await AuthTokenValidation
|
const tokenValidation = await AuthTokenValidation
|
||||||
@ -15,22 +13,8 @@ async function voiceUser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { meetingId, userId: requesterUserId } = tokenValidation;
|
const { meetingId, userId: requesterUserId } = tokenValidation;
|
||||||
|
|
||||||
const onCloseConnection = Meteor.bindEnvironment(async () => {
|
|
||||||
try {
|
|
||||||
// I used user because voiceUser is the function's name
|
|
||||||
const User = await VoiceUsers.findOneAsync({ meetingId, requesterUserId });
|
|
||||||
if (User) {
|
|
||||||
await ejectUserFromVoice(requesterUserId);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
Logger.error(`Exception while executing ejectUserFromVoice for ${requesterUserId}: ${e}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Logger.debug('Publishing Voice User', { meetingId, requesterUserId });
|
Logger.debug('Publishing Voice User', { meetingId, requesterUserId });
|
||||||
|
|
||||||
this._session.socket.on('close', debounce({ delay: 100 }, onCloseConnection));
|
|
||||||
return VoiceUsers.find({ meetingId });
|
return VoiceUsers.find({ meetingId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
title: {
|
title: {
|
||||||
@ -38,7 +38,8 @@ const intlMessages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const AboutComponent = ({ intl, settings }) => {
|
const AboutComponent = (props) => {
|
||||||
|
const { intl, settings, isOpen, onRequestClose, priority, } = props;
|
||||||
const {
|
const {
|
||||||
html5ClientBuild,
|
html5ClientBuild,
|
||||||
copyright,
|
copyright,
|
||||||
@ -54,20 +55,25 @@ const AboutComponent = ({ intl, settings }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalSimple
|
||||||
data-test="aboutModalTitleLabel"
|
data-test="aboutModalTitleLabel"
|
||||||
title={intl.formatMessage(intlMessages.title)}
|
title={intl.formatMessage(intlMessages.title)}
|
||||||
dismiss={{
|
dismiss={{
|
||||||
label: intl.formatMessage(intlMessages.dismissLabel),
|
label: intl.formatMessage(intlMessages.dismissLabel),
|
||||||
description: intl.formatMessage(intlMessages.dismissDesc),
|
description: intl.formatMessage(intlMessages.dismissDesc),
|
||||||
}}
|
}}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{`${intl.formatMessage(intlMessages.copyright)} ${copyright}`}
|
{`${intl.formatMessage(intlMessages.copyright)} ${copyright}`}
|
||||||
<br />
|
<br />
|
||||||
{`${intl.formatMessage(intlMessages.version)} ${html5ClientBuild}`}
|
{`${intl.formatMessage(intlMessages.version)} ${html5ClientBuild}`}
|
||||||
{displayBbbServerVersion ? showLabelVersion() : null}
|
{displayBbbServerVersion ? showLabelVersion() : null}
|
||||||
|
|
||||||
</Modal>
|
</ModalSimple>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
|
import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
|
||||||
import ExternalVideoModal from '/imports/ui/components/external-video-player/modal/container';
|
import ExternalVideoModal from '/imports/ui/components/external-video-player/modal/container';
|
||||||
import RandomUserSelectContainer from '/imports/ui/components/common/modal/random-user/container';
|
import RandomUserSelectContainer from '/imports/ui/components/common/modal/random-user/container';
|
||||||
@ -19,7 +18,6 @@ const propTypes = {
|
|||||||
intl: PropTypes.shape({
|
intl: PropTypes.shape({
|
||||||
formatMessage: PropTypes.func.isRequired,
|
formatMessage: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
mountModal: PropTypes.func.isRequired,
|
|
||||||
amIModerator: PropTypes.bool.isRequired,
|
amIModerator: PropTypes.bool.isRequired,
|
||||||
shortcuts: PropTypes.string,
|
shortcuts: PropTypes.string,
|
||||||
handleTakePresenter: PropTypes.func.isRequired,
|
handleTakePresenter: PropTypes.func.isRequired,
|
||||||
@ -109,22 +107,29 @@ class ActionsDropdown extends PureComponent {
|
|||||||
this.pollId = uniqueId('action-item-');
|
this.pollId = uniqueId('action-item-');
|
||||||
this.takePresenterId = uniqueId('action-item-');
|
this.takePresenterId = uniqueId('action-item-');
|
||||||
this.selectUserRandId = uniqueId('action-item-');
|
this.selectUserRandId = uniqueId('action-item-');
|
||||||
|
this.state = {
|
||||||
|
isExternalVideoModalOpen: false,
|
||||||
|
isRandomUserSelectModalOpen: false,
|
||||||
|
isLayoutModalOpen: false,
|
||||||
|
}
|
||||||
|
|
||||||
this.handleExternalVideoClick = this.handleExternalVideoClick.bind(this);
|
this.handleExternalVideoClick = this.handleExternalVideoClick.bind(this);
|
||||||
this.makePresentationItems = this.makePresentationItems.bind(this);
|
this.makePresentationItems = this.makePresentationItems.bind(this);
|
||||||
|
this.setExternalVideoModalIsOpen = this.setExternalVideoModalIsOpen.bind(this);
|
||||||
|
this.setRandomUserSelectModalIsOpen = this.setRandomUserSelectModalIsOpen.bind(this);
|
||||||
|
this.setLayoutModalIsOpen = this.setLayoutModalIsOpen.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { amIPresenter: wasPresenter } = prevProps;
|
const { amIPresenter: wasPresenter } = prevProps;
|
||||||
const { amIPresenter: isPresenter, mountModal } = this.props;
|
const { amIPresenter: isPresenter } = this.props;
|
||||||
if (wasPresenter && !isPresenter) {
|
if (wasPresenter && !isPresenter) {
|
||||||
mountModal(null);
|
this.setExternalVideoModalIsOpen(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleExternalVideoClick() {
|
handleExternalVideoClick() {
|
||||||
const { mountModal } = this.props;
|
this.setExternalVideoModalIsOpen(true);
|
||||||
mountModal(<ExternalVideoModal />);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailableActions() {
|
getAvailableActions() {
|
||||||
@ -137,7 +142,6 @@ class ActionsDropdown extends PureComponent {
|
|||||||
isPollingEnabled,
|
isPollingEnabled,
|
||||||
isSelectRandomUserEnabled,
|
isSelectRandomUserEnabled,
|
||||||
stopExternalVideoShare,
|
stopExternalVideoShare,
|
||||||
mountModal,
|
|
||||||
layoutContextDispatch,
|
layoutContextDispatch,
|
||||||
setMeetingLayout,
|
setMeetingLayout,
|
||||||
setPushLayout,
|
setPushLayout,
|
||||||
@ -216,7 +220,7 @@ class ActionsDropdown extends PureComponent {
|
|||||||
icon: "user",
|
icon: "user",
|
||||||
label: intl.formatMessage(intlMessages.selectRandUserLabel),
|
label: intl.formatMessage(intlMessages.selectRandUserLabel),
|
||||||
key: this.selectUserRandId,
|
key: this.selectUserRandId,
|
||||||
onClick: () => mountModal(<RandomUserSelectContainer isSelectedUser={false} />),
|
onClick: () => this.setRandomUserSelectModalIsOpen(true),
|
||||||
dataTest: "selectRandomUser",
|
dataTest: "selectRandomUser",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -236,7 +240,7 @@ class ActionsDropdown extends PureComponent {
|
|||||||
icon: 'send',
|
icon: 'send',
|
||||||
label: intl.formatMessage(intlMessages.layoutModal),
|
label: intl.formatMessage(intlMessages.layoutModal),
|
||||||
key: 'layoutModal',
|
key: 'layoutModal',
|
||||||
onClick: () => mountModal(<LayoutModalContainer {...this.props} />),
|
onClick: () => this.setLayoutModalIsOpen(true),
|
||||||
dataTest: 'layoutModal',
|
dataTest: 'layoutModal',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -281,6 +285,27 @@ class ActionsDropdown extends PureComponent {
|
|||||||
return presentationItemElements;
|
return presentationItemElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setExternalVideoModalIsOpen(value) {
|
||||||
|
this.setState({isExternalVideoModalOpen: value});
|
||||||
|
}
|
||||||
|
setRandomUserSelectModalIsOpen(value) {
|
||||||
|
this.setState({isRandomUserSelectModalOpen: value});
|
||||||
|
}
|
||||||
|
setLayoutModalIsOpen(value) {
|
||||||
|
this.setState({isLayoutModalOpen: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderModal(isOpen, setIsOpen, priority, Component) {
|
||||||
|
return isOpen ? <Component
|
||||||
|
{...{
|
||||||
|
onRequestClose: () => setIsOpen(false),
|
||||||
|
priority,
|
||||||
|
setIsOpen,
|
||||||
|
isOpen
|
||||||
|
}}
|
||||||
|
/> : null
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
intl,
|
intl,
|
||||||
@ -290,8 +315,12 @@ class ActionsDropdown extends PureComponent {
|
|||||||
isDropdownOpen,
|
isDropdownOpen,
|
||||||
isMobile,
|
isMobile,
|
||||||
isRTL,
|
isRTL,
|
||||||
|
isSelectRandomUserEnabled,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { isExternalVideoModalOpen,
|
||||||
|
isRandomUserSelectModalOpen, isLayoutModalOpen } = this.state;
|
||||||
|
|
||||||
const availableActions = this.getAvailableActions();
|
const availableActions = this.getAvailableActions();
|
||||||
const availablePresentations = this.makePresentationItems();
|
const availablePresentations = this.makePresentationItems();
|
||||||
const children = availablePresentations.length > 1 && amIPresenter
|
const children = availablePresentations.length > 1 && amIPresenter
|
||||||
@ -305,35 +334,43 @@ class ActionsDropdown extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BBBMenu
|
<>
|
||||||
customStyles={!isMobile ? customStyles : null}
|
<BBBMenu
|
||||||
accessKey={OPEN_ACTIONS_AK}
|
customStyles={!isMobile ? customStyles : null}
|
||||||
trigger={
|
accessKey={OPEN_ACTIONS_AK}
|
||||||
<Styled.HideDropdownButton
|
trigger={
|
||||||
open={isDropdownOpen}
|
<Styled.HideDropdownButton
|
||||||
hideLabel
|
open={isDropdownOpen}
|
||||||
aria-label={intl.formatMessage(intlMessages.actionsLabel)}
|
hideLabel
|
||||||
data-test="actionsButton"
|
aria-label={intl.formatMessage(intlMessages.actionsLabel)}
|
||||||
label={intl.formatMessage(intlMessages.actionsLabel)}
|
data-test="actionsButton"
|
||||||
icon="plus"
|
label={intl.formatMessage(intlMessages.actionsLabel)}
|
||||||
color="primary"
|
icon="plus"
|
||||||
size="lg"
|
color="primary"
|
||||||
circle
|
size="lg"
|
||||||
onClick={() => null}
|
circle
|
||||||
/>
|
onClick={() => null}
|
||||||
}
|
/>
|
||||||
actions={children}
|
}
|
||||||
opts={{
|
actions={children}
|
||||||
id: "actions-dropdown-menu",
|
opts={{
|
||||||
keepMounted: true,
|
id: "actions-dropdown-menu",
|
||||||
transitionDuration: 0,
|
keepMounted: true,
|
||||||
elevation: 3,
|
transitionDuration: 0,
|
||||||
getContentAnchorEl: null,
|
elevation: 3,
|
||||||
fullwidth: "true",
|
getContentAnchorEl: null,
|
||||||
anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'right' : 'left' },
|
fullwidth: "true",
|
||||||
transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
|
anchorOrigin: { vertical: 'top', horizontal: isRTL ? 'right' : 'left' },
|
||||||
}}
|
transformOrigin: { vertical: 'bottom', horizontal: isRTL ? 'right' : 'left' },
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
{this.renderModal(isExternalVideoModalOpen, this.setExternalVideoModalIsOpen, "low",
|
||||||
|
ExternalVideoModal)}
|
||||||
|
{(amIPresenter && isSelectRandomUserEnabled) ? this.renderModal(isRandomUserSelectModalOpen, this.setRandomUserSelectModalIsOpen,
|
||||||
|
"low", RandomUserSelectContainer) : null }
|
||||||
|
{this.renderModal(isLayoutModalOpen, this.setLayoutModalIsOpen,
|
||||||
|
"low", LayoutModalContainer)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,4 +378,4 @@ class ActionsDropdown extends PureComponent {
|
|||||||
ActionsDropdown.propTypes = propTypes;
|
ActionsDropdown.propTypes = propTypes;
|
||||||
ActionsDropdown.defaultProps = defaultProps;
|
ActionsDropdown.defaultProps = defaultProps;
|
||||||
|
|
||||||
export default withShortcutHelper(withModalMounter(ActionsDropdown), 'openActions');
|
export default withShortcutHelper(ActionsDropdown, 'openActions');
|
||||||
|
@ -4,6 +4,7 @@ import deviceInfo from '/imports/utils/deviceInfo';
|
|||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import ActionsDropdown from './actions-dropdown/container';
|
import ActionsDropdown from './actions-dropdown/container';
|
||||||
import AudioCaptionsButtonContainer from '/imports/ui/components/audio/captions/button/container';
|
import AudioCaptionsButtonContainer from '/imports/ui/components/audio/captions/button/container';
|
||||||
|
import CaptionsReaderMenuContainer from '/imports/ui/components/captions/reader-menu/container';
|
||||||
import ScreenshareButtonContainer from '/imports/ui/components/actions-bar/screenshare/container';
|
import ScreenshareButtonContainer from '/imports/ui/components/actions-bar/screenshare/container';
|
||||||
import AudioControlsContainer from '../audio/audio-controls/container';
|
import AudioControlsContainer from '../audio/audio-controls/container';
|
||||||
import JoinVideoOptionsContainer from '../video-provider/video-button/container';
|
import JoinVideoOptionsContainer from '../video-provider/video-button/container';
|
||||||
@ -12,6 +13,20 @@ import RaiseHandDropdownContainer from './raise-hand/container';
|
|||||||
import { isPresentationEnabled } from '/imports/ui/services/features';
|
import { isPresentationEnabled } from '/imports/ui/services/features';
|
||||||
|
|
||||||
class ActionsBar extends PureComponent {
|
class ActionsBar extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isCaptionsReaderMenuModalOpen: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setCaptionsReaderMenuModalIsOpen = this.setCaptionsReaderMenuModalIsOpen.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCaptionsReaderMenuModalIsOpen(value) {
|
||||||
|
this.setState({ isCaptionsReaderMenuModalOpen: value })
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
amIPresenter,
|
amIPresenter,
|
||||||
@ -41,8 +56,10 @@ class ActionsBar extends PureComponent {
|
|||||||
setPushLayout,
|
setPushLayout,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const shouldShowOptionsButton = (isPresentationEnabled() && isThereCurrentPresentation)
|
const { isCaptionsReaderMenuModalOpen } = this.state;
|
||||||
|| isSharingVideo || hasScreenshare || isSharedNotesPinned;
|
|
||||||
|
const shouldShowOptionsButton = (isPresentationEnabled() && isThereCurrentPresentation)
|
||||||
|
|| isSharingVideo || hasScreenshare || isSharedNotesPinned;
|
||||||
return (
|
return (
|
||||||
<Styled.ActionsBar
|
<Styled.ActionsBar
|
||||||
style={
|
style={
|
||||||
@ -71,7 +88,20 @@ class ActionsBar extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
{isCaptionsAvailable
|
{isCaptionsAvailable
|
||||||
? (
|
? (
|
||||||
<CaptionsButtonContainer {...{ intl }} />
|
<>
|
||||||
|
<CaptionsButtonContainer {...{ intl,
|
||||||
|
setIsOpen: this.setCaptionsReaderMenuModalIsOpen,}} />
|
||||||
|
{
|
||||||
|
isCaptionsReaderMenuModalOpen ? <CaptionsReaderMenuContainer
|
||||||
|
{...{
|
||||||
|
onRequestClose: () => this.setCaptionsReaderMenuModalIsOpen(false),
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: this.setCaptionsReaderMenuModalIsOpen,
|
||||||
|
isOpen: isCaptionsReaderMenuModalOpen,
|
||||||
|
}}
|
||||||
|
/> : null
|
||||||
|
}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
{ !deviceInfo.isMobile
|
{ !deviceInfo.isMobile
|
||||||
|
@ -5,8 +5,7 @@ import { range } from '/imports/utils/array-utils';
|
|||||||
import deviceInfo from '/imports/utils/deviceInfo';
|
import deviceInfo from '/imports/utils/deviceInfo';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import { Session } from 'meteor/session';
|
import { Session } from 'meteor/session';
|
||||||
import Modal from '/imports/ui/components/common/modal/fullscreen/component';
|
import ModalFullscreen from '/imports/ui/components/common/modal/fullscreen/component';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import SortList from './sort-user-list/component';
|
import SortList from './sort-user-list/component';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import Icon from '/imports/ui/components/common/icon/component';
|
import Icon from '/imports/ui/components/common/icon/component';
|
||||||
@ -192,7 +191,6 @@ const propTypes = {
|
|||||||
getUsersNotJoined: PropTypes.func.isRequired,
|
getUsersNotJoined: PropTypes.func.isRequired,
|
||||||
getBreakouts: PropTypes.func.isRequired,
|
getBreakouts: PropTypes.func.isRequired,
|
||||||
sendInvitation: PropTypes.func.isRequired,
|
sendInvitation: PropTypes.func.isRequired,
|
||||||
mountModal: PropTypes.func.isRequired,
|
|
||||||
isBreakoutRecordable: PropTypes.bool,
|
isBreakoutRecordable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -411,10 +409,10 @@ class BreakoutRoom extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDismiss() {
|
handleDismiss() {
|
||||||
const { mountModal } = this.props;
|
const { setIsOpen } = this.props;
|
||||||
setPresentationVisibility('block');
|
setPresentationVisibility('block');
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
mountModal(null);
|
setIsOpen(false);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
preventClosing: false,
|
preventClosing: false,
|
||||||
@ -1317,7 +1315,7 @@ class BreakoutRoom extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, isUpdate } = this.props;
|
const { intl, isUpdate, isOpen, priority, setIsOpen, } = this.props;
|
||||||
const {
|
const {
|
||||||
preventClosing,
|
preventClosing,
|
||||||
leastOneUserIsValid,
|
leastOneUserIsValid,
|
||||||
@ -1330,7 +1328,7 @@ class BreakoutRoom extends PureComponent {
|
|||||||
const { isMobile } = deviceInfo;
|
const { isMobile } = deviceInfo;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalFullscreen
|
||||||
title={
|
title={
|
||||||
isUpdate
|
isUpdate
|
||||||
? intl.formatMessage(intlMessages.updateTitle)
|
? intl.formatMessage(intlMessages.updateTitle)
|
||||||
@ -1357,16 +1355,21 @@ class BreakoutRoom extends PureComponent {
|
|||||||
: intl.formatMessage(intlMessages.dismissLabel),
|
: intl.formatMessage(intlMessages.dismissLabel),
|
||||||
}}
|
}}
|
||||||
preventClosing={preventClosing}
|
preventClosing={preventClosing}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
setIsOpen,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
{this.renderTitle()}
|
{this.renderTitle()}
|
||||||
{isMobile ? this.renderMobile() : this.renderDesktop()}
|
{isMobile ? this.renderMobile() : this.renderDesktop()}
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
</Modal>
|
</ModalFullscreen>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BreakoutRoom.propTypes = propTypes;
|
BreakoutRoom.propTypes = propTypes;
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(BreakoutRoom));
|
export default injectIntl(BreakoutRoom);
|
||||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
intl: PropTypes.shape({
|
intl: PropTypes.shape({
|
||||||
formatMessage: PropTypes.func.isRequired,
|
formatMessage: PropTypes.func.isRequired,
|
||||||
@ -47,7 +48,8 @@ const PresentationOptionsContainer = ({
|
|||||||
buttonType = 'desktop';
|
buttonType = 'desktop';
|
||||||
}
|
}
|
||||||
|
|
||||||
const isThereCurrentPresentation = hasExternalVideo || hasScreenshare || hasPresentation || hasPinnedSharedNotes;
|
const isThereCurrentPresentation = hasExternalVideo || hasScreenshare
|
||||||
|
|| hasPresentation || hasPinnedSharedNotes;
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
icon={`${buttonType}${!presentationIsOpen ? '_off' : ''}`}
|
icon={`${buttonType}${!presentationIsOpen ? '_off' : ''}`}
|
||||||
@ -59,7 +61,12 @@ const PresentationOptionsContainer = ({
|
|||||||
hideLabel
|
hideLabel
|
||||||
circle
|
circle
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={() => setPresentationIsOpen(layoutContextDispatch, !presentationIsOpen)}
|
onClick={() => {
|
||||||
|
setPresentationIsOpen(layoutContextDispatch, !presentationIsOpen);
|
||||||
|
if (!hasExternalVideo && !hasScreenshare && !hasPinnedSharedNotes) {
|
||||||
|
Session.set('presentationLastState', !presentationIsOpen);
|
||||||
|
}
|
||||||
|
}}
|
||||||
id="restore-presentation"
|
id="restore-presentation"
|
||||||
ghost={!presentationIsOpen}
|
ghost={!presentationIsOpen}
|
||||||
disabled={!isThereCurrentPresentation}
|
disabled={!isThereCurrentPresentation}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import React, { memo } from 'react';
|
import React, { memo, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import deviceInfo from '/imports/utils/deviceInfo';
|
import deviceInfo from '/imports/utils/deviceInfo';
|
||||||
import browserInfo from '/imports/utils/browserInfo';
|
import browserInfo from '/imports/utils/browserInfo';
|
||||||
import logger from '/imports/startup/client/logger';
|
import logger from '/imports/startup/client/logger';
|
||||||
import { notify } from '/imports/ui/services/notification';
|
import { notify } from '/imports/ui/services/notification';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import ScreenshareBridgeService from '/imports/api/screenshare/client/bridge/service';
|
import ScreenshareBridgeService from '/imports/api/screenshare/client/bridge/service';
|
||||||
import {
|
import {
|
||||||
@ -14,6 +13,7 @@ import {
|
|||||||
} from '/imports/ui/components/screenshare/service';
|
} from '/imports/ui/components/screenshare/service';
|
||||||
import { SCREENSHARING_ERRORS } from '/imports/api/screenshare/client/bridge/errors';
|
import { SCREENSHARING_ERRORS } from '/imports/api/screenshare/client/bridge/errors';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
|
import { parsePayloads } from 'sdp-transform';
|
||||||
|
|
||||||
const { isMobile } = deviceInfo;
|
const { isMobile } = deviceInfo;
|
||||||
const { isSafari, isTabletApp } = browserInfo;
|
const { isSafari, isTabletApp } = browserInfo;
|
||||||
@ -117,8 +117,6 @@ const ScreenshareButton = ({
|
|||||||
isVideoBroadcasting,
|
isVideoBroadcasting,
|
||||||
amIPresenter,
|
amIPresenter,
|
||||||
isMeteorConnected,
|
isMeteorConnected,
|
||||||
screenshareDataSavingSetting,
|
|
||||||
mountModal,
|
|
||||||
}) => {
|
}) => {
|
||||||
// This is the failure callback that will be passed to the /api/screenshare/kurento.js
|
// This is the failure callback that will be passed to the /api/screenshare/kurento.js
|
||||||
// script on the presenter's call
|
// script on the presenter's call
|
||||||
@ -141,18 +139,19 @@ const ScreenshareButton = ({
|
|||||||
screenshareHasEnded();
|
screenshareHasEnded();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderScreenshareUnavailableModal = () => mountModal(
|
const [isScreenshareUnavailableModalOpen, setScreenshareUnavailableModalIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const RenderScreenshareUnavailableModal = (otherProps) =>
|
||||||
<Styled.ScreenShareModal
|
<Styled.ScreenShareModal
|
||||||
onRequestClose={() => mountModal(null)}
|
|
||||||
hideBorder
|
hideBorder
|
||||||
contentLabel={intl.formatMessage(intlMessages.screenShareUnavailable)}
|
contentLabel={intl.formatMessage(intlMessages.screenShareUnavailable)}
|
||||||
|
{...otherProps}
|
||||||
>
|
>
|
||||||
<Styled.Title>
|
<Styled.Title>
|
||||||
{intl.formatMessage(intlMessages.screenShareUnavailable)}
|
{intl.formatMessage(intlMessages.screenShareUnavailable)}
|
||||||
</Styled.Title>
|
</Styled.Title>
|
||||||
<p>{intl.formatMessage(intlMessages.screenShareNotSupported)}</p>
|
<p>{intl.formatMessage(intlMessages.screenShareNotSupported)}</p>
|
||||||
</Styled.ScreenShareModal>,
|
</Styled.ScreenShareModal>;
|
||||||
);
|
|
||||||
|
|
||||||
const screenshareLabel = intlMessages.desktopShareLabel;
|
const screenshareLabel = intlMessages.desktopShareLabel;
|
||||||
|
|
||||||
@ -168,32 +167,46 @@ const ScreenshareButton = ({
|
|||||||
|
|
||||||
const dataTest = isVideoBroadcasting ? 'stopScreenShare' : 'startScreenShare';
|
const dataTest = isVideoBroadcasting ? 'stopScreenShare' : 'startScreenShare';
|
||||||
|
|
||||||
return shouldAllowScreensharing
|
return <>
|
||||||
? (
|
{
|
||||||
<Button
|
shouldAllowScreensharing
|
||||||
disabled={(!isMeteorConnected && !isVideoBroadcasting)}
|
? (
|
||||||
icon={isVideoBroadcasting ? 'desktop' : 'desktop_off'}
|
<Button
|
||||||
data-test={dataTest}
|
disabled={(!isMeteorConnected && !isVideoBroadcasting)}
|
||||||
label={intl.formatMessage(vLabel)}
|
icon={isVideoBroadcasting ? 'desktop' : 'desktop_off'}
|
||||||
description={intl.formatMessage(vDescr)}
|
data-test={dataTest}
|
||||||
color={isVideoBroadcasting ? 'primary' : 'default'}
|
label={intl.formatMessage(vLabel)}
|
||||||
ghost={!isVideoBroadcasting}
|
description={intl.formatMessage(vDescr)}
|
||||||
hideLabel
|
color={isVideoBroadcasting ? 'primary' : 'default'}
|
||||||
circle
|
ghost={!isVideoBroadcasting}
|
||||||
size="lg"
|
hideLabel
|
||||||
onClick={isVideoBroadcasting
|
circle
|
||||||
? screenshareHasEnded
|
size="lg"
|
||||||
: () => {
|
onClick={isVideoBroadcasting
|
||||||
if (isSafari && !ScreenshareBridgeService.HAS_DISPLAY_MEDIA) {
|
? screenshareHasEnded
|
||||||
renderScreenshareUnavailableModal();
|
: () => {
|
||||||
} else {
|
if (isSafari && !ScreenshareBridgeService.HAS_DISPLAY_MEDIA) {
|
||||||
shareScreen(amIPresenter, handleFailure);
|
setScreenshareUnavailableModalIsOpen(true);
|
||||||
}
|
} else {
|
||||||
}}
|
shareScreen(amIPresenter, handleFailure);
|
||||||
id={isVideoBroadcasting ? 'unshare-screen-button' : 'share-screen-button'}
|
}
|
||||||
/>
|
}}
|
||||||
) : null;
|
id={isVideoBroadcasting ? 'unshare-screen-button' : 'share-screen-button'}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isScreenshareUnavailableModalOpen ? <RenderScreenshareUnavailableModal
|
||||||
|
{...{
|
||||||
|
onRequestClose: () => setScreenshareUnavailableModalIsOpen(false),
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: setScreenshareUnavailableModalIsOpen,
|
||||||
|
isOpen: isScreenshareUnavailableModalOpen,
|
||||||
|
}}
|
||||||
|
/> : null
|
||||||
|
}
|
||||||
|
</>
|
||||||
};
|
};
|
||||||
|
|
||||||
ScreenshareButton.propTypes = propTypes;
|
ScreenshareButton.propTypes = propTypes;
|
||||||
export default withModalMounter(injectIntl(memo(ScreenshareButton)));
|
export default injectIntl(memo(ScreenshareButton));
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import ScreenshareButton from './component';
|
import ScreenshareButton from './component';
|
||||||
import { isScreenSharingEnabled } from '/imports/ui/services/features';
|
import { isScreenSharingEnabled } from '/imports/ui/services/features';
|
||||||
import {
|
import {
|
||||||
@ -18,8 +17,8 @@ const ScreenshareButtonContainer = (props) => <ScreenshareButton {...props} />;
|
|||||||
* isMeteorConnected,
|
* isMeteorConnected,
|
||||||
* screenshareDataSavingSetting,
|
* screenshareDataSavingSetting,
|
||||||
*/
|
*/
|
||||||
export default withModalMounter(withTracker(() => ({
|
export default withTracker(() => ({
|
||||||
isVideoBroadcasting: isVideoBroadcasting(),
|
isVideoBroadcasting: isVideoBroadcasting(),
|
||||||
screenshareDataSavingSetting: dataSavingSetting(),
|
screenshareDataSavingSetting: dataSavingSetting(),
|
||||||
enabled: isScreenSharingEnabled(),
|
enabled: isScreenSharingEnabled(),
|
||||||
}))(ScreenshareButtonContainer));
|
}))(ScreenshareButtonContainer);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import { colorGrayDark } from '/imports/ui/stylesheets/styled-components/palette';
|
import { colorGrayDark } from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import {
|
import {
|
||||||
jumboPaddingY,
|
jumboPaddingY,
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} from '/imports/ui/stylesheets/styled-components/general';
|
} from '/imports/ui/stylesheets/styled-components/general';
|
||||||
import { fontSizeLarge } from '/imports/ui/stylesheets/styled-components/typography';
|
import { fontSizeLarge } from '/imports/ui/stylesheets/styled-components/typography';
|
||||||
|
|
||||||
const ScreenShareModal = styled(Modal)`
|
const ScreenShareModal = styled(ModalSimple)`
|
||||||
padding: ${jumboPaddingY};
|
padding: ${jumboPaddingY};
|
||||||
min-height: ${minModalHeight};
|
min-height: ${minModalHeight};
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import { makeCall } from '/imports/ui/services/api';
|
import { makeCall } from '/imports/ui/services/api';
|
||||||
|
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
@ -92,7 +92,7 @@ class ActivityCheck extends Component {
|
|||||||
const { responseDelay } = this.state;
|
const { responseDelay } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalSimple
|
||||||
hideBorder
|
hideBorder
|
||||||
onRequestClose={handleInactivityDismiss}
|
onRequestClose={handleInactivityDismiss}
|
||||||
shouldCloseOnOverlayClick={false}
|
shouldCloseOnOverlayClick={false}
|
||||||
@ -110,7 +110,7 @@ class ActivityCheck extends Component {
|
|||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</Styled.ActivityModalContent>
|
</Styled.ActivityModalContent>
|
||||||
</Modal>
|
</ModalSimple>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { throttle } from '/imports/utils/throttle';
|
import { throttle } from '/imports/utils/throttle';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import Modal from 'react-modal';
|
import ReactModal from 'react-modal';
|
||||||
import browserInfo from '/imports/utils/browserInfo';
|
import browserInfo from '/imports/utils/browserInfo';
|
||||||
import deviceInfo from '/imports/utils/deviceInfo';
|
import deviceInfo from '/imports/utils/deviceInfo';
|
||||||
import PollingContainer from '/imports/ui/components/polling/container';
|
import PollingContainer from '/imports/ui/components/polling/container';
|
||||||
@ -13,7 +13,6 @@ import BreakoutRoomInvitation from '/imports/ui/components/breakout-room/invitat
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import ToastContainer from '/imports/ui/components/common/toast/container';
|
import ToastContainer from '/imports/ui/components/common/toast/container';
|
||||||
import PadsSessionsContainer from '/imports/ui/components/pads/sessions/container';
|
import PadsSessionsContainer from '/imports/ui/components/pads/sessions/container';
|
||||||
import ModalContainer from '/imports/ui/components/common/modal/container';
|
|
||||||
import NotificationsBarContainer from '../notifications-bar/container';
|
import NotificationsBarContainer from '../notifications-bar/container';
|
||||||
import AudioContainer from '../audio/container';
|
import AudioContainer from '../audio/container';
|
||||||
import ChatAlertContainer from '../chat/alert/container';
|
import ChatAlertContainer from '../chat/alert/container';
|
||||||
@ -135,10 +134,16 @@ class App extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
enableResize: !window.matchMedia(MOBILE_MEDIA).matches,
|
enableResize: !window.matchMedia(MOBILE_MEDIA).matches,
|
||||||
|
isAudioModalOpen: false,
|
||||||
|
isRandomUserSelectModalOpen: false,
|
||||||
|
isVideoPreviewModalOpen: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleWindowResize = throttle(this.handleWindowResize).bind(this);
|
this.handleWindowResize = throttle(this.handleWindowResize).bind(this);
|
||||||
this.shouldAriaHide = this.shouldAriaHide.bind(this);
|
this.shouldAriaHide = this.shouldAriaHide.bind(this);
|
||||||
|
this.setAudioModalIsOpen = this.setAudioModalIsOpen.bind(this);
|
||||||
|
this.setRandomUserSelectModalIsOpen = this.setRandomUserSelectModalIsOpen.bind(this);
|
||||||
|
this.setVideoPreviewModalIsOpen = this.setVideoPreviewModalIsOpen.bind(this);
|
||||||
|
|
||||||
this.throttledDeviceType = throttle(() => this.setDeviceType(),
|
this.throttledDeviceType = throttle(() => this.setDeviceType(),
|
||||||
50, { trailing: true, leading: true }).bind(this);
|
50, { trailing: true, leading: true }).bind(this);
|
||||||
@ -162,7 +167,7 @@ class App extends Component {
|
|||||||
value: isRTL,
|
value: isRTL,
|
||||||
});
|
});
|
||||||
|
|
||||||
Modal.setAppElement('#app');
|
ReactModal.setAppElement('#app');
|
||||||
|
|
||||||
const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE;
|
const fontSize = isMobile() ? MOBILE_FONT_SIZE : DESKTOP_FONT_SIZE;
|
||||||
document.getElementsByTagName('html')[0].style.fontSize = fontSize;
|
document.getElementsByTagName('html')[0].style.fontSize = fontSize;
|
||||||
@ -220,7 +225,6 @@ class App extends Component {
|
|||||||
notify,
|
notify,
|
||||||
currentUserEmoji,
|
currentUserEmoji,
|
||||||
intl,
|
intl,
|
||||||
mountModal,
|
|
||||||
deviceType,
|
deviceType,
|
||||||
mountRandomUserModal,
|
mountRandomUserModal,
|
||||||
selectedLayout,
|
selectedLayout,
|
||||||
@ -228,12 +232,11 @@ class App extends Component {
|
|||||||
layoutContextDispatch,
|
layoutContextDispatch,
|
||||||
numCameras,
|
numCameras,
|
||||||
presentationIsOpen,
|
presentationIsOpen,
|
||||||
ignorePollNotifications,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.renderDarkMode();
|
this.renderDarkMode();
|
||||||
|
|
||||||
if (mountRandomUserModal) mountModal(<RandomUserSelectContainer />);
|
if (mountRandomUserModal) this.setRandomUserSelectModalIsOpen(true);
|
||||||
|
|
||||||
if (prevProps.currentUserEmoji.status !== currentUserEmoji.status) {
|
if (prevProps.currentUserEmoji.status !== currentUserEmoji.status) {
|
||||||
const formattedEmojiStatus = intl.formatMessage({ id: `app.actionsBar.emojiMenu.${currentUserEmoji.status}Label` })
|
const formattedEmojiStatus = intl.formatMessage({ id: `app.actionsBar.emojiMenu.${currentUserEmoji.status}Label` })
|
||||||
@ -506,12 +509,26 @@ class App extends Component {
|
|||||||
setMeetingLayout,
|
setMeetingLayout,
|
||||||
setPushLayout,
|
setPushLayout,
|
||||||
shouldShowScreenshare,
|
shouldShowScreenshare,
|
||||||
shouldShowExternalVideo,
|
shouldShowExternalVideo: !!shouldShowExternalVideo,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAudioModalIsOpen(value) {
|
||||||
|
this.setState({isAudioModalOpen: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
setVideoPreviewModalIsOpen(value) {
|
||||||
|
this.setState({isVideoPreviewModalOpen: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
setRandomUserSelectModalIsOpen(value) {
|
||||||
|
const {setMountRandomUserModal} = this.props;
|
||||||
|
this.setState({isRandomUserSelectModalOpen: value});
|
||||||
|
setMountRandomUserModal(false);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
customStyle,
|
customStyle,
|
||||||
@ -528,6 +545,7 @@ class App extends Component {
|
|||||||
darkTheme,
|
darkTheme,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { isAudioModalOpen, isRandomUserSelectModalOpen, isVideoPreviewModalOpen } = this.state;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
@ -571,7 +589,12 @@ class App extends Component {
|
|||||||
<UploaderContainer />
|
<UploaderContainer />
|
||||||
<CaptionsSpeechContainer />
|
<CaptionsSpeechContainer />
|
||||||
<BreakoutRoomInvitation />
|
<BreakoutRoomInvitation />
|
||||||
<AudioContainer />
|
<AudioContainer {...{
|
||||||
|
isAudioModalOpen,
|
||||||
|
setAudioModalIsOpen: this.setAudioModalIsOpen,
|
||||||
|
isVideoPreviewModalOpen,
|
||||||
|
setVideoPreviewModalIsOpen: this.setVideoPreviewModalIsOpen,
|
||||||
|
}} />
|
||||||
<ToastContainer rtl />
|
<ToastContainer rtl />
|
||||||
{(audioAlertEnabled || pushAlertEnabled)
|
{(audioAlertEnabled || pushAlertEnabled)
|
||||||
&& (
|
&& (
|
||||||
@ -583,11 +606,18 @@ class App extends Component {
|
|||||||
<StatusNotifier status="raiseHand" />
|
<StatusNotifier status="raiseHand" />
|
||||||
<ManyWebcamsNotifier />
|
<ManyWebcamsNotifier />
|
||||||
<PollingContainer />
|
<PollingContainer />
|
||||||
<ModalContainer />
|
|
||||||
<PadsSessionsContainer />
|
<PadsSessionsContainer />
|
||||||
{this.renderActionsBar()}
|
{this.renderActionsBar()}
|
||||||
{customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null}
|
{customStyleUrl ? <link rel="stylesheet" type="text/css" href={customStyleUrl} /> : null}
|
||||||
{customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null}
|
{customStyle ? <link rel="stylesheet" type="text/css" href={`data:text/css;charset=UTF-8,${encodeURIComponent(customStyle)}`} /> : null}
|
||||||
|
{isRandomUserSelectModalOpen ? <RandomUserSelectContainer
|
||||||
|
{...{
|
||||||
|
onRequestClose: () => this.setRandomUserSelectModalIsOpen(false),
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: this.setRandomUserSelectModalIsOpen,
|
||||||
|
isOpen: isRandomUserSelectModalOpen,
|
||||||
|
}}
|
||||||
|
/> : null}
|
||||||
</Styled.Layout>
|
</Styled.Layout>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import Auth from '/imports/ui/services/auth';
|
import Auth from '/imports/ui/services/auth';
|
||||||
import Users from '/imports/api/users';
|
import Users from '/imports/api/users';
|
||||||
@ -31,8 +31,6 @@ import {
|
|||||||
validIOSVersion,
|
validIOSVersion,
|
||||||
} from './service';
|
} from './service';
|
||||||
|
|
||||||
import { withModalMounter, getModal } from '/imports/ui/components/common/modal/service';
|
|
||||||
|
|
||||||
import App from './component';
|
import App from './component';
|
||||||
|
|
||||||
const CUSTOM_STYLE_URL = Meteor.settings.public.app.customStyleUrl;
|
const CUSTOM_STYLE_URL = Meteor.settings.public.app.customStyleUrl;
|
||||||
@ -116,10 +114,14 @@ const AppContainer = (props) => {
|
|||||||
|
|
||||||
const prevRandomUser = usePrevious(randomlySelectedUser);
|
const prevRandomUser = usePrevious(randomlySelectedUser);
|
||||||
|
|
||||||
const mountRandomUserModal = !isPresenter
|
const [mountRandomUserModal, setMountRandomUserModal] = useState(false);
|
||||||
&& !isEqual(prevRandomUser, randomlySelectedUser)
|
|
||||||
&& randomlySelectedUser.length > 0
|
useEffect(() => {
|
||||||
&& !isModalOpen;
|
setMountRandomUserModal(!isPresenter
|
||||||
|
&& !isEqual(prevRandomUser, randomlySelectedUser)
|
||||||
|
&& randomlySelectedUser.length > 0
|
||||||
|
&& !isModalOpen);
|
||||||
|
}, [isPresenter, prevRandomUser, randomlySelectedUser, isModalOpen]);
|
||||||
|
|
||||||
const setPushLayout = () => {
|
const setPushLayout = () => {
|
||||||
LayoutService.setPushLayout(pushLayout);
|
LayoutService.setPushLayout(pushLayout);
|
||||||
@ -176,6 +178,7 @@ const AppContainer = (props) => {
|
|||||||
sidebarContentIsOpen,
|
sidebarContentIsOpen,
|
||||||
shouldShowPresentation,
|
shouldShowPresentation,
|
||||||
mountRandomUserModal,
|
mountRandomUserModal,
|
||||||
|
setMountRandomUserModal,
|
||||||
isPresenter,
|
isPresenter,
|
||||||
numCameras: cameraDockInput.numCameras,
|
numCameras: cameraDockInput.numCameras,
|
||||||
}}
|
}}
|
||||||
@ -196,7 +199,7 @@ const currentUserEmoji = (currentUser) => (currentUser
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default withModalMounter(withTracker(() => {
|
export default withTracker(() => {
|
||||||
Users.find({ userId: Auth.userID, meetingId: Auth.meetingID }).observe({
|
Users.find({ userId: Auth.userID, meetingId: Auth.meetingID }).observe({
|
||||||
removed(userData) {
|
removed(userData) {
|
||||||
// wait 3secs (before endMeeting), client will try to authenticate again
|
// wait 3secs (before endMeeting), client will try to authenticate again
|
||||||
@ -315,7 +318,6 @@ export default withModalMounter(withTracker(() => {
|
|||||||
),
|
),
|
||||||
hidePresentationOnJoin: getFromUserSettings('bbb_hide_presentation_on_join', LAYOUT_CONFIG.hidePresentationOnJoin),
|
hidePresentationOnJoin: getFromUserSettings('bbb_hide_presentation_on_join', LAYOUT_CONFIG.hidePresentationOnJoin),
|
||||||
hideActionsBar: getFromUserSettings('bbb_hide_actions_bar', false),
|
hideActionsBar: getFromUserSettings('bbb_hide_actions_bar', false),
|
||||||
isModalOpen: !!getModal(),
|
|
||||||
ignorePollNotifications: Session.get('ignorePollNotifications'),
|
ignorePollNotifications: Session.get('ignorePollNotifications'),
|
||||||
};
|
};
|
||||||
})(AppContainer));
|
})(AppContainer);
|
||||||
|
@ -7,6 +7,7 @@ import InputStreamLiveSelectorContainer from './input-stream-live-selector/conta
|
|||||||
import MutedAlert from '/imports/ui/components/muted-alert/component';
|
import MutedAlert from '/imports/ui/components/muted-alert/component';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
|
import AudioModalContainer from '../audio-modal/container';
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
joinAudio: {
|
joinAudio: {
|
||||||
@ -30,7 +31,6 @@ const intlMessages = defineMessages({
|
|||||||
const propTypes = {
|
const propTypes = {
|
||||||
shortcuts: PropTypes.objectOf(PropTypes.string).isRequired,
|
shortcuts: PropTypes.objectOf(PropTypes.string).isRequired,
|
||||||
handleToggleMuteMicrophone: PropTypes.func.isRequired,
|
handleToggleMuteMicrophone: PropTypes.func.isRequired,
|
||||||
handleJoinAudio: PropTypes.func.isRequired,
|
|
||||||
handleLeaveAudio: PropTypes.func.isRequired,
|
handleLeaveAudio: PropTypes.func.isRequired,
|
||||||
disable: PropTypes.bool.isRequired,
|
disable: PropTypes.bool.isRequired,
|
||||||
muted: PropTypes.bool.isRequired,
|
muted: PropTypes.bool.isRequired,
|
||||||
@ -46,21 +46,28 @@ const propTypes = {
|
|||||||
class AudioControls extends PureComponent {
|
class AudioControls extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isAudioModalOpen: false,
|
||||||
|
};
|
||||||
|
|
||||||
this.renderButtonsAndStreamSelector = this.renderButtonsAndStreamSelector.bind(this);
|
this.renderButtonsAndStreamSelector = this.renderButtonsAndStreamSelector.bind(this);
|
||||||
this.renderJoinLeaveButton = this.renderJoinLeaveButton.bind(this);
|
this.renderJoinLeaveButton = this.renderJoinLeaveButton.bind(this);
|
||||||
|
this.setAudioModalIsOpen = this.setAudioModalIsOpen.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJoinButton() {
|
renderJoinButton() {
|
||||||
const {
|
const {
|
||||||
handleJoinAudio,
|
|
||||||
disable,
|
disable,
|
||||||
intl,
|
intl,
|
||||||
shortcuts,
|
shortcuts,
|
||||||
|
joinListenOnly,
|
||||||
|
isConnected
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleJoinAudio}
|
onClick={() => this.handleJoinAudio(joinListenOnly, isConnected)}
|
||||||
disabled={disable}
|
disabled={disable}
|
||||||
hideLabel
|
hideLabel
|
||||||
aria-label={intl.formatMessage(intlMessages.joinAudio)}
|
aria-label={intl.formatMessage(intlMessages.joinAudio)}
|
||||||
@ -119,6 +126,16 @@ class AudioControls extends PureComponent {
|
|||||||
return this.renderJoinButton();
|
return this.renderJoinButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleJoinAudio(joinListenOnly, isConnected) {
|
||||||
|
(isConnected()
|
||||||
|
? joinListenOnly()
|
||||||
|
: this.setAudioModalIsOpen(true)
|
||||||
|
)}
|
||||||
|
|
||||||
|
setAudioModalIsOpen(value) {
|
||||||
|
this.setState({ isAudioModalOpen: value })
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
showMute,
|
showMute,
|
||||||
@ -130,6 +147,8 @@ class AudioControls extends PureComponent {
|
|||||||
isPresenter,
|
isPresenter,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { isAudioModalOpen } = this.state;
|
||||||
|
|
||||||
const MUTE_ALERT_CONFIG = Meteor.settings.public.app.mutedAlert;
|
const MUTE_ALERT_CONFIG = Meteor.settings.public.app.mutedAlert;
|
||||||
const { enabled: muteAlertEnabled } = MUTE_ALERT_CONFIG;
|
const { enabled: muteAlertEnabled } = MUTE_ALERT_CONFIG;
|
||||||
|
|
||||||
@ -144,6 +163,15 @@ class AudioControls extends PureComponent {
|
|||||||
{
|
{
|
||||||
this.renderJoinLeaveButton()
|
this.renderJoinLeaveButton()
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
isAudioModalOpen ? <AudioModalContainer
|
||||||
|
{...{
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: this.setAudioModalIsOpen,
|
||||||
|
isOpen: isAudioModalOpen
|
||||||
|
}}
|
||||||
|
/> : null
|
||||||
|
}
|
||||||
</Styled.Container>
|
</Styled.Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import AudioManager from '/imports/ui/services/audio-manager';
|
import AudioManager from '/imports/ui/services/audio-manager';
|
||||||
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
|
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
|
||||||
import { withUsersConsumer } from '/imports/ui/components/components-data/users-context/context';
|
import { withUsersConsumer } from '/imports/ui/components/components-data/users-context/context';
|
||||||
@ -9,7 +8,6 @@ import Auth from '/imports/ui/services/auth';
|
|||||||
import Storage from '/imports/ui/services/storage/session';
|
import Storage from '/imports/ui/services/storage/session';
|
||||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||||
import AudioControls from './component';
|
import AudioControls from './component';
|
||||||
import AudioModalContainer from '../audio-modal/container';
|
|
||||||
import {
|
import {
|
||||||
setUserSelectedMicrophone,
|
setUserSelectedMicrophone,
|
||||||
setUserSelectedListenOnly,
|
setUserSelectedListenOnly,
|
||||||
@ -65,7 +63,7 @@ const {
|
|||||||
|
|
||||||
export default withUsersConsumer(
|
export default withUsersConsumer(
|
||||||
lockContextContainer(
|
lockContextContainer(
|
||||||
withModalMounter(withTracker(({ mountModal, userLocks, users }) => {
|
withTracker(({ userLocks, users }) => {
|
||||||
const currentUser = users[Auth.meetingID][Auth.userID];
|
const currentUser = users[Auth.meetingID][Auth.userID];
|
||||||
const isViewer = currentUser.role === ROLE_VIEWER;
|
const isViewer = currentUser.role === ROLE_VIEWER;
|
||||||
const isPresenter = currentUser.presenter;
|
const isPresenter = currentUser.presenter;
|
||||||
@ -87,15 +85,13 @@ export default withUsersConsumer(
|
|||||||
talking: isTalking() && !isMuted(),
|
talking: isTalking() && !isMuted(),
|
||||||
isVoiceUser: isVoiceUser(),
|
isVoiceUser: isVoiceUser(),
|
||||||
handleToggleMuteMicrophone: () => toggleMuteMicrophone(),
|
handleToggleMuteMicrophone: () => toggleMuteMicrophone(),
|
||||||
handleJoinAudio: () => (isConnected()
|
joinListenOnly,
|
||||||
? joinListenOnly()
|
|
||||||
: mountModal(<AudioModalContainer />)
|
|
||||||
),
|
|
||||||
handleLeaveAudio,
|
handleLeaveAudio,
|
||||||
inputStream: AudioManager.inputStream,
|
inputStream: AudioManager.inputStream,
|
||||||
isViewer,
|
isViewer,
|
||||||
isPresenter,
|
isPresenter,
|
||||||
|
isConnected,
|
||||||
});
|
});
|
||||||
})(AudioControlsContainer)),
|
})(AudioControlsContainer),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -14,6 +14,7 @@ const AUDIO_INPUT = 'audioinput';
|
|||||||
const AUDIO_OUTPUT = 'audiooutput';
|
const AUDIO_OUTPUT = 'audiooutput';
|
||||||
const DEFAULT_DEVICE = 'default';
|
const DEFAULT_DEVICE = 'default';
|
||||||
const DEVICE_LABEL_MAX_LENGTH = 40;
|
const DEVICE_LABEL_MAX_LENGTH = 40;
|
||||||
|
const SET_SINK_ID_SUPPORTED = 'setSinkId' in HTMLMediaElement.prototype;
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
changeAudioDevice: {
|
changeAudioDevice: {
|
||||||
@ -52,6 +53,10 @@ const intlMessages = defineMessages({
|
|||||||
id: 'app.audioNotification.deviceChangeFailed',
|
id: 'app.audioNotification.deviceChangeFailed',
|
||||||
description: 'Device change failed',
|
description: 'Device change failed',
|
||||||
},
|
},
|
||||||
|
defaultOutputDeviceLabel: {
|
||||||
|
id: 'app.audio.audioSettings.defaultOutputDeviceLabel',
|
||||||
|
description: 'Default output device label',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
@ -263,8 +268,10 @@ class InputStreamLiveSelector extends Component {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const deviceList = (listLength > 0)
|
let deviceList = [];
|
||||||
? list.map((device, index) => (
|
|
||||||
|
if (listLength > 0) {
|
||||||
|
deviceList = list.map((device, index) => (
|
||||||
{
|
{
|
||||||
key: `${device.deviceId}-${deviceKind}`,
|
key: `${device.deviceId}-${deviceKind}`,
|
||||||
dataTest: `${deviceKind}-${index + 1}`,
|
dataTest: `${deviceKind}-${index + 1}`,
|
||||||
@ -273,8 +280,21 @@ class InputStreamLiveSelector extends Component {
|
|||||||
iconRight: (device.deviceId === currentDeviceId) ? 'check' : null,
|
iconRight: (device.deviceId === currentDeviceId) ? 'check' : null,
|
||||||
onClick: () => this.onDeviceListClick(device.deviceId, deviceKind, callback),
|
onClick: () => this.onDeviceListClick(device.deviceId, deviceKind, callback),
|
||||||
}
|
}
|
||||||
))
|
));
|
||||||
: [
|
} else if (deviceKind === AUDIO_OUTPUT && !SET_SINK_ID_SUPPORTED && listLength === 0) {
|
||||||
|
// If the browser doesn't support setSinkId, show the chosen output device
|
||||||
|
// as a placeholder Default - like it's done in audio/device-selector
|
||||||
|
deviceList = [
|
||||||
|
{
|
||||||
|
key: `defaultDeviceKey-${deviceKind}`,
|
||||||
|
label: intl.formatMessage(intlMessages.defaultOutputDeviceLabel),
|
||||||
|
customStyles: Styled.SelectedLabel,
|
||||||
|
iconRight: 'check',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
deviceList = [
|
||||||
{
|
{
|
||||||
key: `noDeviceFoundKey-${deviceKind}-`,
|
key: `noDeviceFoundKey-${deviceKind}-`,
|
||||||
label: listLength < 0
|
label: listLength < 0
|
||||||
@ -282,6 +302,8 @@ class InputStreamLiveSelector extends Component {
|
|||||||
: intl.formatMessage(intlMessages.noDeviceFound),
|
: intl.formatMessage(intlMessages.noDeviceFound),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
return listTitle.concat(deviceList);
|
return listTitle.concat(deviceList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import AudioDial from '../audio-dial/component';
|
|||||||
import AudioAutoplayPrompt from '../autoplay/component';
|
import AudioAutoplayPrompt from '../autoplay/component';
|
||||||
import Settings from '/imports/ui/services/settings';
|
import Settings from '/imports/ui/services/settings';
|
||||||
import CaptionsSelectContainer from '/imports/ui/components/audio/captions/select/container';
|
import CaptionsSelectContainer from '/imports/ui/components/audio/captions/select/container';
|
||||||
import { showModal } from '/imports/ui/components/common/modal/service';
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
intl: PropTypes.shape({
|
intl: PropTypes.shape({
|
||||||
@ -177,7 +176,6 @@ class AudioModal extends Component {
|
|||||||
audioLocked,
|
audioLocked,
|
||||||
isUsingAudio,
|
isUsingAudio,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
window.addEventListener("CLOSE_AUDIO_MODAL", this.handleCloseAudioModal);
|
|
||||||
|
|
||||||
if (!isUsingAudio) {
|
if (!isUsingAudio) {
|
||||||
if (forceListenOnlyAttendee || audioLocked) return this.handleJoinListenOnly();
|
if (forceListenOnlyAttendee || audioLocked) return this.handleJoinListenOnly();
|
||||||
@ -212,7 +210,6 @@ class AudioModal extends Component {
|
|||||||
exitAudio();
|
exitAudio();
|
||||||
}
|
}
|
||||||
if (resolve) resolve();
|
if (resolve) resolve();
|
||||||
window.removeEventListener("CLOSE_AUDIO_MODAL", this.handleCloseAudioModal);
|
|
||||||
Session.set('audioModalIsOpen', false);
|
Session.set('audioModalIsOpen', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,10 +249,6 @@ class AudioModal extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCloseAudioModal = () => {
|
|
||||||
showModal(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleGoToEchoTest() {
|
handleGoToEchoTest() {
|
||||||
const { AudioError } = this.props;
|
const { AudioError } = this.props;
|
||||||
const { MIC_ERROR } = AudioError;
|
const { MIC_ERROR } = AudioError;
|
||||||
@ -599,6 +592,9 @@ class AudioModal extends Component {
|
|||||||
showPermissionsOvelay,
|
showPermissionsOvelay,
|
||||||
closeModal,
|
closeModal,
|
||||||
isIE,
|
isIE,
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
setIsOpen,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { content } = this.state;
|
const { content } = this.state;
|
||||||
@ -607,6 +603,7 @@ class AudioModal extends Component {
|
|||||||
<span>
|
<span>
|
||||||
{showPermissionsOvelay ? <PermissionsOverlay closeModal={closeModal} /> : null}
|
{showPermissionsOvelay ? <PermissionsOverlay closeModal={closeModal} /> : null}
|
||||||
<Styled.AudioModal
|
<Styled.AudioModal
|
||||||
|
modalName="AUDIO"
|
||||||
onRequestClose={closeModal}
|
onRequestClose={closeModal}
|
||||||
data-test="audioModal"
|
data-test="audioModal"
|
||||||
contentLabel={intl.formatMessage(intlMessages.ariaModalTitle)}
|
contentLabel={intl.formatMessage(intlMessages.ariaModalTitle)}
|
||||||
@ -619,6 +616,11 @@ class AudioModal extends Component {
|
|||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
{...{
|
||||||
|
setIsOpen,
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{isIE ? (
|
{isIE ? (
|
||||||
<Styled.BrowserWarning>
|
<Styled.BrowserWarning>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import browserInfo from '/imports/utils/browserInfo';
|
import browserInfo from '/imports/utils/browserInfo';
|
||||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||||
import AudioModal from './component';
|
import AudioModal from './component';
|
||||||
@ -25,7 +24,7 @@ const APP_CONFIG = Meteor.settings.public.app;
|
|||||||
const invalidDialNumbers = ['0', '613-555-1212', '613-555-1234', '0000'];
|
const invalidDialNumbers = ['0', '613-555-1212', '613-555-1234', '0000'];
|
||||||
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
||||||
|
|
||||||
export default lockContextContainer(withModalMounter(withTracker(({ userLocks }) => {
|
export default lockContextContainer(withTracker(({ userLocks, setIsOpen }) => {
|
||||||
const listenOnlyMode = getFromUserSettings('bbb_listen_only_mode', APP_CONFIG.listenOnlyMode);
|
const listenOnlyMode = getFromUserSettings('bbb_listen_only_mode', APP_CONFIG.listenOnlyMode);
|
||||||
const forceListenOnly = getFromUserSettings('bbb_force_listen_only', APP_CONFIG.forceListenOnly);
|
const forceListenOnly = getFromUserSettings('bbb_force_listen_only', APP_CONFIG.forceListenOnly);
|
||||||
const skipCheck = getFromUserSettings('bbb_skip_check_audio', APP_CONFIG.skipCheck);
|
const skipCheck = getFromUserSettings('bbb_skip_check_audio', APP_CONFIG.skipCheck);
|
||||||
@ -65,7 +64,7 @@ export default lockContextContainer(withModalMounter(withTracker(({ userLocks })
|
|||||||
|
|
||||||
return ({
|
return ({
|
||||||
meetingIsBreakout,
|
meetingIsBreakout,
|
||||||
closeModal,
|
closeModal: () => closeModal(() => setIsOpen(false)),
|
||||||
joinMicrophone: (skipEchoTest) => joinMicrophone(skipEchoTest || skipCheck || skipCheckOnJoin),
|
joinMicrophone: (skipEchoTest) => joinMicrophone(skipEchoTest || skipCheck || skipCheckOnJoin),
|
||||||
joinListenOnly,
|
joinListenOnly,
|
||||||
leaveEchoTest,
|
leaveEchoTest,
|
||||||
@ -100,4 +99,4 @@ export default lockContextContainer(withModalMounter(withTracker(({ userLocks })
|
|||||||
isRTL,
|
isRTL,
|
||||||
AudioError,
|
AudioError,
|
||||||
});
|
});
|
||||||
})(AudioModalContainer)));
|
})(AudioModalContainer));
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { showModal } from '/imports/ui/components/common/modal/service';
|
|
||||||
import Service from '../service';
|
import Service from '../service';
|
||||||
import Storage from '/imports/ui/services/storage/session';
|
import Storage from '/imports/ui/services/storage/session';
|
||||||
|
|
||||||
@ -38,7 +37,7 @@ export const joinMicrophone = (skipEchoTest = false) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return call.then(() => {
|
return call.then(() => {
|
||||||
window.dispatchEvent(new Event("CLOSE_AUDIO_MODAL"));
|
document.dispatchEvent(new Event("CLOSE_MODAL_AUDIO"));
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
@ -55,7 +54,7 @@ export const joinListenOnly = () => {
|
|||||||
// prop transitions to a state where it was handled OR the user opts
|
// prop transitions to a state where it was handled OR the user opts
|
||||||
// to close the modal.
|
// to close the modal.
|
||||||
if (!Service.autoplayBlocked()) {
|
if (!Service.autoplayBlocked()) {
|
||||||
window.dispatchEvent(new Event("CLOSE_AUDIO_MODAL"));
|
document.dispatchEvent(new Event("CLOSE_MODAL_AUDIO"));
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
@ -72,11 +71,11 @@ export const leaveEchoTest = () => {
|
|||||||
return Service.exitAudio();
|
return Service.exitAudio();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const closeModal = () => {
|
export const closeModal = (callback) => {
|
||||||
if (Service.isConnecting()) {
|
if (Service.isConnecting()) {
|
||||||
Service.forceExitAudio();
|
Service.forceExitAudio();
|
||||||
}
|
}
|
||||||
showModal(null);
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import styled, { css, keyframes } from 'styled-components';
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
||||||
import { colorPrimary } from '/imports/ui/stylesheets/styled-components/palette';
|
import { colorPrimary } from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import {
|
import {
|
||||||
@ -90,7 +90,7 @@ const ConnectingAnimation = styled.span`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const AudioModal = styled(Modal)`
|
const AudioModal = styled(ModalSimple)`
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
min-height: 20rem;
|
min-height: 20rem;
|
||||||
`;
|
`;
|
||||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import AudioTestContainer from '/imports/ui/components/audio/audio-test/container';
|
import AudioTestContainer from '/imports/ui/components/audio/audio-test/container';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import logger from '/imports/startup/client/logger';
|
import logger from '/imports/startup/client/logger';
|
||||||
@ -375,4 +374,4 @@ class AudioSettings extends React.Component {
|
|||||||
AudioSettings.propTypes = propTypes;
|
AudioSettings.propTypes = propTypes;
|
||||||
AudioSettings.defaultProps = defaultProps;
|
AudioSettings.defaultProps = defaultProps;
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(AudioSettings));
|
export default injectIntl(AudioSettings);
|
||||||
|
@ -2,7 +2,6 @@ import React, { PureComponent } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { Session } from 'meteor/session';
|
import { Session } from 'meteor/session';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { injectIntl, defineMessages } from 'react-intl';
|
||||||
import { range } from '/imports/utils/array-utils';
|
import { range } from '/imports/utils/array-utils';
|
||||||
import Auth from '/imports/ui/services/auth';
|
import Auth from '/imports/ui/services/auth';
|
||||||
@ -133,7 +132,24 @@ class AudioContainer extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
const { isAudioModalOpen, setAudioModalIsOpen,
|
||||||
|
setVideoPreviewModalIsOpen, isVideoPreviewModalOpen } = this.props;
|
||||||
|
return <>
|
||||||
|
{isAudioModalOpen ? <AudioModalContainer
|
||||||
|
{...{
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: setAudioModalIsOpen,
|
||||||
|
isOpen: isAudioModalOpen
|
||||||
|
}}
|
||||||
|
/> : null}
|
||||||
|
{isVideoPreviewModalOpen ? <VideoPreviewContainer
|
||||||
|
{...{
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: setVideoPreviewModalIsOpen,
|
||||||
|
isOpen: isVideoPreviewModalOpen
|
||||||
|
}}
|
||||||
|
/> : null}
|
||||||
|
</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +179,8 @@ const messages = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ mountModal, intl, userLocks }) => {
|
export default lockContextContainer(injectIntl(withTracker(({ intl, userLocks, isAudioModalOpen, setAudioModalIsOpen,
|
||||||
|
setVideoPreviewModalIsOpen, isVideoPreviewModalOpen }) => {
|
||||||
const { microphoneConstraints } = Settings.application;
|
const { microphoneConstraints } = Settings.application;
|
||||||
const autoJoin = getFromUserSettings('bbb_auto_join_audio', APP_CONFIG.autoJoin);
|
const autoJoin = getFromUserSettings('bbb_auto_join_audio', APP_CONFIG.autoJoin);
|
||||||
const enableVideo = getFromUserSettings('bbb_enable_video', KURENTO_CONFIG.enableVideo);
|
const enableVideo = getFromUserSettings('bbb_enable_video', KURENTO_CONFIG.enableVideo);
|
||||||
@ -174,14 +191,12 @@ export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ m
|
|||||||
const userSelectedListenOnly = didUserSelectedListenOnly();
|
const userSelectedListenOnly = didUserSelectedListenOnly();
|
||||||
const meetingIsBreakout = AppService.meetingIsBreakout();
|
const meetingIsBreakout = AppService.meetingIsBreakout();
|
||||||
const hasBreakoutRooms = AppService.getBreakoutRooms().length > 0;
|
const hasBreakoutRooms = AppService.getBreakoutRooms().length > 0;
|
||||||
const openAudioModal = () => new Promise((resolve) => {
|
const openAudioModal = () => setAudioModalIsOpen(true);
|
||||||
mountModal(<AudioModalContainer resolve={resolve} />);
|
|
||||||
});
|
|
||||||
|
|
||||||
const openVideoPreviewModal = () => new Promise((resolve) => {
|
const openVideoPreviewModal = () => {
|
||||||
if (userWebcam) return resolve();
|
if (userWebcam) return;
|
||||||
mountModal(<VideoPreviewContainer resolve={resolve} />);
|
setVideoPreviewModalIsOpen(true);
|
||||||
});
|
};
|
||||||
|
|
||||||
if (Service.isConnected() && !Service.isListenOnly()) {
|
if (Service.isConnected() && !Service.isListenOnly()) {
|
||||||
Service.updateAudioConstraints(microphoneConstraints);
|
Service.updateAudioConstraints(microphoneConstraints);
|
||||||
@ -208,11 +223,12 @@ export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ m
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setTimeout(() => openAudioModal().then(() => {
|
setTimeout(() => {
|
||||||
if (enableVideo && autoShareWebcam) {
|
openAudioModal();
|
||||||
openVideoPreviewModal();
|
if (enableVideo && autoShareWebcam) {
|
||||||
|
openVideoPreviewModal();
|
||||||
}
|
}
|
||||||
}), 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -222,6 +238,8 @@ export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ m
|
|||||||
meetingIsBreakout,
|
meetingIsBreakout,
|
||||||
userSelectedMicrophone,
|
userSelectedMicrophone,
|
||||||
userSelectedListenOnly,
|
userSelectedListenOnly,
|
||||||
|
isAudioModalOpen,
|
||||||
|
setAudioModalIsOpen,
|
||||||
init: async () => {
|
init: async () => {
|
||||||
await Service.init(messages, intl);
|
await Service.init(messages, intl);
|
||||||
const enableVideo = getFromUserSettings('bbb_enable_video', KURENTO_CONFIG.enableVideo);
|
const enableVideo = getFromUserSettings('bbb_enable_video', KURENTO_CONFIG.enableVideo);
|
||||||
@ -234,7 +252,9 @@ export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ m
|
|||||||
}
|
}
|
||||||
Session.set('audioModalIsOpen', true);
|
Session.set('audioModalIsOpen', true);
|
||||||
if (enableVideo && autoShareWebcam) {
|
if (enableVideo && autoShareWebcam) {
|
||||||
openAudioModal().then(() => { openVideoPreviewModal(); didMountAutoJoin = true; });
|
openAudioModal()
|
||||||
|
openVideoPreviewModal();
|
||||||
|
didMountAutoJoin = true;
|
||||||
} else if (!(
|
} else if (!(
|
||||||
userSelectedMicrophone
|
userSelectedMicrophone
|
||||||
&& userSelectedListenOnly
|
&& userSelectedListenOnly
|
||||||
@ -245,7 +265,7 @@ export default lockContextContainer(withModalMounter(injectIntl(withTracker(({ m
|
|||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})(AudioContainer))));
|
})(AudioContainer)));
|
||||||
|
|
||||||
AudioContainer.propTypes = {
|
AudioContainer.propTypes = {
|
||||||
hasBreakoutRooms: PropTypes.bool.isRequired,
|
hasBreakoutRooms: PropTypes.bool.isRequired,
|
||||||
|
@ -129,13 +129,12 @@ class DeviceSelector extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { options } = this.state;
|
const { options } = this.state;
|
||||||
const { isSafari } = browserInfo;
|
|
||||||
|
|
||||||
let notFoundOption;
|
let notFoundOption;
|
||||||
|
|
||||||
if (blocked) {
|
if (blocked) {
|
||||||
notFoundOption = <option value="finding">{intl.formatMessage(intlMessages.findingDevicesLabel)}</option>;
|
notFoundOption = <option value="finding">{intl.formatMessage(intlMessages.findingDevicesLabel)}</option>;
|
||||||
} else if (kind === 'audiooutput' && isSafari) {
|
} else if (kind === 'audiooutput' && !('setSinkId' in HTMLMediaElement.prototype)) {
|
||||||
const defaultOutputDeviceLabel = intl.formatMessage(intlMessages.defaultOutputDeviceLabel);
|
const defaultOutputDeviceLabel = intl.formatMessage(intlMessages.defaultOutputDeviceLabel);
|
||||||
notFoundOption = <option value="not-found">{defaultOutputDeviceLabel}</option>;
|
notFoundOption = <option value="not-found">{defaultOutputDeviceLabel}</option>;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled, { css, keyframes } from 'styled-components';
|
import styled, { css, keyframes } from 'styled-components';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import { colorBlack } from '/imports/ui/stylesheets/styled-components/palette';
|
import { colorBlack } from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import { jumboPaddingX } from '/imports/ui/stylesheets/styled-components/general';
|
import { jumboPaddingX } from '/imports/ui/stylesheets/styled-components/general';
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ const bounce = keyframes`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const PermissionsOverlayModal = styled(Modal)`
|
const PermissionsOverlayModal = styled(ModalSimple)`
|
||||||
${({ isFirefox }) => isFirefox && `
|
${({ isFirefox }) => isFirefox && `
|
||||||
top: 8em;
|
top: 8em;
|
||||||
left: 22em;
|
left: 22em;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
import ModalFullscreen from '/imports/ui/components/common/modal/fullscreen/component';
|
||||||
import Modal from '/imports/ui/components/common/modal/fullscreen/component';
|
|
||||||
import logger from '/imports/startup/client/logger';
|
import logger from '/imports/startup/client/logger';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import AudioService from '../audio/service';
|
import AudioService from '../audio/service';
|
||||||
@ -49,7 +48,6 @@ const propTypes = {
|
|||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
breakout: PropTypes.objectOf(Object).isRequired,
|
breakout: PropTypes.objectOf(Object).isRequired,
|
||||||
getURL: PropTypes.func.isRequired,
|
getURL: PropTypes.func.isRequired,
|
||||||
mountModal: PropTypes.func.isRequired,
|
|
||||||
breakoutURL: PropTypes.string.isRequired,
|
breakoutURL: PropTypes.string.isRequired,
|
||||||
isFreeJoin: PropTypes.bool.isRequired,
|
isFreeJoin: PropTypes.bool.isRequired,
|
||||||
voiceUserJoined: PropTypes.bool.isRequired,
|
voiceUserJoined: PropTypes.bool.isRequired,
|
||||||
@ -97,7 +95,7 @@ class BreakoutJoinConfirmation extends Component {
|
|||||||
handleJoinBreakoutConfirmation() {
|
handleJoinBreakoutConfirmation() {
|
||||||
const {
|
const {
|
||||||
getURL,
|
getURL,
|
||||||
mountModal,
|
setIsOpen,
|
||||||
breakoutURL,
|
breakoutURL,
|
||||||
isFreeJoin,
|
isFreeJoin,
|
||||||
voiceUserJoined,
|
voiceUserJoined,
|
||||||
@ -133,7 +131,7 @@ class BreakoutJoinConfirmation extends Component {
|
|||||||
|
|
||||||
Session.set('lastBreakoutIdOpened', selectValue);
|
Session.set('lastBreakoutIdOpened', selectValue);
|
||||||
window.open(url);
|
window.open(url);
|
||||||
mountModal(null);
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchJoinURL(selectValue) {
|
async fetchJoinURL(selectValue) {
|
||||||
@ -201,11 +199,13 @@ class BreakoutJoinConfirmation extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { intl, breakoutName, isFreeJoin } = this.props;
|
const { intl, breakoutName, isFreeJoin, setIsOpen,
|
||||||
|
isOpen, priority,
|
||||||
|
} = this.props;
|
||||||
const { waiting } = this.state;
|
const { waiting } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalFullscreen
|
||||||
title={intl.formatMessage(intlMessages.title)}
|
title={intl.formatMessage(intlMessages.title)}
|
||||||
confirm={{
|
confirm={{
|
||||||
callback: this.handleJoinBreakoutConfirmation,
|
callback: this.handleJoinBreakoutConfirmation,
|
||||||
@ -215,16 +215,22 @@ class BreakoutJoinConfirmation extends Component {
|
|||||||
disabled: waiting,
|
disabled: waiting,
|
||||||
}}
|
}}
|
||||||
dismiss={{
|
dismiss={{
|
||||||
|
callback: () => setIsOpen(false),
|
||||||
label: intl.formatMessage(intlMessages.dismissLabel),
|
label: intl.formatMessage(intlMessages.dismissLabel),
|
||||||
description: intl.formatMessage(intlMessages.dismissDesc),
|
description: intl.formatMessage(intlMessages.dismissDesc),
|
||||||
}}
|
}}
|
||||||
|
{...{
|
||||||
|
setIsOpen,
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{ isFreeJoin ? this.renderSelectMeeting() : `${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
|
{ isFreeJoin ? this.renderSelectMeeting() : `${intl.formatMessage(intlMessages.message)} ${breakoutName}?`}
|
||||||
</Modal>
|
</ModalFullscreen>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(BreakoutJoinConfirmation));
|
export default injectIntl(BreakoutJoinConfirmation);
|
||||||
|
|
||||||
BreakoutJoinConfirmation.propTypes = propTypes;
|
BreakoutJoinConfirmation.propTypes = propTypes;
|
||||||
|
@ -33,14 +33,13 @@ const requestJoinURL = (breakoutId) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withTracker(({ breakout, mountModal, breakoutName }) => {
|
export default withTracker(({ breakout, breakoutName }) => {
|
||||||
const isFreeJoin = breakout.freeJoin;
|
const isFreeJoin = breakout.freeJoin;
|
||||||
const { breakoutId } = breakout;
|
const { breakoutId } = breakout;
|
||||||
const url = getURL(breakoutId);
|
const url = getURL(breakoutId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isFreeJoin,
|
isFreeJoin,
|
||||||
mountModal,
|
|
||||||
breakoutName,
|
breakoutName,
|
||||||
breakoutURL: url,
|
breakoutURL: url,
|
||||||
breakouts: breakoutService.getBreakouts(),
|
breakouts: breakoutService.getBreakouts(),
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import BBBMenu from "/imports/ui/components/common/menu/component";
|
import BBBMenu from "/imports/ui/components/common/menu/component";
|
||||||
import CreateBreakoutRoomModal from '/imports/ui/components/actions-bar/create-breakout-room/container';
|
import CreateBreakoutRoomContainer from '/imports/ui/components/actions-bar/create-breakout-room/container';
|
||||||
import Trigger from "/imports/ui/components/common/control-header/right/component";
|
import Trigger from "/imports/ui/components/common/control-header/right/component";
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
@ -27,6 +26,11 @@ const intlMessages = defineMessages({
|
|||||||
class BreakoutDropdown extends PureComponent {
|
class BreakoutDropdown extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isCreateBreakoutRoomModalOpen: false,
|
||||||
|
};
|
||||||
|
this.setCreateBreakoutRoomModalIsOpen = this.setCreateBreakoutRoomModalIsOpen.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailableActions() {
|
getAvailableActions() {
|
||||||
@ -36,7 +40,6 @@ class BreakoutDropdown extends PureComponent {
|
|||||||
endAllBreakouts,
|
endAllBreakouts,
|
||||||
isMeteorConnected,
|
isMeteorConnected,
|
||||||
amIModerator,
|
amIModerator,
|
||||||
mountModal,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.menuItems = [];
|
this.menuItems = [];
|
||||||
@ -58,9 +61,7 @@ class BreakoutDropdown extends PureComponent {
|
|||||||
dataTest: 'openUpdateBreakoutUsersModal',
|
dataTest: 'openUpdateBreakoutUsersModal',
|
||||||
label: intl.formatMessage(intlMessages.manageUsers),
|
label: intl.formatMessage(intlMessages.manageUsers),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
mountModal(
|
this.setCreateBreakoutRoomModalIsOpen(true);
|
||||||
<CreateBreakoutRoomModal isUpdate />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -82,12 +83,19 @@ class BreakoutDropdown extends PureComponent {
|
|||||||
return this.menuItems;
|
return this.menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCreateBreakoutRoomModalIsOpen(value) {
|
||||||
|
this.setState({
|
||||||
|
isCreateBreakoutRoomModalOpen: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
intl,
|
intl,
|
||||||
isRTL,
|
isRTL,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { isCreateBreakoutRoomModalOpen } = this.state;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BBBMenu
|
<BBBMenu
|
||||||
@ -112,9 +120,18 @@ class BreakoutDropdown extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
actions={this.getAvailableActions()}
|
actions={this.getAvailableActions()}
|
||||||
/>
|
/>
|
||||||
|
{isCreateBreakoutRoomModalOpen ? <CreateBreakoutRoomContainer
|
||||||
|
{...{
|
||||||
|
isUpdate: true,
|
||||||
|
onRequestClose: () => this.setCreateBreakoutRoomModalIsOpen(false),
|
||||||
|
priority: "low",
|
||||||
|
setIsOpen: this.setCreateBreakoutRoomModalIsOpen,
|
||||||
|
isOpen: isCreateBreakoutRoomModalOpen
|
||||||
|
}}
|
||||||
|
/> : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(BreakoutDropdown));
|
export default injectIntl(BreakoutDropdown);
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Session } from 'meteor/session';
|
import { Session } from 'meteor/session';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
import BreakoutJoinConfirmationContainer from '/imports/ui/components/breakout-join-confirmation/container';
|
||||||
import BreakoutJoinConfirmation from '/imports/ui/components/breakout-join-confirmation/container';
|
|
||||||
import BreakoutService from '../service';
|
import BreakoutService from '../service';
|
||||||
|
|
||||||
const BREAKOUT_MODAL_DELAY = 200;
|
const BREAKOUT_MODAL_DELAY = 200;
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
mountModal: PropTypes.func.isRequired,
|
|
||||||
lastBreakoutReceived: PropTypes.shape({
|
lastBreakoutReceived: PropTypes.shape({
|
||||||
breakoutUrlData: PropTypes.object.isRequired,
|
breakoutUrlData: PropTypes.object.isRequired,
|
||||||
}),
|
}),
|
||||||
@ -26,20 +24,17 @@ const defaultProps = {
|
|||||||
breakouts: [],
|
breakouts: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const openBreakoutJoinConfirmation = (breakout, breakoutName, mountModal) => mountModal(
|
|
||||||
<BreakoutJoinConfirmation
|
|
||||||
breakout={breakout}
|
|
||||||
breakoutName={breakoutName}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
class BreakoutRoomInvitation extends Component {
|
class BreakoutRoomInvitation extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
didSendBreakoutInvite: false,
|
didSendBreakoutInvite: false,
|
||||||
|
isBreakoutJoinConfirmationModalOpen: false,
|
||||||
|
breakout: null,
|
||||||
|
breakoutName: null,
|
||||||
};
|
};
|
||||||
|
this.setBreakoutJoinConfirmationModalIsOpen = this.setBreakoutJoinConfirmationModalIsOpen.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -101,23 +96,43 @@ class BreakoutRoomInvitation extends Component {
|
|||||||
|
|
||||||
inviteUserToBreakout(breakout) {
|
inviteUserToBreakout(breakout) {
|
||||||
Session.set('lastBreakoutIdInvited', breakout.breakoutId);
|
Session.set('lastBreakoutIdInvited', breakout.breakoutId);
|
||||||
const {
|
|
||||||
mountModal,
|
|
||||||
} = this.props;
|
|
||||||
// There's a race condition on page load with modals. Only one modal can be shown at a
|
// There's a race condition on page load with modals. Only one modal can be shown at a
|
||||||
// time and new ones overwrite old ones. We delay the opening of the breakout modal
|
// time and new ones overwrite old ones. We delay the opening of the breakout modal
|
||||||
// because it should always be on top if breakouts are running.
|
// because it should always be on top if breakouts are running.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
openBreakoutJoinConfirmation.call(this, breakout, breakout.name, mountModal);
|
this.setState({
|
||||||
|
breakout: breakout,
|
||||||
|
breakoutName: breakout.name,
|
||||||
|
})
|
||||||
|
this.setBreakoutJoinConfirmationModalIsOpen(true);
|
||||||
}, BREAKOUT_MODAL_DELAY);
|
}, BREAKOUT_MODAL_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setBreakoutJoinConfirmationModalIsOpen(value) {
|
||||||
|
this.setState({
|
||||||
|
isBreakoutJoinConfirmationModalOpen: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return null;
|
const { isBreakoutJoinConfirmationModalOpen, breakout, breakoutName } = this.state;
|
||||||
|
return (<>
|
||||||
|
{isBreakoutJoinConfirmationModalOpen ? <BreakoutJoinConfirmationContainer
|
||||||
|
breakout={breakout}
|
||||||
|
breakoutName={breakoutName}
|
||||||
|
{...{
|
||||||
|
onRequestClose: () => this.setBreakoutJoinConfirmationModalIsOpen(false),
|
||||||
|
priority: "medium",
|
||||||
|
setIsOpen: this.setBreakoutJoinConfirmationModalIsOpen,
|
||||||
|
isOpen: isBreakoutJoinConfirmationModalOpen
|
||||||
|
}}
|
||||||
|
/> : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BreakoutRoomInvitation.propTypes = propTypes;
|
BreakoutRoomInvitation.propTypes = propTypes;
|
||||||
BreakoutRoomInvitation.defaultProps = defaultProps;
|
BreakoutRoomInvitation.defaultProps = defaultProps;
|
||||||
|
|
||||||
export default withModalMounter(BreakoutRoomInvitation);
|
export default BreakoutRoomInvitation;
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import Service from '/imports/ui/components/captions/service';
|
import Service from '/imports/ui/components/captions/service';
|
||||||
import CaptionsReaderMenuContainer from '/imports/ui/components/captions/reader-menu/container';
|
|
||||||
import CaptionButton from './component';
|
import CaptionButton from './component';
|
||||||
|
|
||||||
const Container = (props) => <CaptionButton {...props} />;
|
const Container = (props) => <CaptionButton {...props} />;
|
||||||
|
|
||||||
export default withModalMounter(withTracker(({ mountModal }) => ({
|
export default withTracker(({ setIsOpen }) => ({
|
||||||
isActive: Service.isCaptionsActive(),
|
isActive: Service.isCaptionsActive(),
|
||||||
handleOnClick: () => (Service.isCaptionsActive()
|
handleOnClick: () => (Service.isCaptionsActive()
|
||||||
? Service.deactivateCaptions()
|
? Service.deactivateCaptions()
|
||||||
: mountModal(<CaptionsReaderMenuContainer />)),
|
: setIsOpen(true)),
|
||||||
}))(Container));
|
}))(Container);
|
||||||
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
|||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import ColorPicker from "./color-picker/component";
|
import ColorPicker from "./color-picker/component";
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
|
|
||||||
const DEFAULT_VALUE = 'select';
|
const DEFAULT_VALUE = 'select';
|
||||||
@ -207,6 +206,8 @@ class ReaderMenu extends PureComponent {
|
|||||||
intl,
|
intl,
|
||||||
ownedLocales,
|
ownedLocales,
|
||||||
closeModal,
|
closeModal,
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -231,6 +232,10 @@ class ReaderMenu extends PureComponent {
|
|||||||
onRequestClose={closeModal}
|
onRequestClose={closeModal}
|
||||||
hideBorder
|
hideBorder
|
||||||
contentLabel={intl.formatMessage(intlMessages.title)}
|
contentLabel={intl.formatMessage(intlMessages.title)}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Styled.Title>
|
<Styled.Title>
|
||||||
{intl.formatMessage(intlMessages.title)}
|
{intl.formatMessage(intlMessages.title)}
|
||||||
@ -408,4 +413,4 @@ class ReaderMenu extends PureComponent {
|
|||||||
|
|
||||||
ReaderMenu.propTypes = propTypes;
|
ReaderMenu.propTypes = propTypes;
|
||||||
|
|
||||||
export default injectIntl(withModalMounter(ReaderMenu));
|
export default injectIntl(ReaderMenu);
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import ReaderMenu from './component';
|
import ReaderMenu from './component';
|
||||||
import CaptionsService from '/imports/ui/components/captions/service';
|
import CaptionsService from '/imports/ui/components/captions/service';
|
||||||
|
|
||||||
const ReaderMenuContainer = (props) => <ReaderMenu {...props} />;
|
const ReaderMenuContainer = (props) => <ReaderMenu {...props} />;
|
||||||
|
|
||||||
export default withModalMounter(withTracker(({ mountModal }) => ({
|
export default withTracker(({ setIsOpen }) => ({
|
||||||
closeModal: () => mountModal(null),
|
closeModal: () => setIsOpen(false),
|
||||||
activateCaptions: (locale, settings) => CaptionsService.activateCaptions(locale, settings),
|
activateCaptions: (locale, settings) => CaptionsService.activateCaptions(locale, settings),
|
||||||
getCaptionsSettings: () => CaptionsService.getCaptionsSettings(),
|
getCaptionsSettings: () => CaptionsService.getCaptionsSettings(),
|
||||||
ownedLocales: CaptionsService.getOwnedLocales(),
|
ownedLocales: CaptionsService.getOwnedLocales(),
|
||||||
}))(ReaderMenuContainer));
|
}))(ReaderMenuContainer);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import {
|
import {
|
||||||
colorGrayDark,
|
colorGrayDark,
|
||||||
colorWhite,
|
colorWhite,
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import { borderSize, borderSizeLarge } from '/imports/ui/stylesheets/styled-components/general';
|
import { borderSize, borderSizeLarge } from '/imports/ui/stylesheets/styled-components/general';
|
||||||
|
|
||||||
const ReaderMenuModal = styled(Modal)`
|
const ReaderMenuModal = styled(ModalSimple)`
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Service from '/imports/ui/components/captions/service';
|
import Service from '/imports/ui/components/captions/service';
|
||||||
import LocalesDropdown from '/imports/ui/components/common/locales-dropdown/component';
|
import LocalesDropdown from '/imports/ui/components/common/locales-dropdown/component';
|
||||||
@ -44,7 +43,6 @@ const intlMessages = defineMessages({
|
|||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
availableLocales: PropTypes.arrayOf(PropTypes.object).isRequired,
|
availableLocales: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
closeModal: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.shape({
|
intl: PropTypes.shape({
|
||||||
formatMessage: PropTypes.func.isRequired,
|
formatMessage: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
@ -68,9 +66,9 @@ class WriterMenu extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const { closeModal } = this.props;
|
const { setIsOpen } = this.props;
|
||||||
|
|
||||||
closeModal();
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(event) {
|
handleChange(event) {
|
||||||
@ -79,7 +77,7 @@ class WriterMenu extends PureComponent {
|
|||||||
|
|
||||||
handleStart() {
|
handleStart() {
|
||||||
const {
|
const {
|
||||||
closeModal,
|
setIsOpen,
|
||||||
layoutContextDispatch,
|
layoutContextDispatch,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -95,24 +93,33 @@ class WriterMenu extends PureComponent {
|
|||||||
value: PANELS.CAPTIONS,
|
value: PANELS.CAPTIONS,
|
||||||
});
|
});
|
||||||
|
|
||||||
closeModal();
|
setIsOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
intl,
|
intl,
|
||||||
availableLocales,
|
availableLocales,
|
||||||
closeModal,
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
priority,
|
||||||
|
setIsOpen
|
||||||
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { locale } = this.state;
|
const { locale } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.WriterMenuModal
|
<Styled.WriterMenuModal
|
||||||
onRequestClose={closeModal}
|
|
||||||
hideBorder
|
hideBorder
|
||||||
contentLabel={intl.formatMessage(intlMessages.title)}
|
contentLabel={intl.formatMessage(intlMessages.title)}
|
||||||
title={intl.formatMessage(intlMessages.title)}
|
title={intl.formatMessage(intlMessages.title)}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
priority,
|
||||||
|
setIsOpen
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
<span>
|
<span>
|
||||||
@ -151,4 +158,4 @@ class WriterMenu extends PureComponent {
|
|||||||
|
|
||||||
WriterMenu.propTypes = propTypes;
|
WriterMenu.propTypes = propTypes;
|
||||||
|
|
||||||
export default injectIntl(withModalMounter(WriterMenu));
|
export default injectIntl(WriterMenu);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { withTracker } from 'meteor/react-meteor-data';
|
import { withTracker } from 'meteor/react-meteor-data';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import Service from '/imports/ui/components/captions/service';
|
import Service from '/imports/ui/components/captions/service';
|
||||||
import WriterMenu from './component';
|
import WriterMenu from './component';
|
||||||
import { layoutDispatch } from '../../layout/context';
|
import { layoutDispatch } from '../../layout/context';
|
||||||
@ -20,7 +19,6 @@ const WriterMenuContainer = (props) => {
|
|||||||
return amIModerator && <WriterMenu {...{ layoutContextDispatch, ...props }} />;
|
return amIModerator && <WriterMenu {...{ layoutContextDispatch, ...props }} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withModalMounter(withTracker(({ mountModal }) => ({
|
export default withTracker(() => ({
|
||||||
closeModal: () => mountModal(null),
|
|
||||||
availableLocales: Service.getAvailableLocales(),
|
availableLocales: Service.getAvailableLocales(),
|
||||||
}))(WriterMenuContainer));
|
}))(WriterMenuContainer);
|
||||||
|
@ -12,9 +12,9 @@ import {
|
|||||||
colorPrimary,
|
colorPrimary,
|
||||||
} from '/imports/ui/stylesheets/styled-components/palette';
|
} from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
|
|
||||||
const WriterMenuModal = styled(Modal)`
|
const WriterMenuModal = styled(ModalSimple)`
|
||||||
min-height: 20rem;
|
min-height: 20rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import BBBMenu from '/imports/ui/components/common/menu/component';
|
import BBBMenu from '/imports/ui/components/common/menu/component';
|
||||||
import { getDateString, uniqueId } from '/imports/utils/string-utils';
|
import { getDateString, uniqueId } from '/imports/utils/string-utils';
|
||||||
import Trigger from '/imports/ui/components/common/control-header/right/component';
|
import Trigger from '/imports/ui/components/common/control-header/right/component';
|
||||||
@ -159,4 +158,4 @@ class ChatDropdown extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(ChatDropdown));
|
export default injectIntl(ChatDropdown);
|
||||||
|
@ -329,7 +329,6 @@ class TimeWindowList extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
key="chat-list"
|
key="chat-list"
|
||||||
data-test="chatMessages"
|
data-test="chatMessages"
|
||||||
aria-live="polite"
|
|
||||||
ref={node => this.messageListWrapper = node}
|
ref={node => this.messageListWrapper = node}
|
||||||
onCopy={(e) => { e.stopPropagation(); }}
|
onCopy={(e) => { e.stopPropagation(); }}
|
||||||
>
|
>
|
||||||
|
@ -164,6 +164,7 @@ export default class ButtonBase extends React.Component {
|
|||||||
'iconRight',
|
'iconRight',
|
||||||
'isVisualEffects',
|
'isVisualEffects',
|
||||||
'panning',
|
'panning',
|
||||||
|
'panSelected',
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -44,6 +44,7 @@ const EmojiButton = styled.button`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border: none;
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
[dir="rtl"] & {
|
[dir="rtl"] & {
|
||||||
right: initial;
|
right: initial;
|
||||||
|
@ -123,6 +123,8 @@ export default class Button extends BaseButton {
|
|||||||
'aria-label': ariaLabel,
|
'aria-label': ariaLabel,
|
||||||
'aria-expanded': ariaExpanded,
|
'aria-expanded': ariaExpanded,
|
||||||
tooltipLabel,
|
tooltipLabel,
|
||||||
|
tooltipdelay,
|
||||||
|
tooltipplacement,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const renderFuncName = circle ? 'renderCircle' : 'renderDefault';
|
const renderFuncName = circle ? 'renderCircle' : 'renderDefault';
|
||||||
@ -132,6 +134,8 @@ export default class Button extends BaseButton {
|
|||||||
return (
|
return (
|
||||||
<TooltipContainer
|
<TooltipContainer
|
||||||
title={tooltipLabel || buttonLabel}
|
title={tooltipLabel || buttonLabel}
|
||||||
|
delay={tooltipdelay}
|
||||||
|
placement={tooltipplacement}
|
||||||
>
|
>
|
||||||
{this[renderFuncName]()}
|
{this[renderFuncName]()}
|
||||||
</TooltipContainer>
|
</TooltipContainer>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import FallbackView from '../fallback-view/component';
|
import FallbackView from '../fallback-view/component';
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
@ -11,14 +10,17 @@ const intlMessages = defineMessages({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const FallbackModal = ({ error, intl, mountModal }) => (
|
const FallbackModal = ({ error, intl }) => {
|
||||||
|
return (
|
||||||
<ModalSimple
|
<ModalSimple
|
||||||
hideBorder
|
hideBorder
|
||||||
onRequestClose={() => mountModal(null)}
|
priority="medium"
|
||||||
|
shouldShowCloseButton={false}
|
||||||
contentLabel={intl.formatMessage(intlMessages.ariaTitle)}
|
contentLabel={intl.formatMessage(intlMessages.ariaTitle)}
|
||||||
|
isOpen={!!error}
|
||||||
>
|
>
|
||||||
<FallbackView {...{ error }} />
|
<FallbackView {...{ error }} />
|
||||||
</ModalSimple>
|
</ModalSimple>
|
||||||
);
|
)};
|
||||||
|
|
||||||
export default withModalMounter(injectIntl(FallbackModal));
|
export default injectIntl(FallbackModal);
|
||||||
|
@ -1,91 +1,39 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
import { registerTitleView, unregisterTitleView } from '/imports/utils/dom-utils';
|
|
||||||
|
|
||||||
const propTypes = {
|
const BaseModal = (props) => {
|
||||||
overlayClassName: PropTypes.string.isRequired,
|
const { setIsOpen, modalName, children,
|
||||||
portalClassName: PropTypes.string.isRequired,
|
isOpen, onRequestClose, className, overlayClassName,
|
||||||
contentLabel: PropTypes.string.isRequired,
|
} = props;
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultProps = {
|
const closeEventHandler = useCallback (() => {
|
||||||
overlayClassName: 'modalOverlay',
|
setIsOpen(false);
|
||||||
contentLabel: 'Modal',
|
} , []);
|
||||||
isOpen: true,
|
useEffect( () => {
|
||||||
};
|
// Only add event listener if name is specified
|
||||||
|
if(!modalName) return;
|
||||||
|
|
||||||
export default class ModalBase extends Component {
|
const closeEventName = `CLOSE_MODAL_${modalName.toUpperCase()}`;
|
||||||
|
|
||||||
componentDidMount() {
|
// Listen to close event on mount
|
||||||
registerTitleView(this.props.contentLabel);
|
document.addEventListener(closeEventName, closeEventHandler);
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
// Remove listener on unmount
|
||||||
unregisterTitleView();
|
return () => {
|
||||||
}
|
document.removeEventListener(closeEventName, closeEventHandler);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
const priority = props.priority ? props.priority : "low"
|
||||||
|
return (<Styled.BaseModal
|
||||||
|
portalClassName={`modal-${priority}`}
|
||||||
|
parentSelector={()=>document.querySelector('#modals-container')}
|
||||||
|
isOpen={isOpen}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
className={className}
|
||||||
|
overlayClassName={overlayClassName}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Styled.BaseModal>
|
||||||
|
)}
|
||||||
|
|
||||||
render() {
|
export default { BaseModal };
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
'data-test': dataTest,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Styled.BaseModal
|
|
||||||
{...this.props}
|
|
||||||
parentSelector={() => {
|
|
||||||
if (document.fullscreenElement &&
|
|
||||||
document.fullscreenElement.nodeName &&
|
|
||||||
document.fullscreenElement.nodeName.toLowerCase() === 'div')
|
|
||||||
return document.fullscreenElement;
|
|
||||||
else return document.body;
|
|
||||||
}}
|
|
||||||
data={{
|
|
||||||
test: dataTest ?? null,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{this.props.children}
|
|
||||||
</Styled.BaseModal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ModalBase.propTypes = propTypes;
|
|
||||||
ModalBase.defaultProps = defaultProps;
|
|
||||||
|
|
||||||
export const withModalState = ComponentToWrap =>
|
|
||||||
class ModalStateWrapper extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isOpen: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hide = this.hide.bind(this);
|
|
||||||
this.show = this.show.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
hide(cb = () => { }) {
|
|
||||||
Promise.resolve(cb())
|
|
||||||
.then(() => this.setState({ isOpen: false }));
|
|
||||||
}
|
|
||||||
|
|
||||||
show(cb = () => { }) {
|
|
||||||
Promise.resolve(cb())
|
|
||||||
.then(() => this.setState({ isOpen: true }));
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (<ComponentToWrap
|
|
||||||
{...this.props}
|
|
||||||
modalHide={this.hide}
|
|
||||||
modalShow={this.show}
|
|
||||||
modalisOpen={this.state.isOpen}
|
|
||||||
/>);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ class ConfirmationModal extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
intl,
|
intl,
|
||||||
mountModal,
|
setIsOpen,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
title,
|
title,
|
||||||
titleMessageId,
|
titleMessageId,
|
||||||
@ -51,6 +50,9 @@ class ConfirmationModal extends Component {
|
|||||||
confirmParam,
|
confirmParam,
|
||||||
disableConfirmButton,
|
disableConfirmButton,
|
||||||
description,
|
description,
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
priority,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -61,9 +63,14 @@ class ConfirmationModal extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.ConfirmationModal
|
<Styled.ConfirmationModal
|
||||||
onRequestClose={() => mountModal(null)}
|
onRequestClose={() => setIsOpen(false)}
|
||||||
contentLabel={title}
|
contentLabel={title}
|
||||||
title={title || intl.formatMessage({ id: titleMessageId }, { 0: titleMessageExtra })}
|
title={title || intl.formatMessage({ id: titleMessageId }, { 0: titleMessageExtra })}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
onRequestClose,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Styled.Container>
|
<Styled.Container>
|
||||||
<Styled.Description>
|
<Styled.Description>
|
||||||
@ -92,12 +99,12 @@ class ConfirmationModal extends Component {
|
|||||||
data-test={confirmButtonDataTest}
|
data-test={confirmButtonDataTest}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onConfirm(confirmParam, checked);
|
onConfirm(confirmParam, checked);
|
||||||
mountModal(null);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Styled.CancelButton
|
<Styled.CancelButton
|
||||||
label={intl.formatMessage(messages.noLabel)}
|
label={intl.formatMessage(messages.noLabel)}
|
||||||
onClick={() => mountModal(null)}
|
onClick={() => setIsOpen(false)}
|
||||||
/>
|
/>
|
||||||
</Styled.Footer>
|
</Styled.Footer>
|
||||||
</Styled.Container>
|
</Styled.Container>
|
||||||
@ -109,4 +116,4 @@ class ConfirmationModal extends Component {
|
|||||||
ConfirmationModal.propTypes = propTypes;
|
ConfirmationModal.propTypes = propTypes;
|
||||||
ConfirmationModal.defaultProps = defaultProps;
|
ConfirmationModal.defaultProps = defaultProps;
|
||||||
|
|
||||||
export default withModalMounter(ConfirmationModal);
|
export default ConfirmationModal;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import {
|
import {
|
||||||
smPaddingX,
|
smPaddingX,
|
||||||
mdPaddingX,
|
mdPaddingX,
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
import { colorGray } from '/imports/ui/stylesheets/styled-components/palette';
|
import { colorGray } from '/imports/ui/stylesheets/styled-components/palette';
|
||||||
import { lineHeightBase } from '/imports/ui/stylesheets/styled-components/typography';
|
import { lineHeightBase } from '/imports/ui/stylesheets/styled-components/typography';
|
||||||
|
|
||||||
const ConfirmationModal = styled(Modal)`
|
const ConfirmationModal = styled(ModalSimple)`
|
||||||
padding: ${mdPaddingX};
|
padding: ${mdPaddingX};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
import { withTracker } from 'meteor/react-meteor-data';
|
|
||||||
import { getModal } from './service';
|
|
||||||
|
|
||||||
export default withTracker(() => ({
|
|
||||||
modalComponent: getModal(),
|
|
||||||
}))(({ modalComponent }) => modalComponent);
|
|
@ -1,7 +1,6 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { withModalState } from '../base/component';
|
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
@ -61,7 +60,7 @@ class ModalFullscreen extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleAction(name) {
|
handleAction(name) {
|
||||||
const { confirm, dismiss, modalHide } = this.props;
|
const { confirm, dismiss } = this.props;
|
||||||
const { callback: callBackConfirm } = confirm;
|
const { callback: callBackConfirm } = confirm;
|
||||||
const { callback: callBackDismiss } = dismiss;
|
const { callback: callBackDismiss } = dismiss;
|
||||||
|
|
||||||
@ -78,7 +77,7 @@ class ModalFullscreen extends PureComponent {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return modalHide(callback);
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -89,7 +88,7 @@ class ModalFullscreen extends PureComponent {
|
|||||||
dismiss,
|
dismiss,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
modalisOpen,
|
isOpen,
|
||||||
preventClosing,
|
preventClosing,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -103,7 +102,7 @@ class ModalFullscreen extends PureComponent {
|
|||||||
return (
|
return (
|
||||||
<Styled.FullscreenModal
|
<Styled.FullscreenModal
|
||||||
id="fsmodal"
|
id="fsmodal"
|
||||||
isOpen={modalisOpen || preventClosing}
|
isOpen={isOpen || preventClosing}
|
||||||
contentLabel={title}
|
contentLabel={title}
|
||||||
overlayClassName={"fullscreenModalOverlay"}
|
overlayClassName={"fullscreenModalOverlay"}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
@ -147,4 +146,4 @@ class ModalFullscreen extends PureComponent {
|
|||||||
ModalFullscreen.propTypes = propTypes;
|
ModalFullscreen.propTypes = propTypes;
|
||||||
ModalFullscreen.defaultProps = defaultProps;
|
ModalFullscreen.defaultProps = defaultProps;
|
||||||
|
|
||||||
export default withModalState(injectIntl(ModalFullscreen));
|
export default injectIntl(ModalFullscreen);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Styled from '../base/styles';
|
import Styled from '../base/component';
|
||||||
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
|
||||||
import Button from '/imports/ui/components/common/button/component';
|
import Button from '/imports/ui/components/common/button/component';
|
||||||
import {
|
import {
|
||||||
|
@ -3,7 +3,6 @@ import Styled from './styles';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
|
||||||
hideBorder: PropTypes.bool,
|
hideBorder: PropTypes.bool,
|
||||||
headerPosition: PropTypes.string,
|
headerPosition: PropTypes.string,
|
||||||
shouldShowCloseButton: PropTypes.bool,
|
shouldShowCloseButton: PropTypes.bool,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Modal from '/imports/ui/components/common/modal/simple/component';
|
import ModalSimple from '/imports/ui/components/common/modal/simple/component';
|
||||||
import AudioService from '/imports/ui/components/audio/service';
|
import AudioService from '/imports/ui/components/audio/service';
|
||||||
import Styled from './styles';
|
import Styled from './styles';
|
||||||
|
|
||||||
@ -42,7 +42,6 @@ const propTypes = {
|
|||||||
intl: PropTypes.shape({
|
intl: PropTypes.shape({
|
||||||
formatMessage: PropTypes.func.isRequired,
|
formatMessage: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
mountModal: PropTypes.func.isRequired,
|
|
||||||
numAvailableViewers: PropTypes.number.isRequired,
|
numAvailableViewers: PropTypes.number.isRequired,
|
||||||
randomUserReq: PropTypes.func.isRequired,
|
randomUserReq: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
@ -123,11 +122,13 @@ class RandomUserSelect extends Component {
|
|||||||
keepModalOpen,
|
keepModalOpen,
|
||||||
toggleKeepModalOpen,
|
toggleKeepModalOpen,
|
||||||
intl,
|
intl,
|
||||||
mountModal,
|
setIsOpen,
|
||||||
numAvailableViewers,
|
numAvailableViewers,
|
||||||
currentUser,
|
currentUser,
|
||||||
clearRandomlySelectedUser,
|
clearRandomlySelectedUser,
|
||||||
mappedRandomlySelectedUsers,
|
mappedRandomlySelectedUsers,
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const counter = SELECT_RANDOM_USER_COUNTDOWN ? this.state.count : 0;
|
const counter = SELECT_RANDOM_USER_COUNTDOWN ? this.state.count : 0;
|
||||||
@ -189,17 +190,21 @@ class RandomUserSelect extends Component {
|
|||||||
}
|
}
|
||||||
if (keepModalOpen) {
|
if (keepModalOpen) {
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalSimple
|
||||||
onRequestClose={() => {
|
onRequestClose={() => {
|
||||||
if (currentUser.presenter) clearRandomlySelectedUser();
|
if (currentUser.presenter) clearRandomlySelectedUser();
|
||||||
toggleKeepModalOpen();
|
toggleKeepModalOpen();
|
||||||
mountModal(null);
|
setIsOpen(false);
|
||||||
}}
|
}}
|
||||||
contentLabel={intl.formatMessage(messages.ariaModalTitle)}
|
contentLabel={intl.formatMessage(messages.ariaModalTitle)}
|
||||||
title={title}
|
title={title}
|
||||||
|
{...{
|
||||||
|
isOpen,
|
||||||
|
priority,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{viewElement}
|
{viewElement}
|
||||||
</Modal>
|
</ModalSimple>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -3,7 +3,6 @@ import { withTracker } from 'meteor/react-meteor-data';
|
|||||||
import Meetings from '/imports/api/meetings';
|
import Meetings from '/imports/api/meetings';
|
||||||
import Users from '/imports/api/users';
|
import Users from '/imports/api/users';
|
||||||
import Auth from '/imports/ui/services/auth';
|
import Auth from '/imports/ui/services/auth';
|
||||||
import { withModalMounter } from '/imports/ui/components/common/modal/service';
|
|
||||||
import { makeCall } from '/imports/ui/services/api';
|
import { makeCall } from '/imports/ui/services/api';
|
||||||
import RandomUserSelect from './component';
|
import RandomUserSelect from './component';
|
||||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||||
@ -56,12 +55,14 @@ const RandomUserSelectContainer = (props) => {
|
|||||||
if (randomlySelectedUser) {
|
if (randomlySelectedUser) {
|
||||||
mappedRandomlySelectedUsers = randomlySelectedUser.map((ui) => {
|
mappedRandomlySelectedUsers = randomlySelectedUser.map((ui) => {
|
||||||
const selectedUser = users[Auth.meetingID][ui[0]];
|
const selectedUser = users[Auth.meetingID][ui[0]];
|
||||||
return [{
|
if (selectedUser){
|
||||||
userId: selectedUser.userId,
|
return [{
|
||||||
avatar: selectedUser.avatar,
|
userId: selectedUser.userId,
|
||||||
color: selectedUser.color,
|
avatar: selectedUser.avatar,
|
||||||
name: selectedUser.name,
|
color: selectedUser.color,
|
||||||
}, ui[1]];
|
name: selectedUser.name,
|
||||||
|
}, ui[1]];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +75,7 @@ const RandomUserSelectContainer = (props) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default withModalMounter(withTracker(({ mountModal }) => {
|
export default withTracker(() => {
|
||||||
const viewerPool = Users.find({
|
const viewerPool = Users.find({
|
||||||
meetingId: Auth.meetingID,
|
meetingId: Auth.meetingID,
|
||||||
presenter: { $ne: true },
|
presenter: { $ne: true },
|
||||||
@ -96,11 +97,10 @@ export default withModalMounter(withTracker(({ mountModal }) => {
|
|||||||
const clearRandomlySelectedUser = () => (SELECT_RANDOM_USER_ENABLED ? makeCall('clearRandomlySelectedUser') : null);
|
const clearRandomlySelectedUser = () => (SELECT_RANDOM_USER_ENABLED ? makeCall('clearRandomlySelectedUser') : null);
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
closeModal: () => mountModal(null),
|
|
||||||
toggleKeepModalOpen,
|
toggleKeepModalOpen,
|
||||||
numAvailableViewers: viewerPool.length,
|
numAvailableViewers: viewerPool.length,
|
||||||
randomUserReq,
|
randomUserReq,
|
||||||
clearRandomlySelectedUser,
|
clearRandomlySelectedUser,
|
||||||
randomlySelectedUser: meeting.randomlySelectedUser,
|
randomlySelectedUser: meeting.randomlySelectedUser,
|
||||||
});
|
});
|
||||||
})(RandomUserSelectContainer));
|
})(RandomUserSelectContainer);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user