diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx index 5b8ca67b475375a945bdb8a52a9f7c9a2434c54c..8711dfa9b9074080249d236c3cfc58a3f3bffdc2 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/component.jsx @@ -111,11 +111,17 @@ const intlMessages = defineMessages({ id: 'app.userList.you', description: 'Text for identifying your user', }, + minimumDurationWarnBreakout: { + id: 'app.createBreakoutRoom.minimumDurationWarnBreakout', + description: 'minimum duration warning message label', + }, + }); const BREAKOUT_LIM = Meteor.settings.public.app.breakouts.breakoutRoomLimit; const MIN_BREAKOUT_ROOMS = 2; const MAX_BREAKOUT_ROOMS = BREAKOUT_LIM > MIN_BREAKOUT_ROOMS ? BREAKOUT_LIM : MIN_BREAKOUT_ROOMS; +const MIN_BREAKOUT_TIME = 5; const propTypes = { intl: PropTypes.shape({ @@ -177,6 +183,7 @@ class BreakoutRoom extends PureComponent { valid: true, record: false, numberOfRoomsIsValid: true, + durationIsValid: true, breakoutJoinedUsers: null, }; @@ -307,19 +314,26 @@ class BreakoutRoom extends PureComponent { freeJoin, record, numberOfRoomsIsValid, + durationIsValid, } = this.state; + const { numberOfRooms, durationTime } = this.state; + + if ((durationTime || 0) < MIN_BREAKOUT_TIME) { + this.setState({ durationIsValid: false }); + return; + } + if (users.length === this.getUserByRoom(0).length && !freeJoin) { this.setState({ valid: false }); return; } - if (!numberOfRoomsIsValid) { + if (!numberOfRoomsIsValid || !durationIsValid) { return; } this.setState({ preventClosing: false }); - const { numberOfRooms, durationTime } = this.state; const rooms = _.range(1, numberOfRooms + 1).map((value) => ({ users: this.getUserByRoom(value).map((u) => u.userId), name: intl.formatMessage(intlMessages.roomName, { @@ -466,17 +480,25 @@ class BreakoutRoom extends PureComponent { increaseDurationTime() { const { durationTime } = this.state; - this.setState({ durationTime: (1 * durationTime) + 1 }); + const durationIsValid = durationTime >= MIN_BREAKOUT_TIME; + + this.setState({ durationTime: (1 * durationTime) + 1, durationIsValid }); } decreaseDurationTime() { const { durationTime } = this.state; const number = ((1 * durationTime) - 1); - this.setState({ durationTime: number < 1 ? 1 : number }); + const newDurationTime = number < MIN_BREAKOUT_TIME ? MIN_BREAKOUT_TIME : number; + const durationIsValid = durationTime >= MIN_BREAKOUT_TIME; + + this.setState({ durationTime: newDurationTime, durationIsValid }); } changeDurationTime(event) { - this.setState({ durationTime: Number.parseInt(event.target.value, 10) || '' }); + const durationTime = Number.parseInt(event.target.value, 10) || ''; + const durationIsValid = durationTime >= MIN_BREAKOUT_TIME; + + this.setState({ durationTime, durationIsValid }); } blurDurationTime(event) { @@ -489,7 +511,7 @@ class BreakoutRoom extends PureComponent { this.setState({ numberOfRooms, numberOfRoomsIsValid: numberOfRooms <= MAX_BREAKOUT_ROOMS - && numberOfRooms >= MIN_BREAKOUT_ROOMS, + && numberOfRooms >= MIN_BREAKOUT_ROOMS, }); } @@ -552,6 +574,7 @@ class BreakoutRoom extends PureComponent { numberOfRooms, durationTime, numberOfRoomsIsValid, + durationIsValid, } = this.state; if (isInvitation) return null; @@ -580,7 +603,7 @@ class BreakoutRoom extends PureComponent { } </select> </div> - <label htmlFor="breakoutRoomTime"> + <label htmlFor="breakoutRoomTime" className={!durationIsValid ? styles.changeToWarn : null}> <p className={styles.labelText} aria-hidden> {intl.formatMessage(intlMessages.duration)} </p> @@ -607,7 +630,7 @@ class BreakoutRoom extends PureComponent { `${intl.formatMessage(intlMessages.minusRoomTime)} ${intl.formatMessage(intlMessages.roomTime, { 0: durationTime - 1 })}` } icon="substract" - onClick={() => {}} + onClick={() => { }} hideLabel circle size="sm" @@ -624,13 +647,17 @@ class BreakoutRoom extends PureComponent { `${intl.formatMessage(intlMessages.addRoomTime)} ${intl.formatMessage(intlMessages.roomTime, { 0: durationTime + 1 })}` } icon="add" - onClick={() => {}} + onClick={() => { }} hideLabel circle size="sm" /> </HoldButton> </div> + <span className={durationIsValid ? styles.dontShow : styles.leastOneWarn}> + {intl.formatMessage(intlMessages.minimumDurationWarnBreakout, { 0: MIN_BREAKOUT_TIME })} + </span> + </label> <Button data-test="randomlyAssign" @@ -796,7 +823,7 @@ class BreakoutRoom extends PureComponent { )) } </span> - { isInvitation || this.renderButtonSetLevel(1, intl.formatMessage(intlMessages.backLabel))} + {isInvitation || this.renderButtonSetLevel(1, intl.formatMessage(intlMessages.backLabel))} </div> ); } @@ -813,15 +840,15 @@ class BreakoutRoom extends PureComponent { <> {!valid && ( - <span className={styles.withError}> - {intl.formatMessage(intlMessages.leastOneWarnBreakout)} - </span> + <span className={styles.withError}> + {intl.formatMessage(intlMessages.leastOneWarnBreakout)} + </span> )} {!numberOfRoomsIsValid && ( - <span className={styles.withError}> - {intl.formatMessage(intlMessages.numberOfRoomsIsValid)} - </span> + <span className={styles.withError}> + {intl.formatMessage(intlMessages.numberOfRoomsIsValid)} + </span> )} </> ); @@ -887,6 +914,7 @@ class BreakoutRoom extends PureComponent { preventClosing, valid, numberOfRoomsIsValid, + durationIsValid, } = this.state; const { isMobile } = deviceInfo; @@ -904,7 +932,7 @@ class BreakoutRoom extends PureComponent { ? intl.formatMessage(intlMessages.invitationConfirm) : intl.formatMessage(intlMessages.confirmButton), callback: isInvitation ? this.onInviteBreakout : this.onCreateBreakouts, - disabled: !valid || !numberOfRoomsIsValid, + disabled: !valid || !numberOfRoomsIsValid || !durationIsValid, } } dismiss={{ diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.scss b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.scss index 65d2f3cdc3346cff79c7d4115c5abfc9cc9fd793..de91d30108879ed1e546d0c51738e0da4636e8b6 100644 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.scss @@ -134,6 +134,14 @@ input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-i & > .freeJoinLabel { color: var(--color-danger); } + + & > .labelText { + color: var(--color-danger); + } + + & .duration { + border-color: var(--color-danger) !important; + } } .breakoutBox { @@ -333,7 +341,7 @@ input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-i .checkBoxesContainer { @extend %flex-row; - margin-top: 1rem; + margin-top: 2rem; } .withError { diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json index 77de7bd03fcc0f460bbc3f53257f4a2202b4597d..ea3770d7f80943229dce805fd4f59d712beb498f 100755 --- a/bigbluebutton-html5/public/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -788,6 +788,7 @@ "app.createBreakoutRoom.addParticipantLabel": "+ Add participant", "app.createBreakoutRoom.freeJoin": "Allow users to choose a breakout room to join", "app.createBreakoutRoom.leastOneWarnBreakout": "You must place at least one user in a breakout room.", + "app.createBreakoutRoom.minimumDurationWarnBreakout": "Minimum duration for a breakout room is {0} minutes.", "app.createBreakoutRoom.modalDesc": "Tip: You can drag-and-drop a user's name to assign them to a specific breakout room.", "app.createBreakoutRoom.roomTime": "{0} minutes", "app.createBreakoutRoom.numberOfRoomsError": "The number of rooms is invalid.",