diff --git a/bigbluebutton-html5/imports/api/breakouts/server/handlers/userBreakoutChanged.js b/bigbluebutton-html5/imports/api/breakouts/server/handlers/userBreakoutChanged.js
new file mode 100644
index 0000000000..dfb999cdc0
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/breakouts/server/handlers/userBreakoutChanged.js
@@ -0,0 +1,57 @@
+import Breakouts from '/imports/api/breakouts';
+import Logger from '/imports/startup/server/logger';
+import { check } from 'meteor/check';
+
+export default function userBreakoutChanged({ body }) {
+ check(body, Object);
+
+ const {
+ parentId,
+ userId,
+ fromBreakoutId,
+ toBreakoutId,
+ redirectToHtml5JoinUrl,
+ } = body;
+
+ check(parentId, String);
+ check(userId, String);
+ check(fromBreakoutId, String);
+ check(toBreakoutId, String);
+ check(redirectToHtml5JoinUrl, String);
+
+ const oldBreakoutSelector = {
+ parentMeetingId: parentId,
+ breakoutId: fromBreakoutId,
+ };
+
+ const newBreakoutSelector = {
+ parentMeetingId: parentId,
+ breakoutId: toBreakoutId,
+ };
+
+ const oldModifier = {
+ $unset: {
+ [`url_${userId}`]: '',
+ },
+ };
+
+ const newModifier = {
+ $set: {
+ [`url_${userId}`]: {
+ redirectToHtml5JoinUrl,
+ insertedTime: new Date().getTime(),
+ },
+ },
+ };
+
+ try {
+ const numberAffectedOld = Breakouts.update(oldBreakoutSelector, oldModifier);
+ const numberAffectedNew = Breakouts.update(newBreakoutSelector, newModifier);
+
+ if (numberAffectedOld && numberAffectedNew) {
+ Logger.info(`Updated user breakout for userId=${userId}`);
+ }
+ } catch (err) {
+ Logger.error(`Updating user breakout: ${err}`);
+ }
+}
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods.js b/bigbluebutton-html5/imports/api/breakouts/server/methods.js
index 6ad26cd142..27b26c4dbf 100755
--- a/bigbluebutton-html5/imports/api/breakouts/server/methods.js
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods.js
@@ -4,6 +4,7 @@ import requestJoinURL from './methods/requestJoinURL';
import endAllBreakouts from './methods/endAllBreakouts';
import setBreakoutsTime from '/imports/api/breakouts/server/methods/setBreakoutsTime';
import sendMessageToAllBreakouts from './methods/sendMessageToAllBreakouts';
+import moveUser from '/imports/api/breakouts/server/methods/moveUser';
Meteor.methods({
requestJoinURL,
@@ -11,4 +12,5 @@ Meteor.methods({
endAllBreakouts,
setBreakoutsTime,
sendMessageToAllBreakouts,
+ moveUser,
});
diff --git a/bigbluebutton-html5/imports/api/breakouts/server/methods/moveUser.js b/bigbluebutton-html5/imports/api/breakouts/server/methods/moveUser.js
new file mode 100644
index 0000000000..e017cee79c
--- /dev/null
+++ b/bigbluebutton-html5/imports/api/breakouts/server/methods/moveUser.js
@@ -0,0 +1,32 @@
+import { Meteor } from 'meteor/meteor';
+import RedisPubSub from '/imports/startup/server/redis';
+import { extractCredentials } from '/imports/api/common/server/helpers';
+import { check } from 'meteor/check';
+import Logger from '/imports/startup/server/logger';
+
+export default function moveUser(fromBreakoutId, toBreakoutId, userIdToMove) {
+ const REDIS_CONFIG = Meteor.settings.private.redis;
+ const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
+ const EVENT_NAME = 'ChangeUserBreakoutReqMsg';
+
+ try {
+ const { meetingId, requesterUserId } = extractCredentials(this.userId);
+
+ check(meetingId, String);
+ check(requesterUserId, String);
+
+ const userId = userIdToMove || requesterUserId;
+
+ return RedisPubSub.publishUserMessage(
+ CHANNEL, EVENT_NAME, meetingId, requesterUserId,
+ {
+ meetingId,
+ fromBreakoutId,
+ toBreakoutId,
+ userId,
+ },
+ );
+ } catch (err) {
+ Logger.error(`Exception while invoking method moveUser ${err.stack}`);
+ }
+}
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 21d1704bc5..f2c1028f08 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
@@ -9,6 +9,7 @@ import Modal from '/imports/ui/components/common/modal/fullscreen/component';
import { withModalMounter } from '/imports/ui/components/common/modal/service';
import SortList from './sort-user-list/component';
import Styled from './styles';
+import Icon from '/imports/ui/components/common/icon/component.jsx';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
@@ -21,6 +22,26 @@ const intlMessages = defineMessages({
id: 'app.createBreakoutRoom.modalDesc',
description: 'modal description',
},
+ breakoutRoomUpdateDesc: {
+ id: 'app.updateBreakoutRoom.modalDesc',
+ description: 'update modal description',
+ },
+ cancelLabel: {
+ id: 'app.updateBreakoutRoom.cancelLabel',
+ description: 'used in the button that close update modal',
+ },
+ updateTitle: {
+ id: 'app.updateBreakoutRoom.title',
+ description: 'update breakout title',
+ },
+ updateConfirm: {
+ id: 'app.updateBreakoutRoom.confirm',
+ description: 'Update to breakout confirm button label',
+ },
+ resetUserRoom: {
+ id: 'app.update.resetRoom',
+ description: 'Reset user room button label',
+ },
confirmButton: {
id: 'app.createBreakoutRoom.confirm',
description: 'confirm button label',
@@ -153,7 +174,7 @@ const propTypes = {
meetingName: PropTypes.string.isRequired,
users: PropTypes.arrayOf(PropTypes.object).isRequired,
createBreakoutRoom: PropTypes.func.isRequired,
- getUsersNotAssigned: PropTypes.func.isRequired,
+ getUsersNotJoined: PropTypes.func.isRequired,
getBreakouts: PropTypes.func.isRequired,
sendInvitation: PropTypes.func.isRequired,
mountModal: PropTypes.func.isRequired,
@@ -192,10 +213,11 @@ class BreakoutRoom extends PureComponent {
this.removeRoomUsers = this.removeRoomUsers.bind(this);
this.renderErrorMessages = this.renderErrorMessages.bind(this);
this.renderJoinedUsers = this.renderJoinedUsers.bind(this);
+ this.onUpdateBreakouts = this.onUpdateBreakouts.bind(this);
this.state = {
numberOfRooms: MIN_BREAKOUT_ROOMS,
- seletedId: '',
+ selectedId: '',
users: [],
durationTime: 15,
freeJoin: false,
@@ -220,7 +242,7 @@ class BreakoutRoom extends PureComponent {
componentDidMount() {
const {
- isInvitation, breakoutJoinedUsers, getLastBreakouts, groups,
+ isInvitation, breakoutJoinedUsers, getLastBreakouts, groups, isUpdate,
} = this.props;
this.setRoomUsers();
if (isInvitation) {
@@ -231,6 +253,30 @@ class BreakoutRoom extends PureComponent {
});
}
+ if (isUpdate) {
+ const usersToMerge = []
+ breakoutJoinedUsers.forEach((breakout) => {
+ breakout.joinedUsers.forEach((user) => {
+ usersToMerge.push({
+ userId: user.userId,
+ userName: user.name,
+ from: breakout.sequence,
+ room: breakout.sequence,
+ isModerator: user.role === ROLE_MODERATOR,
+ joined: true,
+ });
+ });
+ });
+ this.setState((prevState) => {
+ return {
+ users: [
+ ...prevState.users,
+ ...usersToMerge,
+ ],
+ };
+ });
+ }
+
const lastBreakouts = getLastBreakouts();
if (lastBreakouts.length > 0) {
this.populateWithLastBreakouts(lastBreakouts);
@@ -424,6 +470,51 @@ class BreakoutRoom extends PureComponent {
this.handleDismiss();
}
+ getBreakoutBySequence(sequence) {
+ const { getBreakouts } = this.props;
+ const breakouts = getBreakouts();
+
+ return breakouts.find((breakout) => breakout.sequence === sequence);
+ }
+
+ changeUserBreakout(fromBreakoutId, toBreakoutId, userId) {
+ const { moveUser } = this.props;
+
+ moveUser(fromBreakoutId, toBreakoutId, userId);
+ }
+
+ onUpdateBreakouts() {
+ const { users } = this.state;
+ const { sendInvitation } = this.props;
+ const leastOneUserIsValid = users.some((user) => user.from !== user.room);
+
+ if (!leastOneUserIsValid) {
+ this.setState({ leastOneUserIsValid });
+ }
+
+ users.forEach((user) => {
+ const { from, room } = user;
+ let { userId } = user;
+
+ if (from === room || room === 0) return;
+
+ const toBreakout = this.getBreakoutBySequence(room);
+ const { breakoutId: toBreakoutId } = toBreakout;
+
+ if (!user.joined) return sendInvitation(toBreakoutId, userId);
+
+ userId = userId.split('-')[0];
+ const fromBreakout = this.getBreakoutBySequence(from);
+ const { breakoutId: fromBreakoutId } = fromBreakout;
+
+ if (toBreakout.freeJoin) return sendInvitation(toBreakoutId, userId);
+
+ this.changeUserBreakout(fromBreakoutId, toBreakoutId, userId);
+ });
+
+ this.handleDismiss();
+ }
+
onAssignRandomly() {
const { numberOfRooms } = this.state;
const { users } = this.state;
@@ -463,15 +554,16 @@ class BreakoutRoom extends PureComponent {
}
setRoomUsers() {
- const { users, getUsersNotAssigned } = this.props;
+ const { users, getUsersNotJoined } = this.props;
const { users: stateUsers } = this.state;
const stateUsersId = stateUsers.map((user) => user.userId);
- const roomUsers = getUsersNotAssigned(users)
+ const roomUsers = getUsersNotJoined(users)
.filter((user) => !stateUsersId.includes(user.userId))
.map((user) => ({
userId: user.userId,
userName: user.name,
isModerator: user.role === ROLE_MODERATOR,
+ from: 0,
room: 0,
}));
@@ -532,7 +624,7 @@ class BreakoutRoom extends PureComponent {
const usersCopy = [...users];
- usersCopy[idxUser].room = room;
+ if (idxUser >= 0) usersCopy[idxUser].room = room;
this.setState({
users: usersCopy,
@@ -691,7 +783,7 @@ class BreakoutRoom extends PureComponent {
ev.preventDefault();
const data = ev.dataTransfer.getData('text');
this.changeUserRoom(data, room);
- this.setState({ seletedId: '' });
+ this.setState({ selectedId: '' });
};
const changeRoomName = (position) => (ev) => {
@@ -767,6 +859,7 @@ class BreakoutRoom extends PureComponent {
const {
intl,
isInvitation,
+ isUpdate,
} = this.props;
const {
numberOfRooms,
@@ -774,7 +867,7 @@ class BreakoutRoom extends PureComponent {
numberOfRoomsIsValid,
durationIsValid,
} = this.state;
- if (isInvitation) return null;
+ if (isInvitation || isUpdate) return null;
return (
@@ -905,8 +998,8 @@ class BreakoutRoom extends PureComponent {
}
renderCheckboxes() {
- const { intl, isInvitation, isBreakoutRecordable } = this.props;
- if (isInvitation) return null;
+ const { intl, isInvitation, isUpdate, isBreakoutRecordable } = this.props;
+ if (isInvitation || isUpdate) return null;
const {
freeJoin,
record,
@@ -946,14 +1039,14 @@ class BreakoutRoom extends PureComponent {
renderUserItemByRoom(room) {
const {
leastOneUserIsValid,
- seletedId,
+ selectedId,
} = this.state;
const { intl, isMe } = this.props;
const dragStart = (ev) => {
ev.dataTransfer.setData('text', ev.target.id);
- this.setState({ seletedId: ev.target.id });
+ this.setState({ selectedId: ev.target.id });
if (!leastOneUserIsValid) {
this.setState({ leastOneUserIsValid: true });
@@ -961,7 +1054,7 @@ class BreakoutRoom extends PureComponent {
};
const dragEnd = () => {
- this.setState({ seletedId: '' });
+ this.setState({ selectedId: '' });
};
return this.getUserByRoom(room)
@@ -970,14 +1063,27 @@ class BreakoutRoom extends PureComponent {
tabIndex={-1}
id={`roomUserItem-${user.userId}`}
key={user.userId}
- selected={seletedId === user.userId}
+ selected={selectedId.replace('roomUserItem-', '') === user.userId.replace('roomUserItem-', '')}
disabled={false}
+ highlight={room !== user.from}
draggable
onDragStart={dragStart}
onDragEnd={dragEnd}
>
- {user.userName}
- {(isMe(user.userId)) ? ` (${intl.formatMessage(intlMessages.you)})` : ''}
+
+ {user.userName}
+ {(isMe(user.userId)) ? ` (${intl.formatMessage(intlMessages.you)})` : ''}
+
+ { room !== user.from ? (
+ this.changeUserRoom(user.userId, user.from)}
+ >
+
+
+ ) : null }
));
}
@@ -1112,16 +1218,18 @@ class BreakoutRoom extends PureComponent {
}
renderTitle() {
- const { intl } = this.props;
+ const { intl, isUpdate } = this.props;
return (
- {intl.formatMessage(intlMessages.breakoutRoomDesc)}
+ { isUpdate
+ ? intl.formatMessage(intlMessages.breakoutRoomUpdateDesc)
+ : intl.formatMessage(intlMessages.breakoutRoomDesc) }
);
}
render() {
- const { intl, isInvitation } = this.props;
+ const { intl, isInvitation, isUpdate } = this.props;
const {
preventClosing,
leastOneUserIsValid,
@@ -1138,14 +1246,18 @@ class BreakoutRoom extends PureComponent {
title={
isInvitation
? intl.formatMessage(intlMessages.invitationTitle)
+ : isUpdate
+ ? intl.formatMessage(intlMessages.updateTitle)
: intl.formatMessage(intlMessages.breakoutRoomTitle)
}
confirm={
{
label: isInvitation
? intl.formatMessage(intlMessages.invitationConfirm)
+ : isUpdate
+ ? intl.formatMessage(intlMessages.updateConfirm)
: intl.formatMessage(intlMessages.confirmButton),
- callback: isInvitation ? this.onInviteBreakout : this.onCreateBreakouts,
+ callback: isInvitation ? this.onInviteBreakout : isUpdate ? this.onUpdateBreakouts : this.onCreateBreakouts,
disabled: !leastOneUserIsValid
|| !numberOfRoomsIsValid
|| !roomNameDuplicatedIsValid
@@ -1156,7 +1268,9 @@ class BreakoutRoom extends PureComponent {
}
dismiss={{
callback: this.handleDismiss,
- label: intl.formatMessage(intlMessages.dismissLabel),
+ label: isUpdate
+ ? intl.formatMessage(intlMessages.cancelLabel)
+ : intl.formatMessage(intlMessages.dismissLabel),
}}
preventClosing={preventClosing}
>
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx
index b5a1530f7e..5c50fa7279 100644
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/container.jsx
@@ -20,7 +20,7 @@ export default withTracker(() => ({
getBreakouts: ActionsBarService.getBreakouts,
getLastBreakouts: ActionsBarService.getLastBreakouts,
getBreakoutUserWasIn: BreakoutRoomService.getBreakoutUserWasIn,
- getUsersNotAssigned: ActionsBarService.getUsersNotAssigned,
+ getUsersNotJoined: ActionsBarService.getUsersNotJoined,
sendInvitation: ActionsBarService.sendInvitation,
breakoutJoinedUsers: ActionsBarService.breakoutJoinedUsers(),
users: ActionsBarService.users(),
@@ -28,4 +28,5 @@ export default withTracker(() => ({
isMe: ActionsBarService.isMe,
meetingName: ActionsBarService.meetingName(),
amIModerator: ActionsBarService.amIModerator(),
+ moveUser: ActionsBarService.moveUser,
}))(CreateBreakoutRoomContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js
index 08f6487a50..ae96a7f2e7 100644
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/create-breakout-room/styles.js
@@ -12,12 +12,15 @@ import {
colorWhite,
colorPrimary,
colorBlueLight,
+ colorBlueLightest,
+ colorGrayLightest,
} from '/imports/ui/stylesheets/styled-components/palette';
-import { fontSizeSmall, fontSizeBase } from '/imports/ui/stylesheets/styled-components/typography';
+import { fontSizeSmall, fontSizeBase, fontSizeSmaller } from '/imports/ui/stylesheets/styled-components/typography';
import {
borderRadius,
borderSize,
lgPaddingX,
+ lgPaddingY,
} from '/imports/ui/stylesheets/styled-components/general';
const BoxContainer = styled.div`
@@ -66,9 +69,10 @@ const FreeJoinLabel = styled.label`
const BreakoutNameInput = styled.input`
width: 100%;
text-align: left;
- font-weight: normal;
- padding: .25rem;
+ font-weight: 600;
+ padding: .25rem .25rem .25rem 0;
margin: 0;
+ border: none;
&::placeholder {
color: ${colorGray};
opacity: 1;
@@ -79,8 +83,9 @@ const BreakoutBox = styled(ScrollboxVertical)`
width: 100%;
min-height: 6rem;
max-height: 8rem;
- border: 1px solid ${colorGrayLighter};
- border-radius: ${borderRadius};
+ border: 1px solid ${colorGrayLightest};
+ border-radius: ${borderRadius};
+ padding: ${lgPaddingY} 0;
`;
const SpanWarn = styled.span`
@@ -228,12 +233,21 @@ const RoomUserItem = styled.p`
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
- border-bottom: solid .5px ${colorGrayLighter};
+ display: flex;
+ justify-content: space-between;
[dir="rtl"] & {
padding: .25rem .25rem .25rem 0;
}
+ span.close {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ margin-right: 5px;
+ font-size: ${fontSizeSmaller};
+ }
+
${({ selected }) => selected && `
background-color: ${colorPrimary};
color: ${colorWhite};
@@ -243,6 +257,10 @@ const RoomUserItem = styled.p`
cursor: not-allowed;
color: ${colorGrayLighter};
`}
+
+ ${({ highlight }) => highlight && `
+ background-color: ${colorBlueLightest};
+ `}
`;
const LockIcon = styled.span`
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/service.js b/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
index 146ab005c1..e84d1e7c1f 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
@@ -30,7 +30,7 @@ const currentBreakoutUsers = (user) => !Breakouts.findOne({
const filterBreakoutUsers = (filter) => (users) => users.filter(filter);
-const getUsersNotAssigned = filterBreakoutUsers(currentBreakoutUsers);
+const getUsersNotJoined = filterBreakoutUsers(currentBreakoutUsers);
const takePresenterRole = () => makeCall('assignPresenter', Auth.userID);
@@ -70,9 +70,10 @@ export default {
breakoutJoinedUsers: () => Breakouts.find({
joinedUsers: { $exists: true },
}, { fields: { joinedUsers: 1, breakoutId: 1, sequence: 1 }, sort: { sequence: 1 } }).fetch(),
+ moveUser: (fromBreakoutId, toBreakoutId, userId) => makeCall('moveUser', fromBreakoutId, toBreakoutId, userId),
getBreakouts,
getLastBreakouts,
- getUsersNotAssigned,
+ getUsersNotJoined,
takePresenterRole,
isSharingVideo: () => getVideoUrl(),
};
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx
index d486604c09..6c95c85bc2 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/breakout-dropdown/component.jsx
@@ -1,7 +1,9 @@
import React, { PureComponent } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
+import { withModalMounter } from '/imports/ui/components/common/modal/service';
import BBBMenu from "/imports/ui/components/common/menu/component";
import Button from '/imports/ui/components/common/button/component';
+import CreateBreakoutRoomModal from '/imports/ui/components/actions-bar/create-breakout-room/container';
const intlMessages = defineMessages({
options: {
@@ -12,6 +14,11 @@ const intlMessages = defineMessages({
id: 'app.breakout.dropdown.manageDuration',
description: 'Manage duration label',
},
+ manageUsers: {
+ id: 'app.breakout.dropdown.manageUsers',
+ description: 'Manage users label',
+ defaultMessage: 'Manage Users',
+ },
destroy: {
id: 'app.breakout.dropdown.destroyAll',
description: 'Destroy breakouts label',
@@ -30,6 +37,7 @@ class BreakoutDropdown extends PureComponent {
endAllBreakouts,
isMeteorConnected,
amIModerator,
+ mountModal,
} = this.props;
this.menuItems = [];
@@ -45,6 +53,19 @@ class BreakoutDropdown extends PureComponent {
}
);
+ this.menuItems.push(
+ {
+ key: 'updateBreakoutUsers',
+ dataTest: 'openUpdateBreakoutUsersModal',
+ label: intl.formatMessage(intlMessages.manageUsers),
+ onClick: () => {
+ mountModal(
+
+ );
+ }
+ }
+ );
+
if (amIModerator) {
this.menuItems.push(
{
@@ -101,4 +122,4 @@ class BreakoutDropdown extends PureComponent {
}
}
-export default injectIntl(BreakoutDropdown);
+export default withModalMounter(injectIntl(BreakoutDropdown));
diff --git a/bigbluebutton-html5/imports/ui/components/common/modal/fullscreen/styles.js b/bigbluebutton-html5/imports/ui/components/common/modal/fullscreen/styles.js
index 6048436322..fc351816d9 100644
--- a/bigbluebutton-html5/imports/ui/components/common/modal/fullscreen/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/common/modal/fullscreen/styles.js
@@ -2,16 +2,17 @@ import styled from 'styled-components';
import Styled from '../base/styles';
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
import Button from '/imports/ui/components/common/button/component';
-import { borderSize, smPaddingX } from '/imports/ui/stylesheets/styled-components/general';
+import { borderSize, smPaddingX, borderRadius } from '/imports/ui/stylesheets/styled-components/general';
import {
lineHeightComputed,
modalTitleFw,
} from '/imports/ui/stylesheets/styled-components/typography';
import {
- colorGrayLighter,
+ colorGrayLightest,
colorText,
colorWhite,
colorLink,
+ colorBlueLight,
} from '/imports/ui/stylesheets/styled-components/palette';
const FullscreenModal = styled(Styled.BaseModal)`
@@ -32,7 +33,7 @@ const FullscreenModal = styled(Styled.BaseModal)`
const Header = styled.header`
display: flex;
padding: ${lineHeightComputed} 0;
- border-bottom: ${borderSize} solid ${colorGrayLighter};
+ border-bottom: ${borderSize} solid ${colorGrayLightest};
`;
const Title = styled.h1`
@@ -61,12 +62,26 @@ const Content = styled.div`
const DismissButton = styled(Button)`
flex: 0 1 48%;
+ border: 1px solid ${colorBlueLight};
+ border-radius: ${borderRadius};
+ color: ${colorBlueLight};
+
+ &:focus,
+ .buttonWrapper:focus:not([aria-disabled="true"]) & {
+ color: ${colorBlueLight};
+ }
+
+ &:hover,
+ .buttonWrapper:hover & {
+ color: ${colorBlueLight};
+ }
`;
const ConfirmButton = styled(Button)`
flex: 0 1 48%;
color: ${colorWhite} !important;
background-color: ${colorLink} !important;
+ border-width: 1px;
${({ popout }) => popout === 'popout' && `
& > i {
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
index f03790dee8..68f2e647d9 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/component.jsx
@@ -213,7 +213,7 @@ class UserOptions extends PureComponent {
meetingIsBreakout,
hasBreakoutRoom,
isBreakoutEnabled,
- getUsersNotAssigned,
+ getUsersNotJoined,
openLearningDashboardUrl,
amIModerator,
users,
@@ -229,7 +229,7 @@ class UserOptions extends PureComponent {
const canInviteUsers = amIModerator
&& !meetingIsBreakout
&& hasBreakoutRoom
- && getUsersNotAssigned(users).length;
+ && getUsersNotJoined(users).length;
const { locale } = intl;
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
index 5f50d670c8..3c16e19d23 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
@@ -85,7 +85,7 @@ const UserOptionsContainer = withTracker((props) => {
toggleStatus,
isMeetingMuted: isMeetingMuteOnStart(),
amIModerator: ActionsBarService.amIModerator(),
- getUsersNotAssigned: ActionsBarService.getUsersNotAssigned,
+ getUsersNotJoined: ActionsBarService.getUsersNotJoined,
hasBreakoutRoom: UserListService.hasBreakoutRoom(),
isBreakoutEnabled: ActionsBarService.isBreakoutEnabled(),
isBreakoutRecordable: ActionsBarService.isBreakoutRecordable(),
diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json
index 33b4fd5102..b8b2feff80 100755
--- a/bigbluebutton-html5/public/locales/en.json
+++ b/bigbluebutton-html5/public/locales/en.json
@@ -913,6 +913,11 @@
"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.updateBreakoutRoom.modalDesc": "To update or invite a user, simply drag them into the desired room.",
+ "app.updateBreakoutRoom.cancelLabel": "Cancel",
+ "app.updateBreakoutRoom.title": "Update Breakout Rooms",
+ "app.updateBreakoutRoom.confirm": "Apply",
+ "app.update.resetRoom": "Reset user room",
"app.externalVideo.start": "Share a new video",
"app.externalVideo.title": "Share an external video",
"app.externalVideo.input": "External Video URL",