diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/ExtendBreakoutRoomsTimeMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/ExtendBreakoutRoomsTimeMsgHdlr.scala index 667f926a3b..7b40411f0e 100644 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/ExtendBreakoutRoomsTimeMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/breakout/ExtendBreakoutRoomsTimeMsgHdlr.scala @@ -4,8 +4,9 @@ import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.api.{ ExtendBreakoutRoomTimeInternalMsg, SendTimeRemainingAuditInternalMsg } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } 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.util.TimeUtil trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait { this: MeetingActor => @@ -25,12 +26,35 @@ trait ExtendBreakoutRoomsTimeMsgHdlr extends RightsManagementTrait { } else { val updatedModel = for { breakoutModel <- state.breakout + startedOn <- breakoutModel.startedOn } yield { - breakoutModel.rooms.values.foreach { room => - eventBus.publish(BigBlueButtonEvent(room.id, ExtendBreakoutRoomTimeInternalMsg(props.breakoutProps.parentId, room.id, msg.body.extendTimeInMinutes))) + val breakoutRoomEndTime = TimeUtil.millisToSeconds(startedOn) + TimeUtil.minutesToSeconds(breakoutModel.durationInMinutes) + val breakoutRoomSecsRemaining = breakoutRoomEndTime - TimeUtil.millisToSeconds(System.currentTimeMillis()) + + var isExtendTimeHigherThanMeetingRemaining = false + + if (state.expiryTracker.durationInMs > 0) { + val mainRoomEndTime = state.expiryTracker.startedOnInMs + state.expiryTracker.durationInMs + val mainRoomSecsRemaining = TimeUtil.millisToSeconds(mainRoomEndTime - TimeUtil.timeNowInMs()) + val mainRoomTimeRemainingInMinutes = mainRoomSecsRemaining / 60 + + //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 (isExtendTimeHigherThanMeetingRemaining) { + breakoutModel + } else { + breakoutModel.rooms.values.foreach { room => + eventBus.publish(BigBlueButtonEvent(room.id, ExtendBreakoutRoomTimeInternalMsg(props.breakoutProps.parentId, room.id, msg.body.extendTimeInMinutes))) + } + log.debug("Extending {} minutes for breakout rooms time in meeting {}", msg.body.extendTimeInMinutes, props.meetingProp.intId) + breakoutModel.extendTime(msg.body.extendTimeInMinutes) } - log.debug("Extending {} minutes for breakout rooms time in meeting {}", msg.body.extendTimeInMinutes, props.meetingProp.intId) - breakoutModel.extendTime(msg.body.extendTimeInMinutes) } val event = buildExtendBreakoutRoomsTimeEvtMsg(msg.body.extendTimeInMinutes) diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx index 2ce8a002e8..a9cb36f2e9 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx @@ -71,6 +71,10 @@ const intlMessages = defineMessages({ id: 'app.createBreakoutRoom.extendTimeCancel', description: 'Button label to cancel extend 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', + }, }); class BreakoutRoom extends PureComponent { @@ -103,6 +107,7 @@ class BreakoutRoom extends PureComponent { joinedAudioOnly: false, breakoutId: '', visibleExtendTimeForm: false, + visibleExtendTimeHigherThanMeetingTimeError: false, extendTime: 5, }; } @@ -182,6 +187,10 @@ class BreakoutRoom extends PureComponent { this.setState({ visibleExtendTimeForm: true }); } + showExtendTimeHigherThanMeetingTimeError(show) { + this.setState({ visibleExtendTimeHigherThanMeetingTimeError: show }); + } + resetExtendTimeForm() { this.setState({ visibleExtendTimeForm: false, extendTime: 5 }); } @@ -366,9 +375,9 @@ class BreakoutRoom extends PureComponent { renderDuration() { const { - intl, breakoutRooms, amIModerator, isMeteorConnected, extendBreakoutsTime, + intl, breakoutRooms, amIModerator, isMeteorConnected, extendBreakoutsTime, isExtendTimeHigherThanMeetingRemaining, } = this.props; - const { extendTime, visibleExtendTimeForm } = this.state; + const { extendTime, visibleExtendTimeForm, visibleExtendTimeHigherThanMeetingTimeError } = this.state; return (
{ amIModerator && visibleExtendTimeForm ? ( @@ -391,6 +400,13 @@ class BreakoutRoom extends PureComponent { />

+ { visibleExtendTimeHigherThanMeetingTimeError ? ( + + {intl.formatMessage(intlMessages.extendTimeHigherThanMeetingTimeError)} +
+
+
+ ) : null }
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx index 96c6b925db..f2fac782ff 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx @@ -18,6 +18,7 @@ export default withTracker((props) => { endAllBreakouts, requestJoinURL, extendBreakoutsTime, + isExtendTimeHigherThanMeetingRemaining, findBreakouts, breakoutRoomUser, transferUserToMeeting, @@ -42,6 +43,7 @@ export default withTracker((props) => { endAllBreakouts, requestJoinURL, extendBreakoutsTime, + isExtendTimeHigherThanMeetingRemaining, breakoutRoomUser, transferUserToMeeting, transferToBreakout, diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js index 35e8041ce6..c873247fe3 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js @@ -1,5 +1,5 @@ import Breakouts from '/imports/api/breakouts'; -import Meetings from '/imports/api/meetings'; +import Meetings, { MeetingTimeRemaining } from '/imports/api/meetings'; import { makeCall } from '/imports/ui/services/api'; import Auth from '/imports/ui/services/auth'; import { Session } from 'meteor/session'; @@ -38,11 +38,37 @@ const requestJoinURL = (breakoutId) => { }); }; +const isExtendTimeHigherThanMeetingRemaining = (extendTimeInMinutes) => { + const meetingId = Auth.meetingID; + const meetingTimeRemaining = MeetingTimeRemaining.findOne({ meetingId }); + + if (meetingTimeRemaining) { + const { timeRemaining } = meetingTimeRemaining; + + if (timeRemaining) { + const breakoutRooms = findBreakouts(); + const breakoutRoomsTimeRemaining = (breakoutRooms[0]).timeRemaining; + const newBreakoutRoomsRemainingTime = breakoutRoomsTimeRemaining + (extendTimeInMinutes * 60); + // Keep margin of 5 seconds for breakout rooms end before parent meeting + const meetingTimeRemainingWithMargin = timeRemaining - 5; + + if (newBreakoutRoomsRemainingTime > meetingTimeRemainingWithMargin) { + return true; + } + } + } + + return false; +}; + const extendBreakoutsTime = (extendTimeInMinutes) => { - if (extendTimeInMinutes <= 0) return; + if (extendTimeInMinutes <= 0) return false; + makeCall('extendBreakoutsTime', { extendTimeInMinutes, }); + + return true; }; const transferUserToMeeting = (fromMeetingId, toMeetingId) => makeCall('transferUser', fromMeetingId, toMeetingId); @@ -116,6 +142,7 @@ export default { findBreakouts, endAllBreakouts, extendBreakoutsTime, + isExtendTimeHigherThanMeetingRemaining, requestJoinURL, breakoutRoomUser, transferUserToMeeting, diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/styles.scss b/bigbluebutton-html5/imports/ui/components/breakout-room/styles.scss index f47483ca18..4cf9fc0e4a 100644 --- a/bigbluebutton-html5/imports/ui/components/breakout-room/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/breakout-room/styles.scss @@ -163,6 +163,10 @@ margin: .5rem 0 .5rem 0; } +.withError { + color: var(--color-danger); +} + .usersAssignedNumberLabel { margin: 0 0 0 .25rem; diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json index a6ae121ff1..25527eeb84 100755 --- a/bigbluebutton-html5/public/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -794,6 +794,7 @@ "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.externalVideo.start": "Share a new video", "app.externalVideo.title": "Share an external video", "app.externalVideo.input": "External Video URL",