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.
* @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.
* @param meetingId
* @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
@ -76,12 +76,12 @@ case class BreakoutRoomUsersUpdateInternalMsg(parentId: String, breakoutId: Stri
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 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.

View File

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

View File

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

View File

@ -61,7 +61,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
breakout.freeJoin,
liveMeeting.props.voiceProp.dialNumber,
breakout.voiceConf,
msg.body.durationInMinutes,
msg.body.durationInMinutes * 60,
liveMeeting.props.password.moderatorPass,
liveMeeting.props.password.viewerPass,
presId, presSlide, msg.body.record,
@ -72,7 +72,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
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))
}

View File

@ -9,7 +9,7 @@ trait SendBreakoutTimeRemainingInternalMsgHdlr {
val outGW: OutMsgRouter
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)
}
}

View File

@ -1,20 +1,20 @@
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.running.{ MeetingActor, OutMsgRouter }
trait ExtendBreakoutRoomTimeInternalMsgHdlr {
trait UpdateBreakoutRoomTimeInternalMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleExtendBreakoutRoomTimeInternalMsgHdlr(msg: ExtendBreakoutRoomTimeInternalMsg, state: MeetingState2x): MeetingState2x = {
def handleUpdateBreakoutRoomTimeInternalMsgHdlr(msg: UpdateBreakoutRoomTimeInternalMsg, state: MeetingState2x): MeetingState2x = {
val breakoutModel = for {
model <- state.breakout
} yield {
val updatedBreakoutModel = model.extendTime(msg.extendTimeInMinutes)
val updatedBreakoutModel = model.setTime(msg.durationInSeconds)
updatedBreakoutModel
}

View File

@ -1,37 +1,38 @@
package org.bigbluebutton.core.apps.breakout
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.bus.BigBlueButtonEvent
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil
trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
trait UpdateBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
this: MeetingActor =>
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)) {
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)
state
} else if (msg.body.extendTimeInMinutes <= 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)
} else if (msg.body.timeInMinutes <= 0) {
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
} else {
val updatedModel = for {
breakoutModel <- state.breakout
startedOn <- breakoutModel.startedOn
} yield {
val breakoutRoomEndTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(breakoutModel.durationInMinutes)
val breakoutRoomSecsRemaining = breakoutRoomEndTime - TimeUtil.millisToSeconds(System.currentTimeMillis())
val newSecsRemaining = msg.body.timeInMinutes * 60
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) {
val mainRoomEndTime = state.expiryTracker.startedOnInMs + state.expiryTracker.durationInMs
@ -40,28 +41,28 @@ trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait {
//Avoid breakout room end later than main room
//Keep 5 seconds of margin to finish breakout room and send informations to parent meeting
if (breakoutRoomSecsRemaining + TimeUtil.minutesToSeconds(msg.body.extendTimeInMinutes) > (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)
isExtendTimeHigherThanMeetingRemaining = true
if (newSecsRemaining > (mainRoomSecsRemaining - 5)) {
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)
isNewTimeHigherThanMeetingRemaining = true
}
}
if (isExtendTimeHigherThanMeetingRemaining) {
if (isNewTimeHigherThanMeetingRemaining) {
breakoutModel
} else {
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)
breakoutModel.extendTime(msg.body.extendTimeInMinutes)
log.debug("Updating {} minutes for breakout rooms time in meeting {}", msg.body.timeInMinutes, props.meetingProp.intId)
breakoutModel.setTime(newDurationInSeconds)
}
}
val event = buildExtendBreakoutRoomsTimeEvtMsg(msg.body.extendTimeInMinutes)
val event = buildUpdateBreakoutRoomsTimeEvtMsg(msg.body.timeInMinutes)
outGW.send(event)
//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 {
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 envelope = BbbCoreEnvelope(ExtendBreakoutRoomsTimeEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(ExtendBreakoutRoomsTimeEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
val envelope = BbbCoreEnvelope(UpdateBreakoutRoomsTimeEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UpdateBreakoutRoomsTimeEvtMsg.NAME, liveMeeting.props.meetingProp.intId, "not-used")
val body = ExtendBreakoutRoomsTimeEvtMsgBody(props.meetingProp.intId, extendTimeInMinutes)
val event = ExtendBreakoutRoomsTimeEvtMsg(header, body)
val body = UpdateBreakoutRoomsTimeEvtMsgBody(props.meetingProp.intId, timeInMinutes)
val event = UpdateBreakoutRoomsTimeEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}

View File

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

View File

@ -283,7 +283,7 @@ class MeetingActor(
case msg: SendBreakoutUsersAuditInternalMsg => handleSendBreakoutUsersUpdateInternalMsg(msg)
case msg: BreakoutRoomUsersUpdateInternalMsg => state = handleBreakoutRoomUsersUpdateInternalMsg(msg, state)
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: SendMessageToBreakoutRoomInternalMsg => state = handleSendMessageToBreakoutRoomInternalMsg(msg, state, liveMeeting, msgBus)
case msg: SendBreakoutTimeRemainingInternalMsg => handleSendBreakoutTimeRemainingInternalMsg(msg)
@ -454,7 +454,7 @@ class MeetingActor(
case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state)
case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(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)
// Voice

View File

@ -73,7 +73,7 @@ class MeetingActorAudit(
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, MonitorNumberOfUsersInternalMsg(props.meetingProp.intId)))
// 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) {
// 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: EndAllBreakoutRoomsMsg => logMessage(msg)
case m: TransferUserToMeetingRequestMsg => logMessage(msg)
case m: ExtendBreakoutRoomsTimeReqMsg => logMessage(msg)
case m: UpdateBreakoutRoomsTimeReqMsg => logMessage(msg)
case m: SendMessageToAllBreakoutRoomsReqMsg => logMessage(msg)
case m: UserLeftVoiceConfToClientEvtMsg => logMessage(msg)
case m: UserLeftVoiceConfEvtMsg => logMessage(msg)

View File

@ -18,19 +18,19 @@ trait SendBreakoutTimeRemainingMsgHdlr {
model <- state.breakout
startedOn <- model.startedOn
} yield {
val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(model.durationInMinutes)
val endMeetingTime = TimeUtil.millisToSeconds(startedOn) + model.durationInSeconds
val timeRemaining = endMeetingTime - TimeUtil.millisToSeconds(System.currentTimeMillis())
if (!liveMeeting.props.meetingProp.isBreakout) {
// 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)
}
// Tell all breakout rooms of time remaining so they can notify their users.
// This syncs all rooms about time remaining.
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) {
@ -41,7 +41,7 @@ trait SendBreakoutTimeRemainingMsgHdlr {
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 envelope = BbbCoreEnvelope(BreakoutRoomsTimeRemainingUpdateEvtMsg.NAME, routing)
val body = BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeLeftInSec)

View File

@ -545,10 +545,10 @@ object MsgBuilder {
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 envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)
val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec)
val body = MeetingTimeRemainingUpdateEvtMsgBody(timeLeftInSec, timeUpdatedInMinutes)
val header = BbbClientMsgHeader(MeetingTimeRemainingUpdateEvtMsg.NAME, meetingId, "not-used")
val event = MeetingTimeRemainingUpdateEvtMsg(header, body)

View File

@ -31,7 +31,7 @@ case class BreakoutRoomsTimeRemainingUpdateEvtMsg(
header: BbbClientMsgHeader,
body: BreakoutRoomsTimeRemainingUpdateEvtMsgBody
) extends BbbCoreMsg
case class BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeRemaining: Long)
case class BreakoutRoomsTimeRemainingUpdateEvtMsgBody(timeRemaining: Long, timeUpdatedInMinutes: Int)
/**
* 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 UpdateBreakoutUsersEvtMsgBody(parentId: String, breakoutId: String, users: Vector[BreakoutUserVO])
object ExtendBreakoutRoomsTimeReqMsg { val NAME = "ExtendBreakoutRoomsTimeReqMsg" }
case class ExtendBreakoutRoomsTimeReqMsg(header: BbbClientMsgHeader, body: ExtendBreakoutRoomsTimeReqMsgBody) extends StandardMsg
case class ExtendBreakoutRoomsTimeReqMsgBody(meetingId: String, extendTimeInMinutes: Int)
object UpdateBreakoutRoomsTimeReqMsg { val NAME = "UpdateBreakoutRoomsTimeReqMsg" }
case class UpdateBreakoutRoomsTimeReqMsg(header: BbbClientMsgHeader, body: UpdateBreakoutRoomsTimeReqMsgBody) extends StandardMsg
case class UpdateBreakoutRoomsTimeReqMsgBody(meetingId: String, timeInMinutes: Int)
object ExtendBreakoutRoomsTimeEvtMsg { val NAME = "ExtendBreakoutRoomsTimeEvtMsg" }
case class ExtendBreakoutRoomsTimeEvtMsg(header: BbbClientMsgHeader, body: ExtendBreakoutRoomsTimeEvtMsgBody) extends BbbCoreMsg
case class ExtendBreakoutRoomsTimeEvtMsgBody(meetingId: String, extendTimeInMinutes: Int)
object UpdateBreakoutRoomsTimeEvtMsg { val NAME = "UpdateBreakoutRoomsTimeEvtMsg" }
case class UpdateBreakoutRoomsTimeEvtMsg(header: BbbClientMsgHeader, body: UpdateBreakoutRoomsTimeEvtMsgBody) extends BbbCoreMsg
case class UpdateBreakoutRoomsTimeEvtMsgBody(meetingId: String, timeInMinutes: Int)
object SendMessageToAllBreakoutRoomsReqMsg { val NAME = "SendMessageToAllBreakoutRoomsReqMsg" }
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 requestJoinURL from './methods/requestJoinURL';
import endAllBreakouts from './methods/endAllBreakouts';
import extendBreakoutsTime from './methods/extendBreakoutsTime';
import setBreakoutsTime from '/imports/api/breakouts/server/methods/setBreakoutsTime';
import sendMessageToAllBreakouts from './methods/sendMessageToAllBreakouts';
Meteor.methods({
requestJoinURL,
createBreakoutRoom,
endAllBreakouts,
extendBreakoutsTime,
setBreakoutsTime,
sendMessageToAllBreakouts,
});

View File

@ -4,10 +4,10 @@ import { extractCredentials } from '/imports/api/common/server/helpers';
import { check } from 'meteor/check';
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 CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'ExtendBreakoutRoomsTimeReqMsg';
const EVENT_NAME = 'UpdateBreakoutRoomsTimeReqMsg';
try {
const { meetingId, requesterUserId } = extractCredentials(this.userId);
@ -15,14 +15,14 @@ export default function extendBreakoutsTime({ extendTimeInMinutes }) {
check(meetingId, String);
check(requesterUserId, String);
return RedisPubSub.publishUserMessage(
RedisPubSub.publishUserMessage(
CHANNEL, EVENT_NAME, meetingId, requesterUserId,
{
meetingId,
extendTimeInMinutes,
timeInMinutes,
},
);
} 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,
sender: Object,
message: String,
extra: Object,
messageValues: Match.Maybe(Object),
extra: Match.Maybe(Object),
correlationId: Match.Maybe(String),
});
const msgDocument = {

View File

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

View File

@ -16,7 +16,7 @@ const BreakoutContainer = (props) => {
return <BreakoutComponent
amIPresenter={amIPresenter}
{...{ layoutContextDispatch, ...props }}
{...{ layoutContextDispatch, ...props }}
/>;
};
@ -24,9 +24,9 @@ export default withTracker((props) => {
const {
endAllBreakouts,
requestJoinURL,
extendBreakoutsTime,
setBreakoutsTime,
sendMessageToAllBreakouts,
isExtendTimeHigherThanMeetingRemaining,
isNewTimeHigherThanMeetingRemaining,
findBreakouts,
getBreakoutRoomUrl,
transferUserToMeeting,
@ -50,9 +50,9 @@ export default withTracker((props) => {
breakoutRooms,
endAllBreakouts,
requestJoinURL,
extendBreakoutsTime,
setBreakoutsTime,
sendMessageToAllBreakouts,
isExtendTimeHigherThanMeetingRemaining,
isNewTimeHigherThanMeetingRemaining,
getBreakoutRoomUrl,
transferUserToMeeting,
transferToBreakout,

View File

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

View File

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

View File

@ -50,7 +50,11 @@ const intlMessages = defineMessages({
[CHAT_CLEAR_MESSAGE]: {
id: 'app.chat.clearPublicChatMessage',
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 {
@ -89,6 +93,7 @@ class TimeWindowChatItem extends PureComponent {
renderSystemMessage() {
const {
messages,
messageValues,
chatAreaId,
handleReadMessage,
messageKey,
@ -104,13 +109,16 @@ class TimeWindowChatItem extends PureComponent {
key={`time-window-chat-item-${messageKey}`}
ref={element => this.itemRef = element} >
<Styled.Messages>
{messages.map(message => (
{messages.map((message) => (
message.text !== ''
? (
<Styled.SystemMessageChatItem
border={message.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}
isSystemMessage={message.id ? true : false}
systemMessageType={message.text === CHAT_CLEAR_MESSAGE ? 'chatClearMessageText' : 'chatWelcomeMessageText'}
@ -283,4 +291,4 @@ class TimeWindowChatItem extends PureComponent {
TimeWindowChatItem.propTypes = propTypes;
TimeWindowChatItem.defaultProps = defaultProps;
export default injectIntl(TimeWindowChatItem);
export default injectIntl(TimeWindowChatItem);

View File

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

View File

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