Merge pull request #20734 from danielpetri1/pass-breakout-pres

feat: Pass a different presentation to each breakout
This commit is contained in:
Anton Georgiev 2024-07-23 11:52:07 -04:00 committed by GitHub
commit b248143cae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 89 additions and 9 deletions

View File

@ -18,8 +18,11 @@ object BreakoutModel {
captureSlides: Boolean, captureSlides: Boolean,
captureNotesFilename: String, captureNotesFilename: String,
captureSlidesFilename: String, captureSlidesFilename: String,
allPages: Boolean,
presId: String,
): BreakoutRoom2x = { ): BreakoutRoom2x = {
new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false, captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename) new BreakoutRoom2x(id, externalId, name, parentId, sequence, shortName, isDefaultName, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false,
captureNotes, captureSlides, captureNotesFilename, captureSlidesFilename, allPages, presId)
} }
} }

View File

@ -40,26 +40,30 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
} }
def processRequest(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = { def processRequest(msg: CreateBreakoutRoomsCmdMsg, state: MeetingState2x): MeetingState2x = {
val presId = getPresentationId(state) // The current presentation
val presId = getPresentationId(state) val presSlide = getPresentationSlide(state) // The current slide
val presSlide = getPresentationSlide(state)
val parentId = liveMeeting.props.meetingProp.intId val parentId = liveMeeting.props.meetingProp.intId
var rooms = new collection.immutable.HashMap[String, BreakoutRoom2x] var rooms = new collection.immutable.HashMap[String, BreakoutRoom2x]
var i = 0 var i = 0
for (room <- msg.body.rooms) { for (room <- msg.body.rooms) {
val roomPresId = if (room.presId.isEmpty) presId else room.presId;
i += 1 i += 1
val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i) val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i)
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i) val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName, val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.shortName,
room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes, room.isDefaultName, room.freeJoin, voiceConf, room.users, msg.body.captureNotes,
msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename) msg.body.captureSlides, room.captureNotesFilename, room.captureSlidesFilename,
room.allPages, roomPresId)
rooms = rooms + (breakout.id -> breakout) rooms = rooms + (breakout.id -> breakout)
} }
for (breakout <- rooms.values.toVector) { for (breakout <- rooms.values.toVector) {
val roomSlides = if (breakout.allPages) -1 else presSlide;
val roomDetail = new BreakoutRoomDetail( val roomDetail = new BreakoutRoomDetail(
breakout.id, breakout.name, breakout.id, breakout.name,
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
@ -72,7 +76,9 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
msg.body.durationInMinutes * 60, 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, breakout.presId,
roomSlides,
msg.body.record,
liveMeeting.props.breakoutProps.privateChatEnabled, liveMeeting.props.breakoutProps.privateChatEnabled,
breakout.captureNotes, breakout.captureNotes,
breakout.captureSlides, breakout.captureSlides,

View File

@ -19,6 +19,8 @@ case class BreakoutRoom2x(
captureSlides: Boolean, captureSlides: Boolean,
captureNotesFilename: String, captureNotesFilename: String,
captureSlidesFilename: String, captureSlidesFilename: String,
allPages: Boolean,
presId: String,
) { ) {
} }

View File

@ -71,7 +71,7 @@ case class BreakoutRoomDetail(
object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" } object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" }
case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg
case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, captureNotes: Boolean, captureSlides: Boolean, rooms: Vector[BreakoutRoomMsgBody], sendInviteToModerators: Boolean) case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, captureNotes: Boolean, captureSlides: Boolean, rooms: Vector[BreakoutRoomMsgBody], sendInviteToModerators: Boolean)
case class BreakoutRoomMsgBody(name: String, sequence: Int, shortName: String, captureNotesFilename: String, captureSlidesFilename: String, isDefaultName: Boolean, freeJoin: Boolean, users: Vector[String]) case class BreakoutRoomMsgBody(name: String, sequence: Int, shortName: String, captureNotesFilename: String, captureSlidesFilename: String, isDefaultName: Boolean, freeJoin: Boolean, users: Vector[String], allPages: Boolean, presId: String)
// Sent by user to request ending all the breakout rooms // Sent by user to request ending all the breakout rooms
object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" } object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" }

View File

