Merge remote-tracking branch 'origin/update-breakout-duration' into update-breakout-duration

This commit is contained in:
Gustavo Trott 2022-02-24 13:53:01 -03:00
commit 6839a424c1
25 changed files with 196 additions and 154 deletions

View File

@ -24,14 +24,14 @@ case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
* Audit message sent to meeting to trigger updating clients of meeting time remaining. * Audit message sent to meeting to trigger updating clients of meeting time remaining.
* @param meetingId * @param meetingId
*/ */
case class SendTimeRemainingAuditInternalMsg(meetingId: String) extends InMessage case class SendTimeRemainingAuditInternalMsg(meetingId: String, timeUpdatedInMinutes: Int) extends InMessage
/** /**
* Parent message sent to breakout rooms to trigger updating clients of meeting time remaining. * Parent message sent to breakout rooms to trigger updating clients of meeting time remaining.
* @param meetingId * @param meetingId
* @param timeLeftInSec * @param timeLeftInSec
*/ */
case class SendBreakoutTimeRemainingInternalMsg(meetingId: String, timeLeftInSec: Long) extends InMessage case class SendBreakoutTimeRemainingInternalMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int) extends InMessage
case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
@ -76,12 +76,12 @@ case class BreakoutRoomUsersUpdateInternalMsg(parentId: String, breakoutId: Stri
case class EndBreakoutRoomInternalMsg(parentId: String, breakoutId: String, reason: String) extends InMessage case class EndBreakoutRoomInternalMsg(parentId: String, breakoutId: String, reason: String) extends InMessage
/** /**
* Sent by parent meeting to breakout room to extend time. * Sent by parent meeting to breakout room to update time.
* @param parentId * @param parentId
* @param breakoutId * @param breakoutId
* @param extendTimeInMinutes * @param durationInSeconds
*/ */
case class ExtendBreakoutRoomTimeInternalMsg(parentId: String, breakoutId: String, extendTimeInMinutes: Int) extends InMessage case class UpdateBreakoutRoomTimeInternalMsg(parentId: String, breakoutId: String, durationInSeconds: Int) extends InMessage
/** /**
* Sent by parent meeting to breakout room to extend time. * Sent by parent meeting to breakout room to extend time.

View File

@ -22,7 +22,7 @@ object BreakoutModel {
case class BreakoutModel( case class BreakoutModel(
startedOn: Option[Long], startedOn: Option[Long],
durationInMinutes: Int, durationInSeconds: Int,
rooms: Map[String, BreakoutRoom2x] rooms: Map[String, BreakoutRoom2x]
) { ) {
@ -78,8 +78,8 @@ case class BreakoutModel(
copy(rooms = rooms - id) copy(rooms = rooms - id)
} }
def extendTime(timeToExtendInMinutes: Int): BreakoutModel = { def setTime(newDurationInSeconds: Int): BreakoutModel = {
copy(durationInMinutes = durationInMinutes + timeToExtendInMinutes) copy(durationInSeconds = newDurationInSeconds)
} }
} }

View File

@ -10,14 +10,14 @@ trait BreakoutApp2x extends BreakoutRoomCreatedMsgHdlr
with BreakoutRoomUsersUpdateMsgHdlr with BreakoutRoomUsersUpdateMsgHdlr
with CreateBreakoutRoomsCmdMsgHdlr with CreateBreakoutRoomsCmdMsgHdlr
with EndAllBreakoutRoomsMsgHdlr with EndAllBreakoutRoomsMsgHdlr
with ExtendBreakoutRoomsTimeMsgHdlr with UpdateBreakoutRoomsTimeMsgHdlr
with SendMessageToAllBreakoutRoomsMsgHdlr with SendMessageToAllBreakoutRoomsMsgHdlr
with SendMessageToBreakoutRoomInternalMsgHdlr with SendMessageToBreakoutRoomInternalMsgHdlr
with RequestBreakoutJoinURLReqMsgHdlr with RequestBreakoutJoinURLReqMsgHdlr
with SendBreakoutUsersUpdateMsgHdlr with SendBreakoutUsersUpdateMsgHdlr
with TransferUserToMeetingRequestHdlr with TransferUserToMeetingRequestHdlr
with EndBreakoutRoomInternalMsgHdlr with EndBreakoutRoomInternalMsgHdlr
with ExtendBreakoutRoomTimeInternalMsgHdlr with UpdateBreakoutRoomTimeInternalMsgHdlr
with BreakoutRoomEndedInternalMsgHdlr { with BreakoutRoomEndedInternalMsgHdlr {
this: MeetingActor => this: MeetingActor =>

View File

@ -61,7 +61,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
breakout.freeJoin, breakout.freeJoin,
liveMeeting.props.voiceProp.dialNumber, liveMeeting.props.voiceProp.dialNumber,
breakout.voiceConf, breakout.voiceConf,
msg.body.durationInMinutes, msg.body.durationInMinutes * 60,
liveMeeting.props.password.moderatorPass, liveMeeting.props.password.moderatorPass,
liveMeeting.props.password.viewerPass, liveMeeting.props.password.viewerPass,
presId, presSlide, msg.body.record, presId, presSlide, msg.body.record,
@ -72,7 +72,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
outGW.send(event) outGW.send(event)
} }
val breakoutModel = new BreakoutModel(None, msg.body.durationInMinutes, rooms) val breakoutModel = new BreakoutModel(None, msg.body.durationInMinutes * 60, rooms)
state.update(Some(breakoutModel)) state.update(Some(breakoutModel))
} }

View File

@ -9,7 +9,7 @@ trait SendBreakoutTimeRemainingInternalMsgHdlr {
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleSendBreakoutTimeRemainingInternalMsg(msg: SendBreakoutTimeRemainingInternalMsg): Unit = { def handleSendBreakoutTimeRemainingInternalMsg(msg: SendBreakoutTimeRemainingInternalMsg): Unit = {
val event = MsgBuilder.buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, msg.timeLeftInSec.toInt) val event = MsgBuilder.buildMeetingTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, msg.timeLeftInSec.toInt, msg.timeUpdatedInMinutes)
outGW.send(event) outGW.send(event)
} }
} }

View File

@ -1,20 +1,20 @@
package org.bigbluebutton.core.apps.breakout package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.core.api.{ ExtendBreakoutRoomTimeInternalMsg } import org.bigbluebutton.core.api.{ UpdateBreakoutRoomTimeInternalMsg }
import org.bigbluebutton.core.domain.{ MeetingState2x } import org.bigbluebutton.core.domain.{ MeetingState2x }
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
trait ExtendBreakoutRoomTimeInternalMsgHdlr { trait UpdateBreakoutRoomTimeInternalMsgHdlr {
this: MeetingActor => this: MeetingActor =>
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleExtendBreakoutRoomTimeInternalMsgHdlr(msg: ExtendBreakoutRoomTimeInternalMsg, state: MeetingState2x): MeetingState2x = { def handleUpdateBreakoutRoomTimeInternalMsgHdlr(msg: UpdateBreakoutRoomTimeInternalMsg, state: MeetingState2x): MeetingState2x = {
val breakoutModel = for { val breakoutModel = for {
model <- state.breakout model <- state.breakout
} yield { } yield {
val updatedBreakoutModel = model.extendTime(msg.extendTimeInMinutes) val updatedBreakoutModel = model.setTime(msg.durationInSeconds)
updatedBreakoutModel updatedBreakoutModel
} }

View File

@ -1,37 +1,38 @@
package org.bigbluebutton.core.apps.breakout package org.bigbluebutton.core.apps.breakout
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ ExtendBreakoutRoomTimeInternalMsg, SendTimeRemainingAuditInternalMsg } import org.bigbluebutton.core.api.{ UpdateBreakoutRoomTimeInternalMsg, SendTimeRemainingAuditInternalMsg }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
import org.bigbluebutton.core.bus.BigBlueButtonEvent import org.bigbluebutton.core.bus.BigBlueButtonEvent
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil
trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait { trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
this: MeetingActor => this: MeetingActor =>
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleExtendBreakoutRoomsTimeMsg(msg: ExtendBreakoutRoomsTimeReqMsg, state: MeetingState2x): MeetingState2x = { def handleUpdateBreakoutRoomsTimeMsg(msg: UpdateBreakoutRoomsTimeReqMsg, state: MeetingState2x): MeetingState2x = {
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) { if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to extend time for breakout rooms for meeting." val reason = "No permission to update time for breakout rooms for meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
state state
} else if (msg.body.extendTimeInMinutes <= 0) { } else if (msg.body.timeInMinutes <= 0) {
log.error("Error while trying to extend {} minutes for breakout rooms time in meeting {}. Only positive values are allowed!", msg.body.extendTimeInMinutes, props.meetingProp.intId) log.error("Error while trying to update {} minutes for breakout rooms time in meeting {}. Only positive values are allowed!", msg.body.timeInMinutes, props.meetingProp.intId)
state state
} else { } else {
val updatedModel = for { val updatedModel = for {
breakoutModel <- state.breakout breakoutModel <- state.breakout
startedOn <- breakoutModel.startedOn startedOn <- breakoutModel.startedOn
} yield { } yield {
val breakoutRoomEndTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(breakoutModel.durationInMinutes) val newSecsRemaining = msg.body.timeInMinutes * 60
val breakoutRoomSecsRemaining = breakoutRoomEndTime - TimeUtil.millisToSeconds(System.currentTimeMillis()) val breakoutRoomSecsElapsed = TimeUtil.millisToSeconds(System.currentTimeMillis()) - TimeUtil.millisToSeconds(startedOn);
val newDurationInSeconds = breakoutRoomSecsElapsed.toInt + newSecsRemaining
var isExtendTimeHigherThanMeetingRemaining = false var isNewTimeHigherThanMeetingRemaining = false
if (state.expiryTracker.durationInMs > 0) { if (state.expiryTracker.durationInMs > 0) {
val mainRoomEndTime = state.expiryTracker.startedOnInMs + state.expiryTracker.durationInMs val mainRoomEndTime = state.expiryTracker.startedOnInMs + state.expiryTracker.durationInMs
@ -40,28 +41,28 @@ trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
//Avoid breakout room end later than main room //Avoid breakout room end later than main room
//Keep 5 seconds of margin to finish breakout room and send informations to parent meeting //Keep 5 seconds of margin to finish breakout room and send informations to parent meeting
if (breakoutRoomSecsRemaining + TimeUtil.minutesToSeconds(msg.body.extendTimeInMinutes) > (mainRoomSecsRemaining - 5)) { if (newSecsRemaining > (mainRoomSecsRemaining - 5)) {
log.error("Error while trying to extend {} minutes for breakout rooms time in meeting {}. Parent meeting will end up in {} minutes!", msg.body.extendTimeInMinutes, props.meetingProp.intId, mainRoomTimeRemainingInMinutes) log.error("Error while trying to update {} minutes for breakout rooms time in meeting {}. Parent meeting will end up in {} minutes!", msg.body.timeInMinutes, props.meetingProp.intId, mainRoomTimeRemainingInMinutes)
isExtendTimeHigherThanMeetingRemaining = true isNewTimeHigherThanMeetingRemaining = true
} }
} }
if (isExtendTimeHigherThanMeetingRemaining) { if (isNewTimeHigherThanMeetingRemaining) {
breakoutModel breakoutModel
} else { } else {
breakoutModel.rooms.values.foreach { room => breakoutModel.rooms.values.foreach { room =>
eventBus.publish(BigBlueButtonEvent(room.id, ExtendBreakoutRoomTimeInternalMsg(props.breakoutProps.parentId, room.id, msg.body.extendTimeInMinutes))) eventBus.publish(BigBlueButtonEvent(room.id, UpdateBreakoutRoomTimeInternalMsg(props.breakoutProps.parentId, room.id, newDurationInSeconds)))
} }
log.debug("Extending {} minutes for breakout rooms time in meeting {}", msg.body.extendTimeInMinutes, props.meetingProp.intId) log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
breakoutModel.extendTime(msg.body.extendTimeInMinutes) breakoutModel.setTime(newDurationInSeconds)
} }
} }
val event = buildExtendBreakoutRoomsTimeEvtMsg(msg.body.extendTimeInMinutes) val event = buildUpdateBreakoutRoomsTimeEvtMsg(msg.body.timeInMinutes)
outGW.send(event) outGW.send(event)
//Force Update time remaining in the clients //Force Update time remaining in the clients
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId))) eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId, msg.body.timeInMinutes)))
updatedModel match { updatedModel match {
case Some(model) => state.update(Some(model)) case Some(model) => state.update(Some(model))
@ -70,13 +71,13 @@ trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
} }
} }
def buildExtendBreakoutRoomsTimeEvtMsg(extendTimeInMinutes: Int): BbbCommonEnvCoreMsg = { def buildUpdateBreakoutRoomsTimeEvtMsg(timeInMinutes: Int): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(ExtendBreakoutRoomsTimeEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(UpdateBreakoutRoomsTimeEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ExtendBreakoutRoomsTimeEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used") val header = BbbClientMsgHeader(UpdateBreakoutRoomsTimeEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
val body = ExtendBreakoutRoomsTimeEvtMsgBody(props.meetingProp.intId, extendTimeInMinutes) val body = UpdateBreakoutRoomsTimeEvtMsgBody(props.meetingProp.intId, timeInMinutes)
val event = ExtendBreakoutRoomsTimeEvtMsg(header, body) val event = UpdateBreakoutRoomsTimeEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }

View File

@ -223,8 +223,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[EndAllBreakoutRoomsMsg](envelope, jsonNode) routeGenericMsg[EndAllBreakoutRoomsMsg](envelope, jsonNode)
case TransferUserToMeetingRequestMsg.NAME => case TransferUserToMeetingRequestMsg.NAME =>
routeGenericMsg[TransferUserToMeetingRequestMsg](envelope, jsonNode) routeGenericMsg[TransferUserToMeetingRequestMsg](envelope, jsonNode)
case ExtendBreakoutRoomsTimeReqMsg.NAME => case UpdateBreakoutRoomsTimeReqMsg.NAME =>
routeGenericMsg[ExtendBreakoutRoomsTimeReqMsg](envelope, jsonNode) routeGenericMsg[UpdateBreakoutRoomsTimeReqMsg](envelope, jsonNode)
case SendMessageToAllBreakoutRoomsReqMsg.NAME => case SendMessageToAllBreakoutRoomsReqMsg.NAME =>
routeGenericMsg[SendMessageToAllBreakoutRoomsReqMsg](envelope, jsonNode) routeGenericMsg[SendMessageToAllBreakoutRoomsReqMsg](envelope, jsonNode)

View File

@ -283,7 +283,7 @@ class MeetingActor(
case msg: SendBreakoutUsersAuditInternalMsg => handleSendBreakoutUsersUpdateInternalMsg(msg) case msg: SendBreakoutUsersAuditInternalMsg => handleSendBreakoutUsersUpdateInternalMsg(msg)
case msg: BreakoutRoomUsersUpdateInternalMsg => state = handleBreakoutRoomUsersUpdateInternalMsg(msg, state) case msg: BreakoutRoomUsersUpdateInternalMsg => state = handleBreakoutRoomUsersUpdateInternalMsg(msg, state)
case msg: EndBreakoutRoomInternalMsg => handleEndBreakoutRoomInternalMsg(msg) case msg: EndBreakoutRoomInternalMsg => handleEndBreakoutRoomInternalMsg(msg)
case msg: ExtendBreakoutRoomTimeInternalMsg => state = handleExtendBreakoutRoomTimeInternalMsgHdlr(msg, state) case msg: UpdateBreakoutRoomTimeInternalMsg => state = handleUpdateBreakoutRoomTimeInternalMsgHdlr(msg, state)
case msg: BreakoutRoomEndedInternalMsg => state = handleBreakoutRoomEndedInternalMsg(msg, state) case msg: BreakoutRoomEndedInternalMsg => state = handleBreakoutRoomEndedInternalMsg(msg, state)
case msg: SendMessageToBreakoutRoomInternalMsg => state = handleSendMessageToBreakoutRoomInternalMsg(msg, state, liveMeeting, msgBus) case msg: SendMessageToBreakoutRoomInternalMsg => state = handleSendMessageToBreakoutRoomInternalMsg(msg, state, liveMeeting, msgBus)
case msg: SendBreakoutTimeRemainingInternalMsg => handleSendBreakoutTimeRemainingInternalMsg(msg) case msg: SendBreakoutTimeRemainingInternalMsg => handleSendBreakoutTimeRemainingInternalMsg(msg)
@ -454,7 +454,7 @@ class MeetingActor(
case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state) case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state)
case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state) case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state)
case m: TransferUserToMeetingRequestMsg => state = handleTransferUserToMeetingRequestMsg(m, state) case m: TransferUserToMeetingRequestMsg => state = handleTransferUserToMeetingRequestMsg(m, state)
case m: ExtendBreakoutRoomsTimeReqMsg => state = handleExtendBreakoutRoomsTimeMsg(m, state) case m: UpdateBreakoutRoomsTimeReqMsg => state = handleUpdateBreakoutRoomsTimeMsg(m, state)
case m: SendMessageToAllBreakoutRoomsReqMsg => state = handleSendMessageToAllBreakoutRoomsMsg(m, state) case m: SendMessageToAllBreakoutRoomsReqMsg => state = handleSendMessageToAllBreakoutRoomsMsg(m, state)
// Voice // Voice

View File

@ -73,7 +73,7 @@ class MeetingActorAudit(
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsersInternalMsg(props.meetingProp.intId))) eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsersInternalMsg(props.meetingProp.intId)))
// Trigger updating users of time remaining on meeting. // Trigger updating users of time remaining on meeting.
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId))) eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendTimeRemainingAuditInternalMsg(props.meetingProp.intId, 0)))
if (props.meetingProp.isBreakout) { if (props.meetingProp.isBreakout) {
// This is a breakout room. Update the main meeting with list of users in this breakout room. // This is a breakout room. Update the main meeting with list of users in this breakout room.

View File

@ -60,7 +60,7 @@ class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging {
case m: RequestBreakoutJoinURLReqMsg => logMessage(msg) case m: RequestBreakoutJoinURLReqMsg => logMessage(msg)
case m: EndAllBreakoutRoomsMsg => logMessage(msg) case m: EndAllBreakoutRoomsMsg => logMessage(msg)
case m: TransferUserToMeetingRequestMsg => logMessage(msg) case m: TransferUserToMeetingRequestMsg => logMessage(msg)
case m: ExtendBreakoutRoomsTimeReqMsg => logMessage(msg) case m: UpdateBreakoutRoomsTimeReqMsg => logMessage(msg)
case m: SendMessageToAllBreakoutRoomsReqMsg => logMessage(msg) case m: SendMessageToAllBreakoutRoomsReqMsg => logMessage(msg)
case m: UserLeftVoiceConfToClientEvtMsg => logMessage(msg) case m: UserLeftVoiceConfToClientEvtMsg => logMessage(msg)
case m: UserLeftVoiceConfEvtMsg => logMessage(msg) case m: UserLeftVoiceConfEvtMsg => logMessage(msg)

View File

@ -18,19 +18,19 @@ trait SendBreakoutTimeRemainingMsgHdlr {
model <- state.breakout model <- state.breakout
startedOn <- model.startedOn startedOn <- model.startedOn
} yield { } yield {
val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(model.durationInMinutes) val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + model.durationInSeconds
val timeRemaining = endMeetingTime - TimeUtil.millisToSeconds(System.currentTimeMillis()) val timeRemaining = endMeetingTime - TimeUtil.millisToSeconds(System.currentTimeMillis())
if (!liveMeeting.props.meetingProp.isBreakout) { if (!liveMeeting.props.meetingProp.isBreakout) {
// Notify parent meeting users of breakout rooms time remaining // Notify parent meeting users of breakout rooms time remaining
val event = buildBreakoutRoomsTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, timeRemaining.toInt) val event = buildBreakoutRoomsTimeRemainingUpdateEvtMsg(liveMeeting.props.meetingProp.intId, timeRemaining.toInt, 0)
outGW.send(event) outGW.send(event)
} }
// Tell all breakout rooms of time remaining so they can notify their users. // Tell all breakout rooms of time remaining so they can notify their users.
// This syncs all rooms about time remaining. // This syncs all rooms about time remaining.
model.rooms.values.foreach { room => model.rooms.values.foreach { room =>
eventBus.publish(BigBlueButtonEvent(room.id, SendBreakoutTimeRemainingInternalMsg(props.breakoutProps.parentId, timeRemaining.toInt))) eventBus.publish(BigBlueButtonEvent(room.id, SendBreakoutTimeRemainingInternalMsg(props.breakoutProps.parentId, timeRemaining.toInt, msg.timeUpdatedInMinutes)))
} }
if (timeRemaining < 0) { if (timeRemaining < 0) {
@ -41,7 +41,7 @@ trait SendBreakoutTimeRemainingMsgHdlr {
state state
} }
def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { def buildBreakoutRoomsTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int = 0): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(BreakoutRoomsTimeRemainingUpdateEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(BreakoutRoomsTimeRemainingUpdateEvtMsg.NAME, routing)
val body = BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeLeftInSec) val body = BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeLeftInSec)

View File

@ -545,10 +545,10 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long): BbbCommonEnvCoreMsg = { def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int = 0): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used") val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)
val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec) val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec, timeUpdatedInMinutes)
val header = BbbClientMsgHeader(MeetingTimeRemainingUpdateEvtMsg.NAME, meetingId, "not-used") val header = BbbClientMsgHeader(MeetingTimeRemainingUpdateEvtMsg.NAME, meetingId, "not-used")
val event = MeetingTimeRemainingUpdateEvtMsg(header, body) val event = MeetingTimeRemainingUpdateEvtMsg(header, body)

View File

@ -31,7 +31,7 @@ case class BreakoutRoomsTimeRemainingUpdateEvtMsg(
header: BbbClientMsgHeader, header: BbbClientMsgHeader,
body: BreakoutRoomsTimeRemainingUpdateEvtMsgBody body: BreakoutRoomsTimeRemainingUpdateEvtMsgBody
) extends BbbCoreMsg ) extends BbbCoreMsg
case class BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeRemaining: Long) case class BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeRemaining: Long, timeUpdatedInMinutes: Int)
/** /**
* Sent to bbb-web to create breakout rooms. * Sent to bbb-web to create breakout rooms.
@ -94,13 +94,13 @@ object UpdateBreakoutUsersEvtMsg { val NAME = "UpdateBreakoutUsersEvtMsg" }
case class UpdateBreakoutUsersEvtMsg(header: BbbClientMsgHeader, body: UpdateBreakoutUsersEvtMsgBody) extends BbbCoreMsg case class UpdateBreakoutUsersEvtMsg(header: BbbClientMsgHeader, body: UpdateBreakoutUsersEvtMsgBody) extends BbbCoreMsg
case class UpdateBreakoutUsersEvtMsgBody(parentId: String, breakoutId: String, users: Vector[BreakoutUserVO]) case class UpdateBreakoutUsersEvtMsgBody(parentId: String, breakoutId: String, users: Vector[BreakoutUserVO])
object ExtendBreakoutRoomsTimeReqMsg { val NAME = "ExtendBreakoutRoomsTimeReqMsg" } object UpdateBreakoutRoomsTimeReqMsg { val NAME = "UpdateBreakoutRoomsTimeReqMsg" }
case class ExtendBreakoutRoomsTimeReqMsg(header: BbbClientMsgHeader, body: ExtendBreakoutRoomsTimeReqMsgBody) extends StandardMsg case class UpdateBreakoutRoomsTimeReqMsg(header: BbbClientMsgHeader, body: UpdateBreakoutRoomsTimeReqMsgBody) extends StandardMsg
case class ExtendBreakoutRoomsTimeReqMsgBody(meetingId: String, extendTimeInMinutes: Int) case class UpdateBreakoutRoomsTimeReqMsgBody(meetingId: String, timeInMinutes: Int)
object ExtendBreakoutRoomsTimeEvtMsg { val NAME = "ExtendBreakoutRoomsTimeEvtMsg" } object UpdateBreakoutRoomsTimeEvtMsg { val NAME = "UpdateBreakoutRoomsTimeEvtMsg" }
case class ExtendBreakoutRoomsTimeEvtMsg(header: BbbClientMsgHeader, body: ExtendBreakoutRoomsTimeEvtMsgBody) extends BbbCoreMsg case class UpdateBreakoutRoomsTimeEvtMsg(header: BbbClientMsgHeader, body: UpdateBreakoutRoomsTimeEvtMsgBody) extends BbbCoreMsg
case class ExtendBreakoutRoomsTimeEvtMsgBody(meetingId: String, extendTimeInMinutes: Int) case class UpdateBreakoutRoomsTimeEvtMsgBody(meetingId: String, timeInMinutes: Int)
object SendMessageToAllBreakoutRoomsReqMsg { val NAME = "SendMessageToAllBreakoutRoomsReqMsg" } object SendMessageToAllBreakoutRoomsReqMsg { val NAME = "SendMessageToAllBreakoutRoomsReqMsg" }
case class SendMessageToAllBreakoutRoomsReqMsg(header: BbbClientMsgHeader, body: SendMessageToAllBreakoutRoomsReqMsgBody) extends StandardMsg case class SendMessageToAllBreakoutRoomsReqMsg(header: BbbClientMsgHeader, body: SendMessageToAllBreakoutRoomsReqMsgBody) extends StandardMsg

View File

@ -2,13 +2,13 @@ import { Meteor } from 'meteor/meteor';
import createBreakoutRoom from '/imports/api/breakouts/server/methods/createBreakout'; import createBreakoutRoom from '/imports/api/breakouts/server/methods/createBreakout';
import requestJoinURL from './methods/requestJoinURL'; import requestJoinURL from './methods/requestJoinURL';
import endAllBreakouts from './methods/endAllBreakouts'; import endAllBreakouts from './methods/endAllBreakouts';
import extendBreakoutsTime from './methods/extendBreakoutsTime'; import setBreakoutsTime from '/imports/api/breakouts/server/methods/setBreakoutsTime';
import sendMessageToAllBreakouts from './methods/sendMessageToAllBreakouts'; import sendMessageToAllBreakouts from './methods/sendMessageToAllBreakouts';
Meteor.methods({ Meteor.methods({
requestJoinURL, requestJoinURL,
createBreakoutRoom, createBreakoutRoom,
endAllBreakouts, endAllBreakouts,
extendBreakoutsTime, setBreakoutsTime,
sendMessageToAllBreakouts, sendMessageToAllBreakouts,
}); });

View File

@ -4,10 +4,10 @@ import { extractCredentials } from '/imports/api/common/server/helpers';
import { check } from 'meteor/check'; import { check } from 'meteor/check';
import Logger from '/imports/startup/server/logger'; import Logger from '/imports/startup/server/logger';
export default function extendBreakoutsTime({ extendTimeInMinutes }) { export default function setBreakoutsTime({ timeInMinutes }) {
const REDIS_CONFIG = Meteor.settings.private.redis; const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps; const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'ExtendBreakoutRoomsTimeReqMsg'; const EVENT_NAME = 'UpdateBreakoutRoomsTimeReqMsg';
try { try {
const { meetingId, requesterUserId } = extractCredentials(this.userId); const { meetingId, requesterUserId } = extractCredentials(this.userId);
@ -15,14 +15,14 @@ export default function extendBreakoutsTime({ extendTimeInMinutes }) {
check(meetingId, String); check(meetingId, String);
check(requesterUserId, String); check(requesterUserId, String);
return RedisPubSub.publishUserMessage( RedisPubSub.publishUserMessage(
CHANNEL, EVENT_NAME, meetingId, requesterUserId, CHANNEL, EVENT_NAME, meetingId, requesterUserId,
{ {
meetingId, meetingId,
extendTimeInMinutes, timeInMinutes,
}, },
); );
} catch (err) { } catch (err) {
Logger.error(`Exception while invoking method extendBreakoutsTime ${err.stack}`); Logger.error(`Exception while invoking method setBreakoutsTime ${err.stack}`);
} }
} }

View File

@ -24,7 +24,8 @@ export default function addSystemMsg(meetingId, chatId, msg) {
timestamp: Number, timestamp: Number,
sender: Object, sender: Object,
message: String, message: String,
extra: Object, messageValues: Match.Maybe(Object),
extra: Match.Maybe(Object),
correlationId: Match.Maybe(String), correlationId: Match.Maybe(String),
}); });
const msgDocument = { const msgDocument = {

View File

@ -1,14 +1,16 @@
import { check } from 'meteor/check'; import { check } from 'meteor/check';
import { MeetingTimeRemaining } from '/imports/api/meetings'; import Meetings, { MeetingTimeRemaining } from '/imports/api/meetings';
import Logger from '/imports/startup/server/logger'; import Logger from '/imports/startup/server/logger';
import addSystemMsg from '/imports/api/group-chat-msg/server/modifiers/addSystemMsg';
export default function handleTimeRemainingUpdate({ body }, meetingId) { export default function handleTimeRemainingUpdate({ body }, meetingId) {
check(meetingId, String); check(meetingId, String);
check(body, { check(body, {
timeLeftInSec: Number, timeLeftInSec: Number,
timeUpdatedInMinutes: Number,
}); });
const { timeLeftInSec } = body; const { timeLeftInSec, timeUpdatedInMinutes } = body;
const selector = { const selector = {
meetingId, meetingId,
@ -25,4 +27,34 @@ export default function handleTimeRemainingUpdate({ body }, meetingId) {
} catch (err) { } catch (err) {
Logger.error(`Changing recording time: ${err}`); Logger.error(`Changing recording time: ${err}`);
} }
if (timeUpdatedInMinutes > 0) {
const Meeting = Meetings.findOne({ meetingId });
if (Meeting.meetingProp.isBreakout) {
const CHAT_CONFIG = Meteor.settings.public.chat;
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
const PUBLIC_CHAT_SYSTEM_ID = CHAT_CONFIG.system_userid;
const PUBLIC_CHAT_INFO = CHAT_CONFIG.system_messages_keys.chat_info;
const SYSTEM_CHAT_TYPE = CHAT_CONFIG.type_system;
const messageValues = {
0: timeUpdatedInMinutes,
};
const payload = {
id: `${SYSTEM_CHAT_TYPE}-${PUBLIC_CHAT_INFO}`,
timestamp: Date.now(),
correlationId: `${PUBLIC_CHAT_SYSTEM_ID}-${Date.now()}`,
sender: {
id: PUBLIC_CHAT_SYSTEM_ID,
name: '',
},
message: 'breakoutDurationUpdated',
messageValues,
};
addSystemMsg(meetingId, PUBLIC_GROUP_CHAT_ID, payload);
}
}
} }

View File

@ -63,21 +63,21 @@ const intlMessages = defineMessages({
id: 'app.createBreakoutRoom.alreadyConnected', id: 'app.createBreakoutRoom.alreadyConnected',
description: 'label for the user that is already connected to breakout room', description: 'label for the user that is already connected to breakout room',
}, },
extendTimeInMinutes: { setTimeInMinutes: {
id: 'app.createBreakoutRoom.extendTimeInMinutes', id: 'app.createBreakoutRoom.setTimeInMinutes',
description: 'Label for input to extend time (minutes)', description: 'Label for input to set time (minutes)',
}, },
extendTimeLabel: { setTimeLabel: {
id: 'app.createBreakoutRoom.extendTimeLabel', id: 'app.createBreakoutRoom.setTimeLabel',
description: 'Button label to incresce breakout rooms time', description: 'Button label to set breakout rooms time',
}, },
extendTimeCancel: { setTimeCancel: {
id: 'app.createBreakoutRoom.extendTimeCancel', id: 'app.createBreakoutRoom.setTimeCancel',
description: 'Button label to cancel extend breakout rooms time', description: 'Button label to cancel set breakout rooms time',
}, },
extendTimeHigherThanMeetingTimeError: { setTimeHigherThanMeetingTimeError: {
id: 'app.createBreakoutRoom.extendTimeHigherThanMeetingTimeError', id: 'app.createBreakoutRoom.setTimeHigherThanMeetingTimeError',
description: 'Label for error when extend breakout rooms time would be higher than remaining time in parent meeting', description: 'Label for error when new breakout rooms time would be higher than remaining time in parent meeting',
}, },
}); });
@ -104,9 +104,9 @@ class BreakoutRoom extends PureComponent {
this.getBreakoutLabel = this.getBreakoutLabel.bind(this); this.getBreakoutLabel = this.getBreakoutLabel.bind(this);
this.renderDuration = this.renderDuration.bind(this); this.renderDuration = this.renderDuration.bind(this);
this.transferUserToBreakoutRoom = this.transferUserToBreakoutRoom.bind(this); this.transferUserToBreakoutRoom = this.transferUserToBreakoutRoom.bind(this);
this.changeExtendTime = this.changeExtendTime.bind(this); this.changeSetTime = this.changeSetTime.bind(this);
this.showExtendTimeForm = this.showExtendTimeForm.bind(this); this.showSetTimeForm = this.showSetTimeForm.bind(this);
this.resetExtendTimeForm = this.resetExtendTimeForm.bind(this); this.resetSetTimeForm = this.resetSetTimeForm.bind(this);
this.renderUserActions = this.renderUserActions.bind(this); this.renderUserActions = this.renderUserActions.bind(this);
this.returnBackToMeeeting = this.returnBackToMeeeting.bind(this); this.returnBackToMeeeting = this.returnBackToMeeeting.bind(this);
this.closePanel = this.closePanel.bind(this); this.closePanel = this.closePanel.bind(this);
@ -116,9 +116,9 @@ class BreakoutRoom extends PureComponent {
generated: false, generated: false,
joinedAudioOnly: false, joinedAudioOnly: false,
breakoutId: '', breakoutId: '',
visibleExtendTimeForm: false, visibleSetTimeForm: false,
visibleExtendTimeHigherThanMeetingTimeError: false, visibleSetTimeHigherThanMeetingTimeError: false,
extendTime: 5, newTime: 15,
}; };
} }
@ -219,21 +219,21 @@ class BreakoutRoom extends PureComponent {
this.setState({ joinedAudioOnly: false }); this.setState({ joinedAudioOnly: false });
} }
changeExtendTime(event) { changeSetTime(event) {
const newExtendTime = Number.parseInt(event.target.value, 10) || 0; const newSetTime = Number.parseInt(event.target.value, 10) || 0;
this.setState({ extendTime: newExtendTime >= 0 ? newExtendTime : 0 }); this.setState({ newTime: newSetTime >= 0 ? newSetTime : 0 });
} }
showExtendTimeForm() { showSetTimeForm() {
this.setState({ visibleExtendTimeForm: true }); this.setState({ visibleSetTimeForm: true });
} }
showExtendTimeHigherThanMeetingTimeError(show) { showSetTimeHigherThanMeetingTimeError(show) {
this.setState({ visibleExtendTimeHigherThanMeetingTimeError: show }); this.setState({ visibleSetTimeHigherThanMeetingTimeError: show });
} }
resetExtendTimeForm() { resetSetTimeForm() {
this.setState({ visibleExtendTimeForm: false, extendTime: 5 }); this.setState({ visibleSetTimeForm: false, newTime: 5 });
} }
transferUserToBreakoutRoom(breakoutId) { transferUserToBreakoutRoom(breakoutId) {
@ -436,35 +436,35 @@ class BreakoutRoom extends PureComponent {
breakoutRooms, breakoutRooms,
amIModerator, amIModerator,
isMeteorConnected, isMeteorConnected,
extendBreakoutsTime, setBreakoutsTime,
isExtendTimeHigherThanMeetingRemaining, isNewTimeHigherThanMeetingRemaining,
} = this.props; } = this.props;
const { const {
extendTime, newTime,
visibleExtendTimeForm, visibleSetTimeForm,
visibleExtendTimeHigherThanMeetingTimeError, visibleSetTimeHigherThanMeetingTimeError,
} = this.state; } = this.state;
return ( return (
<Styled.DurationContainer> <Styled.DurationContainer>
{amIModerator && visibleExtendTimeForm ? ( {amIModerator && visibleSetTimeForm ? (
<Styled.ExtendTimeContainer> <Styled.SetTimeContainer>
<label htmlFor="inputExtendTimeSelector" > <label htmlFor="inputSetTimeSelector" >
{intl.formatMessage(intlMessages.extendTimeInMinutes)} {intl.formatMessage(intlMessages.setTimeInMinutes)}
</label> </label>
<br /> <br />
<Styled.ExtendDurationInput <Styled.SetDurationInput
id="inputExtendTimeSelector" id="inputSetTimeSelector"
type="number" type="number"
min="1" min="1"
value={extendTime} value={newTime}
onChange={this.changeExtendTime} onChange={this.changeSetTime}
aria-label={intl.formatMessage(intlMessages.extendTimeInMinutes)} aria-label={intl.formatMessage(intlMessages.setTimeInMinutes)}
/> />
<br /> <br />
<br /> <br />
{visibleExtendTimeHigherThanMeetingTimeError ? ( {visibleSetTimeHigherThanMeetingTimeError ? (
<Styled.WithError> <Styled.WithError>
{intl.formatMessage(intlMessages.extendTimeHigherThanMeetingTimeError)} {intl.formatMessage(intlMessages.setTimeHigherThanMeetingTimeError)}
<br /> <br />
<br /> <br />
</Styled.WithError> </Styled.WithError>
@ -473,42 +473,42 @@ class BreakoutRoom extends PureComponent {
color="default" color="default"
disabled={!isMeteorConnected} disabled={!isMeteorConnected}
size="sm" size="sm"
label={intl.formatMessage(intlMessages.extendTimeCancel)} label={intl.formatMessage(intlMessages.setTimeCancel)}
onClick={this.resetExtendTimeForm} onClick={this.resetSetTimeForm}
/> />
<Styled.EndButton <Styled.EndButton
color="primary" color="primary"
disabled={!isMeteorConnected} disabled={!isMeteorConnected}
size="sm" size="sm"
label={intl.formatMessage(intlMessages.extendTimeLabel)} label={intl.formatMessage(intlMessages.setTimeLabel)}
onClick={() => { onClick={() => {
this.showExtendTimeHigherThanMeetingTimeError(false); this.showSetTimeHigherThanMeetingTimeError(false);
if (isExtendTimeHigherThanMeetingRemaining(extendTime)) { if (isNewTimeHigherThanMeetingRemaining(newTime)) {
this.showExtendTimeHigherThanMeetingTimeError(true); this.showSetTimeHigherThanMeetingTimeError(true);
} else if (extendBreakoutsTime(extendTime)) { } else if (setBreakoutsTime(newTime)) {
this.resetExtendTimeForm(); this.resetSetTimeForm();
} }
}} }}
/> />
</Styled.ExtendTimeContainer> </Styled.SetTimeContainer>
) : null} ) : null}
<Styled.Duration> <Styled.Duration>
<BreakoutRoomContainer <BreakoutRoomContainer
messageDuration={intlMessages.breakoutDuration} messageDuration={intlMessages.breakoutDuration}
breakoutRoom={breakoutRooms[0]} breakoutRoom={breakoutRooms[0]}
/> />
{amIModerator && !visibleExtendTimeForm {amIModerator && !visibleSetTimeForm
? ( ? (
<Button <Button
onClick={this.showExtendTimeForm} onClick={this.showSetTimeForm}
color="default" color="default"
icon="add" icon="more"
circle circle
hideLabel hideLabel
size="sm" size="sm"
label={intl.formatMessage(intlMessages.extendTimeLabel)} label={intl.formatMessage(intlMessages.setTimeInMinutes)}
aria-label={intl.formatMessage(intlMessages.extendTimeLabel)} aria-label={intl.formatMessage(intlMessages.setTimeInMinutes)}
disabled={!isMeteorConnected} disabled={!isMeteorConnected}
/> />
) )

View File

@ -24,9 +24,9 @@ export default withTracker((props) => {
const { const {
endAllBreakouts, endAllBreakouts,
requestJoinURL, requestJoinURL,
extendBreakoutsTime, setBreakoutsTime,
sendMessageToAllBreakouts, sendMessageToAllBreakouts,
isExtendTimeHigherThanMeetingRemaining, isNewTimeHigherThanMeetingRemaining,
findBreakouts, findBreakouts,
getBreakoutRoomUrl, getBreakoutRoomUrl,
transferUserToMeeting, transferUserToMeeting,
@ -50,9 +50,9 @@ export default withTracker((props) => {
breakoutRooms, breakoutRooms,
endAllBreakouts, endAllBreakouts,
requestJoinURL, requestJoinURL,
extendBreakoutsTime, setBreakoutsTime,
sendMessageToAllBreakouts, sendMessageToAllBreakouts,
isExtendTimeHigherThanMeetingRemaining, isNewTimeHigherThanMeetingRemaining,
getBreakoutRoomUrl, getBreakoutRoomUrl,
transferUserToMeeting, transferUserToMeeting,
transferToBreakout, transferToBreakout,

View File

@ -47,7 +47,7 @@ const requestJoinURL = (breakoutId) => {
}); });
}; };
const isExtendTimeHigherThanMeetingRemaining = (extendTimeInMinutes) => { const isNewTimeHigherThanMeetingRemaining = (newTimeInMinutes) => {
const meetingId = Auth.meetingID; const meetingId = Auth.meetingID;
const meetingTimeRemaining = MeetingTimeRemaining.findOne({ meetingId }); const meetingTimeRemaining = MeetingTimeRemaining.findOne({ meetingId });
@ -55,10 +55,7 @@ const isExtendTimeHigherThanMeetingRemaining = (extendTimeInMinutes) => {
const { timeRemaining } = meetingTimeRemaining; const { timeRemaining } = meetingTimeRemaining;
if (timeRemaining) { if (timeRemaining) {
const breakoutRooms = findBreakouts(); const newBreakoutRoomsRemainingTime = newTimeInMinutes * 60;
const breakoutRoomsTimeRemaining = breakoutRooms[0].timeRemaining;
const newBreakoutRoomsRemainingTime =
breakoutRoomsTimeRemaining + extendTimeInMinutes * 60;
// Keep margin of 5 seconds for breakout rooms end before parent meeting // Keep margin of 5 seconds for breakout rooms end before parent meeting
const meetingTimeRemainingWithMargin = timeRemaining - 5; const meetingTimeRemainingWithMargin = timeRemaining - 5;
@ -71,11 +68,11 @@ const isExtendTimeHigherThanMeetingRemaining = (extendTimeInMinutes) => {
return false; return false;
}; };
const extendBreakoutsTime = (extendTimeInMinutes) => { const setBreakoutsTime = (timeInMinutes) => {
if (extendTimeInMinutes <= 0) return false; if (timeInMinutes <= 0) return false;
makeCall('extendBreakoutsTime', { makeCall('setBreakoutsTime', {
extendTimeInMinutes, timeInMinutes,
}); });
return true; return true;
@ -209,10 +206,10 @@ const isUserInBreakoutRoom = (joinedUsers) => {
export default { export default {
findBreakouts, findBreakouts,
endAllBreakouts, endAllBreakouts,
extendBreakoutsTime, setBreakoutsTime,
sendMessageToAllBreakouts, sendMessageToAllBreakouts,
getUserMessagesToAllBreakouts, getUserMessagesToAllBreakouts,
isExtendTimeHigherThanMeetingRemaining, isNewTimeHigherThanMeetingRemaining,
requestJoinURL, requestJoinURL,
getBreakoutRoomUrl, getBreakoutRoomUrl,
transferUserToMeeting, transferUserToMeeting,

View File

@ -154,13 +154,13 @@ const DurationContainer = styled.div`
text-align: center; text-align: center;
`; `;
const ExtendTimeContainer = styled.div` const SetTimeContainer = styled.div`
border-top: 1px solid ${systemMessageBorderColor}; border-top: 1px solid ${systemMessageBorderColor};
border-bottom: 1px solid ${systemMessageBorderColor}; border-bottom: 1px solid ${systemMessageBorderColor};
padding: 10px 0px; padding: 10px 0px;
`; `;
const ExtendDurationInput = styled.input` const SetDurationInput = styled.input`
width: 50%; width: 50%;
text-align: center; text-align: center;
padding: .25rem; padding: .25rem;
@ -253,8 +253,8 @@ export default {
BreakoutColumn, BreakoutColumn,
BreakoutScrollableList, BreakoutScrollableList,
DurationContainer, DurationContainer,
ExtendTimeContainer, SetTimeContainer,
ExtendDurationInput, SetDurationInput,
WithError, WithError,
EndButton, EndButton,
Duration, Duration,

View File

@ -50,7 +50,11 @@ const intlMessages = defineMessages({
[CHAT_CLEAR_MESSAGE]: { [CHAT_CLEAR_MESSAGE]: {
id: 'app.chat.clearPublicChatMessage', id: 'app.chat.clearPublicChatMessage',
description: 'message of when clear the public chat', description: 'message of when clear the public chat',
} },
breakoutDurationUpdated: {
id: 'app.chat.breakoutDurationUpdated',
description: 'used when the breakout duration is updated',
},
}); });
class TimeWindowChatItem extends PureComponent { class TimeWindowChatItem extends PureComponent {
@ -89,6 +93,7 @@ class TimeWindowChatItem extends PureComponent {
renderSystemMessage() { renderSystemMessage() {
const { const {
messages, messages,
messageValues,
chatAreaId, chatAreaId,
handleReadMessage, handleReadMessage,
messageKey, messageKey,
@ -104,13 +109,16 @@ class TimeWindowChatItem extends PureComponent {
key={`time-window-chat-item-${messageKey}`} key={`time-window-chat-item-${messageKey}`}
ref={element => this.itemRef = element} > ref={element => this.itemRef = element} >
<Styled.Messages> <Styled.Messages>
{messages.map(message => ( {messages.map((message) => (
message.text !== '' message.text !== ''
? ( ? (
<Styled.SystemMessageChatItem <Styled.SystemMessageChatItem
border={message.id} border={message.id}
key={message.id ? message.id : _.uniqueId('id-')} key={message.id ? message.id : _.uniqueId('id-')}
text={intlMessages[message.text] ? intl.formatMessage(intlMessages[message.text]) : message.text } text={intlMessages[message.text] ? intl.formatMessage(
intlMessages[message.text],
messageValues || {},
) : message.text}
time={message.time} time={message.time}
isSystemMessage={message.id ? true : false} isSystemMessage={message.id ? true : false}
systemMessageType={message.text === CHAT_CLEAR_MESSAGE ? 'chatClearMessageText' : 'chatWelcomeMessageText'} systemMessageType={message.text === CHAT_CLEAR_MESSAGE ? 'chatClearMessageText' : 'chatWelcomeMessageText'}

View File

@ -24,6 +24,7 @@ const TimeWindowChatItemContainer = (props) => {
timestamp, timestamp,
content, content,
extra, extra,
messageValues,
} = message; } = message;
const messages = content; const messages = content;
@ -49,6 +50,7 @@ const TimeWindowChatItemContainer = (props) => {
read: message.read, read: message.read,
messages, messages,
extra, extra,
messageValues,
getPollResultString: PollService.getPollResultString, getPollResultString: PollService.getPollResultString,
user, user,
timestamp, timestamp,

View File

@ -20,6 +20,7 @@
"app.chat.label": "Chat", "app.chat.label": "Chat",
"app.chat.offline": "Offline", "app.chat.offline": "Offline",
"app.chat.pollResult": "Poll Results", "app.chat.pollResult": "Poll Results",
"app.chat.breakoutDurationUpdated": "Breakout time is now {0} minutes",
"app.chat.emptyLogLabel": "Chat log empty", "app.chat.emptyLogLabel": "Chat log empty",
"app.chat.clearPublicChatMessage": "The public chat history was cleared by a moderator", "app.chat.clearPublicChatMessage": "The public chat history was cleared by a moderator",
"app.chat.multi.typing": "Multiple users are typing", "app.chat.multi.typing": "Multiple users are typing",
@ -891,10 +892,10 @@
"app.createBreakoutRoom.numberOfRoomsError": "The number of rooms is invalid.", "app.createBreakoutRoom.numberOfRoomsError": "The number of rooms is invalid.",
"app.createBreakoutRoom.duplicatedRoomNameError": "Room name can't be duplicated.", "app.createBreakoutRoom.duplicatedRoomNameError": "Room name can't be duplicated.",
"app.createBreakoutRoom.emptyRoomNameError": "Room name can't be empty.", "app.createBreakoutRoom.emptyRoomNameError": "Room name can't be empty.",
"app.createBreakoutRoom.extendTimeInMinutes": "Time to extend (minutes)", "app.createBreakoutRoom.setTimeInMinutes": "Set duration to (minutes)",
"app.createBreakoutRoom.extendTimeLabel": "Extend", "app.createBreakoutRoom.setTimeLabel": "Apply",
"app.createBreakoutRoom.extendTimeCancel": "Cancel", "app.createBreakoutRoom.setTimeCancel": "Cancel",
"app.createBreakoutRoom.extendTimeHigherThanMeetingTimeError": "The breakout rooms duration can't exceed the meeting remaining time.", "app.createBreakoutRoom.setTimeHigherThanMeetingTimeError": "The breakout rooms duration can't exceed the meeting remaining time.",
"app.createBreakoutRoom.roomNameInputDesc": "Updates breakout room name", "app.createBreakoutRoom.roomNameInputDesc": "Updates breakout room name",
"app.externalVideo.start": "Share a new video", "app.externalVideo.start": "Share a new video",
"app.externalVideo.title": "Share an external video", "app.externalVideo.title": "Share an external video",