feat: allow for moving users among breakouts + modal design updates
This commit is contained in:
parent
5778306626
commit
0ea405b67f
@ -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}`);
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
|
@ -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}`);
|
||||
}
|
||||
}
|
@ -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 (
|
||||
<React.Fragment key="breakout-form">
|
||||
@ -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}
|
||||
<span>
|
||||
<span>{user.userName}</span>
|
||||
<i>{(isMe(user.userId)) ? ` (${intl.formatMessage(intlMessages.you)})` : ''}</i>
|
||||
</span>
|
||||
{ room !== user.from ? (
|
||||
<span
|
||||
className="close"
|
||||
role="button"
|
||||
aria-label={intl.formatMessage(intlMessages.resetUserRoom)}
|
||||
onClick={() => this.changeUserRoom(user.userId, user.from)}
|
||||
>
|
||||
<Icon iconName="close" />
|
||||
</span>
|
||||
) : null }
|
||||
</Styled.RoomUserItem>
|
||||
));
|
||||
}
|
||||
@ -1112,16 +1218,18 @@ class BreakoutRoom extends PureComponent {
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
const { intl } = this.props;
|
||||
const { intl, isUpdate } = this.props;
|
||||
return (
|
||||
<Styled.SubTitle>
|
||||
{intl.formatMessage(intlMessages.breakoutRoomDesc)}
|
||||
{ isUpdate
|
||||
? intl.formatMessage(intlMessages.breakoutRoomUpdateDesc)
|
||||
: intl.formatMessage(intlMessages.breakoutRoomDesc) }
|
||||
</Styled.SubTitle>
|
||||
);
|
||||
}
|
||||
|
||||
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}
|
||||
>
|
||||
|
@ -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);
|
||||
|
@ -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: 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`
|
||||
|
@ -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(),
|
||||
};
|
||||
|
@ -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(
|
||||
<CreateBreakoutRoomModal isUpdate />
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (amIModerator) {
|
||||
this.menuItems.push(
|
||||
{
|
||||
@ -101,4 +122,4 @@ class BreakoutDropdown extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(BreakoutDropdown);
|
||||
export default withModalMounter(injectIntl(BreakoutDropdown));
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user