@ -94,6 +94,7 @@ public class PresentationUrlDownloadService {
}, 5, TimeUnit.SECONDS); }, 5, TimeUnit.SECONDS);
} }
// A negative presentationSlide indicates the entire presentation deck should be used.
private void extractPage(final String sourceMeetingId, final String presentationId, private void extractPage(final String sourceMeetingId, final String presentationId,
final Integer presentationSlide, final String destinationMeetingId) { final Integer presentationSlide, final String destinationMeetingId) {
@ -146,7 +147,7 @@ public class PresentationUrlDownloadService {
+ newFilename; + newFilename;
File newPresentation = new File(newFilePath); File newPresentation = new File(newFilePath);
if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf")) { if (sourcePresentationFile.getName().toLowerCase().endsWith("pdf") && presentationSlide >= 0) {
pageExtractor.extractPage(sourcePresentationFile, new File( pageExtractor.extractPage(sourcePresentationFile, new File(
newFilePath), presentationSlide); newFilePath), presentationSlide);
} else { } else {

View File

@ -181,12 +181,17 @@ const intlMessages = defineMessages({
id: 'app.createBreakoutRoom.sendInvitationToMods', id: 'app.createBreakoutRoom.sendInvitationToMods',
description: 'label for checkbox send invitation to moderators', description: 'label for checkbox send invitation to moderators',
}, },
currentSlide: {
id: 'app.createBreakoutRoom.currentSlideLabel',
description: 'label for current slide',
},
}); });
const BREAKOUT_LIM = Meteor.settings.public.app.breakouts.breakoutRoomLimit; const BREAKOUT_LIM = Meteor.settings.public.app.breakouts.breakoutRoomLimit;
const MIN_BREAKOUT_ROOMS = 2; const MIN_BREAKOUT_ROOMS = 2;
const MAX_BREAKOUT_ROOMS = BREAKOUT_LIM > MIN_BREAKOUT_ROOMS ? BREAKOUT_LIM : MIN_BREAKOUT_ROOMS; const MAX_BREAKOUT_ROOMS = BREAKOUT_LIM > MIN_BREAKOUT_ROOMS ? BREAKOUT_LIM : MIN_BREAKOUT_ROOMS;
const MIN_BREAKOUT_TIME = 5; const MIN_BREAKOUT_TIME = 5;
const CURRENT_SLIDE_PREFIX = 'current-';
const propTypes = { const propTypes = {
intl: PropTypes.shape({ intl: PropTypes.shape({
@ -244,6 +249,7 @@ class BreakoutRoom extends PureComponent {
this.removeRoomUsers = this.removeRoomUsers.bind(this); this.removeRoomUsers = this.removeRoomUsers.bind(this);
this.renderErrorMessages = this.renderErrorMessages.bind(this); this.renderErrorMessages = this.renderErrorMessages.bind(this);
this.onUpdateBreakouts = this.onUpdateBreakouts.bind(this); this.onUpdateBreakouts = this.onUpdateBreakouts.bind(this);
this.getRoomPresentation = this.getRoomPresentation.bind(this);
this.state = { this.state = {
numberOfRooms: MIN_BREAKOUT_ROOMS, numberOfRooms: MIN_BREAKOUT_ROOMS,
@ -266,6 +272,7 @@ class BreakoutRoom extends PureComponent {
durationIsValid: true, durationIsValid: true,
breakoutJoinedUsers: null, breakoutJoinedUsers: null,
enableUnassingUsers: null, enableUnassingUsers: null,
roomPresentations: [],
}; };
this.btnLevelId = uniqueId('btn-set-level-'); this.btnLevelId = uniqueId('btn-set-level-');
@ -503,6 +510,8 @@ class BreakoutRoom extends PureComponent {
isDefaultName: !this.hasNameChanged(seq), isDefaultName: !this.hasNameChanged(seq),
freeJoin, freeJoin,
sequence: seq, sequence: seq,
allPages: !this.getRoomPresentation(seq).startsWith(CURRENT_SLIDE_PREFIX),
presId: this.getRoomPresentation(seq).replace(CURRENT_SLIDE_PREFIX, ''),
})); }));
createBreakoutRoom(rooms, durationTime, record, captureNotes, captureSlides, inviteMods); createBreakoutRoom(rooms, durationTime, record, captureNotes, captureSlides, inviteMods);
@ -688,6 +697,15 @@ class BreakoutRoom extends PureComponent {
}); });
} }
getRoomPresentation(position) {
const { roomPresentations } = this.state;
const { presentations } = this.props;
const currentPresentation = presentations.find((presentation) => presentation.current);
return roomPresentations[position] || `${CURRENT_SLIDE_PREFIX}${currentPresentation?.id}`;
}
getFullName(position) { getFullName(position) {
const { meetingName } = this.props; const { meetingName } = this.props;
@ -909,11 +927,12 @@ class BreakoutRoom extends PureComponent {
} }
renderRoomsGrid() { renderRoomsGrid() {
const { intl, isUpdate } = this.props; const { intl, isUpdate, presentations } = this.props;
const { const {
leastOneUserIsValid, leastOneUserIsValid,
numberOfRooms, numberOfRooms,
roomNamesChanged, roomNamesChanged,
roomPresentations,
} = this.state; } = this.state;
const rooms = (numberOfRooms > MAX_BREAKOUT_ROOMS const rooms = (numberOfRooms > MAX_BREAKOUT_ROOMS
@ -941,6 +960,17 @@ class BreakoutRoom extends PureComponent {
}); });
}; };
const changeRoomPresentation = (position) => (ev) => {
const newRoomsPresentations = [...roomPresentations];
newRoomsPresentations[position] = ev.target.value;
this.setState({
roomPresentations: newRoomsPresentations,
});
};
const currentPresentation = presentations.find((presentation) => presentation.current);
return ( return (
<Styled.BoxContainer key="rooms-grid-" ref={(r) => { this.listOfUsers = r; }} data-test="roomGrid"> <Styled.BoxContainer key="rooms-grid-" ref={(r) => { this.listOfUsers = r; }} data-test="roomGrid">
{ {
@ -963,6 +993,31 @@ class BreakoutRoom extends PureComponent {
{intl.formatMessage(intlMessages.roomNameInputDesc)} {intl.formatMessage(intlMessages.roomNameInputDesc)}
</div> </div>
</Styled.FreeJoinLabel> </Styled.FreeJoinLabel>
{ presentations.length > 0 ? (
<Styled.BreakoutSlideLabel>
<Styled.InputRooms
value={this.getRoomPresentation(value)}
onChange={changeRoomPresentation(value)}
valid
>
{ currentPresentation?.id ? (
<option key="current-slide" value={`${CURRENT_SLIDE_PREFIX}${currentPresentation.id}`}>
{intl.formatMessage(intlMessages.currentSlide)}
</option>
) : null }
{
presentations.map((presentation) => (
<option
key={presentation.id}
value={presentation.id}
>
{presentation.name}
</option>
))
}
</Styled.InputRooms>
</Styled.BreakoutSlideLabel>
) : null }
<Styled.BreakoutBox id={`breakoutBox-${value}`} onDrop={drop(value)} onDragOver={allowDrop} tabIndex={0}> <Styled.BreakoutBox id={`breakoutBox-${value}`} onDrop={drop(value)} onDragOver={allowDrop} tabIndex={0}>
{this.renderUserItemByRoom(value)} {this.renderUserItemByRoom(value)}
</Styled.BreakoutBox> </Styled.BreakoutBox>

View File

@ -3,6 +3,7 @@ import { withTracker } from 'meteor/react-meteor-data';
import ActionsBarService from '/imports/ui/components/actions-bar/service'; import ActionsBarService from '/imports/ui/components/actions-bar/service';
import BreakoutRoomService from '/imports/ui/components/breakout-room/service'; import BreakoutRoomService from '/imports/ui/components/breakout-room/service';
import CreateBreakoutRoomModal from './component'; import CreateBreakoutRoomModal from './component';
import Presentations from '/imports/api/presentations';
import { isImportSharedNotesFromBreakoutRoomsEnabled, isImportPresentationWithAnnotationsFromBreakoutRoomsEnabled } from '/imports/ui/services/features'; import { isImportSharedNotesFromBreakoutRoomsEnabled, isImportPresentationWithAnnotationsFromBreakoutRoomsEnabled } from '/imports/ui/services/features';
const METEOR_SETTINGS_APP = Meteor.settings.public.app; const METEOR_SETTINGS_APP = Meteor.settings.public.app;
@ -46,4 +47,5 @@ export default withTracker(() => ({
meetingName: ActionsBarService.meetingName(), meetingName: ActionsBarService.meetingName(),
amIModerator: ActionsBarService.amIModerator(), amIModerator: ActionsBarService.amIModerator(),
moveUser: ActionsBarService.moveUser, moveUser: ActionsBarService.moveUser,
presentations: Presentations.find({ 'conversion.done': true }).fetch(),
}))(CreateBreakoutRoomContainer); }))(CreateBreakoutRoomContainer);

View File

@ -68,6 +68,15 @@ const FreeJoinLabel = styled.label`
} }
`; `;
const BreakoutSlideLabel = styled.label`
font-size: ${fontSizeSmall};
font-weight: bolder;
display: flex;
align-items: center;
font-size: ${fontSizeSmall};
margin-bottom: 0.2rem;
`
const BreakoutNameInput = styled.input` const BreakoutNameInput = styled.input`
width: 100%; width: 100%;
text-align: center; text-align: center;
@ -377,4 +386,5 @@ export default {
SubTitle, SubTitle,
Content, Content,
ContentContainer, ContentContainer,
BreakoutSlideLabel,
}; };

View File

@ -1249,6 +1249,7 @@
"app.createBreakoutRoom.setTimeHigherThanMeetingTimeError": "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.createBreakoutRoom.movedUserLabel": "Moved {0} to room {1}", "app.createBreakoutRoom.movedUserLabel": "Moved {0} to room {1}",
"app.createBreakoutRoom.currentSlideLabel": "Current slide",
"app.updateBreakoutRoom.modalDesc": "To update or invite a user, simply drag them into the desired room.", "app.updateBreakoutRoom.modalDesc": "To update or invite a user, simply drag them into the desired room.",
"app.updateBreakoutRoom.cancelLabel": "Cancel", "app.updateBreakoutRoom.cancelLabel": "Cancel",
"app.updateBreakoutRoom.title": "Update Breakout Rooms", "app.updateBreakoutRoom.title": "Update Breakout Rooms",