Merge pull request #11175 from MaximKhlobystov/select-random-user
Presenter selecting a random viewer
This commit is contained in:
commit
45c1ff6e40
@ -0,0 +1,42 @@
|
||||
package org.bigbluebutton.core.apps.users
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import scala.util.Random
|
||||
|
||||
trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
||||
this: UsersApp =>
|
||||
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
|
||||
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
|
||||
|
||||
def broadcastEvent(msg: SelectRandomViewerReqMsg, selectedUser: UserState): Unit = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
val envelope = BbbCoreEnvelope(SelectRandomViewerRespMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(SelectRandomViewerRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||
|
||||
val body = SelectRandomViewerRespMsgBody(msg.header.userId, selectedUser.intId)
|
||||
val event = SelectRandomViewerRespMsg(header, body)
|
||||
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
|
||||
outGW.send(msgEvent)
|
||||
}
|
||||
|
||||
if (permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "No permission to select random user."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
} else {
|
||||
val users = Users2x.findViewers(liveMeeting.users2x)
|
||||
val randNum = new scala.util.Random
|
||||
|
||||
if (users.size > 0) {
|
||||
broadcastEvent(msg, users(randNum.nextInt(users.size)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -145,6 +145,7 @@ class UsersApp(
|
||||
with SendRecordingTimerInternalMsgHdlr
|
||||
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
|
||||
with GetRecordingStatusReqMsgHdlr
|
||||
with SelectRandomViewerReqMsgHdlr
|
||||
with GetWebcamsOnlyForModeratorReqMsgHdlr
|
||||
with AssignPresenterReqMsgHdlr
|
||||
with EjectDuplicateUserReqMsgHdlr
|
||||
|
@ -59,6 +59,10 @@ object Users2x {
|
||||
users.toVector.filter(u => !u.presenter)
|
||||
}
|
||||
|
||||
def findViewers(users: Users2x): Vector[UserState] = {
|
||||
users.toVector.filter(u => u.role == Roles.VIEWER_ROLE)
|
||||
}
|
||||
|
||||
def updateLastUserActivity(users: Users2x, u: UserState): UserState = {
|
||||
val newUserState = modify(u)(_.lastActivityTime).setTo(TimeUtil.timeNowInMs())
|
||||
users.save(newUserState)
|
||||
@ -241,6 +245,7 @@ class Users2x {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class OldPresenter(userId: String, changedPresenterOn: Long)
|
||||
|
@ -103,6 +103,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[GetPresenterGroupReqMsg](envelope, jsonNode)
|
||||
case UserActivitySignCmdMsg.NAME =>
|
||||
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
|
||||
case SelectRandomViewerReqMsg.NAME =>
|
||||
routeGenericMsg[SelectRandomViewerReqMsg](envelope, jsonNode)
|
||||
|
||||
// Poll
|
||||
case StartCustomPollReqMsg.NAME =>
|
||||
|
@ -308,6 +308,7 @@ class MeetingActor(
|
||||
case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m)
|
||||
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||
case m: SelectRandomViewerReqMsg => usersApp.handleSelectRandomViewerReqMsg(m)
|
||||
|
||||
// Client requested to eject user
|
||||
case m: EjectUserFromMeetingCmdMsg =>
|
||||
|
@ -394,3 +394,17 @@ case class UserInactivityInspectMsgBody(meetingId: String, responseDelay: Long)
|
||||
object UserActivitySignCmdMsg { val NAME = "UserActivitySignCmdMsg" }
|
||||
case class UserActivitySignCmdMsg(header: BbbClientMsgHeader, body: UserActivitySignCmdMsgBody) extends StandardMsg
|
||||
case class UserActivitySignCmdMsgBody(userId: String)
|
||||
|
||||
/**
|
||||
* Sent from client to randomly select a viewer
|
||||
*/
|
||||
object SelectRandomViewerReqMsg { val NAME = "SelectRandomViewerReqMsg" }
|
||||
case class SelectRandomViewerReqMsg(header: BbbClientMsgHeader, body: SelectRandomViewerReqMsgBody) extends StandardMsg
|
||||
case class SelectRandomViewerReqMsgBody(requestedBy: String)
|
||||
|
||||
/**
|
||||
* Response to request for a random viewer
|
||||
*/
|
||||
object SelectRandomViewerRespMsg { val NAME = "SelectRandomViewerRespMsg" }
|
||||
case class SelectRandomViewerRespMsg(header: BbbClientMsgHeader, body: SelectRandomViewerRespMsgBody) extends StandardMsg
|
||||
case class SelectRandomViewerRespMsgBody(requestedBy: String, selectedUserId: String)
|
||||
|
@ -9,6 +9,7 @@ import handleRecordingStatusChange from './handlers/recordingStatusChange';
|
||||
import handleRecordingTimerChange from './handlers/recordingTimerChange';
|
||||
import handleTimeRemainingUpdate from './handlers/timeRemainingUpdate';
|
||||
import handleChangeWebcamOnlyModerator from './handlers/webcamOnlyModerator';
|
||||
import handleSelectRandomViewer from './handlers/selectRandomViewer';
|
||||
|
||||
RedisPubSub.on('MeetingCreatedEvtMsg', handleMeetingCreation);
|
||||
RedisPubSub.on('SyncGetMeetingInfoRespMsg', handleGetAllMeetings);
|
||||
@ -21,3 +22,4 @@ RedisPubSub.on('UpdateRecordingTimerEvtMsg', handleRecordingTimerChange);
|
||||
RedisPubSub.on('WebcamsOnlyForModeratorChangedEvtMsg', handleChangeWebcamOnlyModerator);
|
||||
RedisPubSub.on('GetLockSettingsRespMsg', handleMeetingLocksChange);
|
||||
RedisPubSub.on('MeetingTimeRemainingUpdateEvtMsg', handleTimeRemainingUpdate);
|
||||
RedisPubSub.on('SelectRandomViewerRespMsg', handleSelectRandomViewer);
|
||||
|
@ -0,0 +1,13 @@
|
||||
import { check } from 'meteor/check';
|
||||
import updateRandomViewer from '../modifiers/updateRandomViewer';
|
||||
|
||||
export default function randomlySelectedUser({ header, body }) {
|
||||
const { selectedUserId, requestedBy } = body;
|
||||
const { meetingId } = header;
|
||||
|
||||
check(meetingId, String);
|
||||
check(requestedBy, String);
|
||||
check(selectedUserId, String);
|
||||
|
||||
updateRandomViewer(meetingId, selectedUserId, requestedBy);
|
||||
}
|
@ -4,6 +4,7 @@ import toggleRecording from './methods/toggleRecording';
|
||||
import transferUser from './methods/transferUser';
|
||||
import toggleLockSettings from './methods/toggleLockSettings';
|
||||
import toggleWebcamsOnlyForModerator from './methods/toggleWebcamsOnlyForModerator';
|
||||
import clearRandomlySelectedUser from './methods/clearRandomlySelectedUser';
|
||||
|
||||
Meteor.methods({
|
||||
endMeeting,
|
||||
@ -11,4 +12,5 @@ Meteor.methods({
|
||||
toggleLockSettings,
|
||||
transferUser,
|
||||
toggleWebcamsOnlyForModerator,
|
||||
clearRandomlySelectedUser,
|
||||
});
|
||||
|
@ -0,0 +1,26 @@
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||
|
||||
export default function clearRandomlySelectedUser() {
|
||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
randomlySelectedUser: '',
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const { insertedId } = Meetings.update(selector, modifier);
|
||||
if (insertedId) {
|
||||
Logger.info(`Cleared randomly selected user from meeting=${meetingId} by id=${requesterUserId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(`Clearing randomly selected user : ${err}`);
|
||||
}
|
||||
}
|
@ -146,6 +146,7 @@ export default function addMeeting(meeting) {
|
||||
meetingId,
|
||||
meetingEnded,
|
||||
publishedPoll: false,
|
||||
randomlySelectedUser: '',
|
||||
}, flat(newMeeting, {
|
||||
safe: true,
|
||||
})),
|
||||
|
@ -0,0 +1,28 @@
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
export default function updateRandomUser(meetingId, userId, requesterId) {
|
||||
check(meetingId, String);
|
||||
check(userId, String);
|
||||
check(requesterId, String);
|
||||
|
||||
const selector = {
|
||||
meetingId,
|
||||
};
|
||||
|
||||
const modifier = {
|
||||
$set: {
|
||||
randomlySelectedUser: userId,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const { insertedId } = Meetings.upsert(selector, modifier);
|
||||
if (insertedId) {
|
||||
Logger.info(`Set randomly selected userId=${userId} by requesterId=${requesterId} in meeitingId=${meetingId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(`Setting randomly selected userId=${userId} by requesterId=${requesterId} in meetingId=${meetingId}`);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import toggleUserLock from './methods/toggleUserLock';
|
||||
import setUserEffectiveConnectionType from './methods/setUserEffectiveConnectionType';
|
||||
import userActivitySign from './methods/userActivitySign';
|
||||
import userLeftMeeting from './methods/userLeftMeeting';
|
||||
import setRandomUser from './methods/setRandomUser';
|
||||
|
||||
Meteor.methods({
|
||||
setEmojiStatus,
|
||||
@ -19,4 +20,5 @@ Meteor.methods({
|
||||
setUserEffectiveConnectionType,
|
||||
userActivitySign,
|
||||
userLeftMeeting,
|
||||
setRandomUser,
|
||||
});
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import RedisPubSub from '/imports/startup/server/redis';
|
||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||
|
||||
export default function setRandomUser() {
|
||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||
const EVENT_NAME = 'SelectRandomViewerReqMsg';
|
||||
|
||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||
|
||||
const payload = {
|
||||
requestedBy: requesterUserId,
|
||||
};
|
||||
|
||||
RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
||||
}
|
@ -12,6 +12,7 @@ import { withModalMounter } from '/imports/ui/components/modal/service';
|
||||
import withShortcutHelper from '/imports/ui/components/shortcut-help/service';
|
||||
import DropdownListSeparator from '/imports/ui/components/dropdown/list/separator/component';
|
||||
import ExternalVideoModal from '/imports/ui/components/external-video-player/modal/container';
|
||||
import RandomUserSelectContainer from '/imports/ui/components/modal/random-user/container';
|
||||
import cx from 'classnames';
|
||||
import { styles } from '../styles';
|
||||
|
||||
@ -75,6 +76,14 @@ const intlMessages = defineMessages({
|
||||
id: 'app.actionsBar.actionsDropdown.stopShareExternalVideo',
|
||||
description: 'Stop sharing external video button',
|
||||
},
|
||||
selectRandUserLabel: {
|
||||
id: 'app.actionsBar.actionsDropdown.selectRandUserLabel',
|
||||
description: 'Label for selecting a random user',
|
||||
},
|
||||
selectRandUserDesc: {
|
||||
id: 'app.actionsBar.actionsDropdown.selectRandUserDesc',
|
||||
description: 'Description for select random user option',
|
||||
},
|
||||
});
|
||||
|
||||
const handlePresentationClick = () => Session.set('showUploadPresentationView', true);
|
||||
@ -86,6 +95,7 @@ class ActionsDropdown extends PureComponent {
|
||||
this.presentationItemId = _.uniqueId('action-item-');
|
||||
this.pollId = _.uniqueId('action-item-');
|
||||
this.takePresenterId = _.uniqueId('action-item-');
|
||||
this.selectUserRandId = _.uniqueId('action-item-');
|
||||
|
||||
this.handleExternalVideoClick = this.handleExternalVideoClick.bind(this);
|
||||
this.makePresentationItems = this.makePresentationItems.bind(this);
|
||||
@ -108,6 +118,7 @@ class ActionsDropdown extends PureComponent {
|
||||
isSharingVideo,
|
||||
isPollingEnabled,
|
||||
stopExternalVideoShare,
|
||||
mountModal,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@ -177,6 +188,17 @@ class ActionsDropdown extends PureComponent {
|
||||
/>
|
||||
)
|
||||
: null),
|
||||
(amIPresenter
|
||||
? (
|
||||
<DropdownListItem
|
||||
icon="user"
|
||||
label={intl.formatMessage(intlMessages.selectRandUserLabel)}
|
||||
description={intl.formatMessage(intlMessages.selectRandUserDesc)}
|
||||
key={this.selectUserRandId}
|
||||
onClick={() => mountModal(<RandomUserSelectContainer isSelectedUser={false} />)}
|
||||
/>
|
||||
)
|
||||
: null),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import StatusNotifier from '/imports/ui/components/status-notifier/container';
|
||||
import MediaService from '/imports/ui/components/media/service';
|
||||
import ManyWebcamsNotifier from '/imports/ui/components/video-provider/many-users-notify/container';
|
||||
import UploaderContainer from '/imports/ui/components/presentation/presentation-uploader/container';
|
||||
import RandomUserSelectContainer from '/imports/ui/components/modal/random-user/container';
|
||||
import { withDraggableContext } from '../media/webcam-draggable-overlay/context';
|
||||
import { styles } from './styles';
|
||||
import { NAVBAR_HEIGHT } from '/imports/ui/components/layout/layout-manager';
|
||||
@ -154,9 +155,18 @@ class App extends Component {
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
meetingMuted, notify, currentUserEmoji, intl, hasPublishedPoll,
|
||||
meetingMuted,
|
||||
notify,
|
||||
currentUserEmoji,
|
||||
intl,
|
||||
hasPublishedPoll,
|
||||
randomlySelectedUser,
|
||||
currentUserId,
|
||||
mountModal,
|
||||
} = this.props;
|
||||
|
||||
if (randomlySelectedUser === currentUserId) mountModal(<RandomUserSelectContainer />);
|
||||
|
||||
if (prevProps.currentUserEmoji.status !== currentUserEmoji.status) {
|
||||
const formattedEmojiStatus = intl.formatMessage({ id: `app.actionsBar.emojiMenu.${currentUserEmoji.status}Label` })
|
||||
|| currentUserEmoji.status;
|
||||
|
@ -91,10 +91,10 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
|
||||
},
|
||||
});
|
||||
|
||||
const currentUser = Users.findOne({ userId: Auth.userID }, { fields: { approved: 1, emoji: 1 } });
|
||||
const currentUser = Users.findOne({ userId: Auth.userID }, { fields: { approved: 1, emoji: 1, userId: 1 } });
|
||||
const currentMeeting = Meetings.findOne({ meetingId: Auth.meetingID },
|
||||
{ fields: { publishedPoll: 1, voiceProp: 1 } });
|
||||
const { publishedPoll, voiceProp } = currentMeeting;
|
||||
{ fields: { publishedPoll: 1, voiceProp: 1, randomlySelectedUser: 1 } });
|
||||
const { publishedPoll, voiceProp, randomlySelectedUser } = currentMeeting;
|
||||
|
||||
if (!currentUser.approved) {
|
||||
baseControls.updateLoadingState(intl.formatMessage(intlMessages.waitingApprovalMessage));
|
||||
@ -122,6 +122,8 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
|
||||
hasPublishedPoll: publishedPoll,
|
||||
startBandwidthMonitoring,
|
||||
handleNetworkConnection: () => updateNavigatorConnection(navigator.connection),
|
||||
randomlySelectedUser,
|
||||
currentUserId: currentUser.userId,
|
||||
};
|
||||
})(AppContainer)));
|
||||
|
||||
|
@ -0,0 +1,122 @@
|
||||
import React, { Component } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
import Modal from '/imports/ui/components/modal/simple/component';
|
||||
import Button from '/imports/ui/components/button/component';
|
||||
import { styles } from './styles';
|
||||
|
||||
const messages = defineMessages({
|
||||
noViewers: {
|
||||
id: 'app.modal.randomUser.noViewers.description',
|
||||
description: 'Label displayed when no viewers are avaiable',
|
||||
},
|
||||
selected: {
|
||||
id: 'app.modal.randomUser.selected.description',
|
||||
description: 'Label shown to the selected user',
|
||||
},
|
||||
randUserTitle: {
|
||||
id: 'app.modal.randomUser.title',
|
||||
description: 'Modal title label',
|
||||
},
|
||||
reselect: {
|
||||
id: 'app.modal.randomUser.reselect.label',
|
||||
description: 'select new random user button label',
|
||||
},
|
||||
ariaModalTitle: {
|
||||
id: 'app.modal.randomUser.ariaLabel.title',
|
||||
description: 'modal title displayed to screen reader',
|
||||
},
|
||||
});
|
||||
|
||||
const propTypes = {
|
||||
intl: PropTypes.shape({
|
||||
formatMessage: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
mountModal: PropTypes.func.isRequired,
|
||||
numAvailableViewers: PropTypes.number.isRequired,
|
||||
randomUserReq: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class RandomUserSelect extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (props.currentUser.presenter) {
|
||||
props.randomUserReq();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { selectedUser, currentUser, mountModal } = this.props;
|
||||
if (selectedUser && selectedUser.userId !== currentUser.userId && !currentUser.presenter) {
|
||||
mountModal(null);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
mountModal,
|
||||
numAvailableViewers,
|
||||
randomUserReq,
|
||||
selectedUser,
|
||||
currentUser,
|
||||
clearRandomlySelectedUser,
|
||||
} = this.props;
|
||||
|
||||
if (!selectedUser) return null;
|
||||
|
||||
const isSelectedUser = currentUser.userId === selectedUser.userId;
|
||||
|
||||
const viewElement = numAvailableViewers < 1 ? (
|
||||
<div className={styles.modalViewContainer}>
|
||||
<div className={styles.modalViewTitle}>
|
||||
{intl.formatMessage(messages.randUserTitle)}
|
||||
</div>
|
||||
<div>{intl.formatMessage(messages.noViewers)}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.modalViewContainer}>
|
||||
<div className={styles.modalViewTitle}>
|
||||
{isSelectedUser
|
||||
? `${intl.formatMessage(messages.selected)}`
|
||||
: `${intl.formatMessage(messages.randUserTitle)}`
|
||||
}
|
||||
</div>
|
||||
<div aria-hidden className={styles.modalAvatar} style={{ backgroundColor: `${selectedUser.color}` }}>
|
||||
{selectedUser.name.slice(0, 2)}
|
||||
</div>
|
||||
<div className={styles.selectedUserName}>
|
||||
{selectedUser.name}
|
||||
</div>
|
||||
{!isSelectedUser
|
||||
&& (
|
||||
<Button
|
||||
label={intl.formatMessage(messages.reselect)}
|
||||
color="primary"
|
||||
size="md"
|
||||
className={styles.selectBtn}
|
||||
onClick={() => randomUserReq()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
hideBorder
|
||||
onRequestClose={() => {
|
||||
if (currentUser.presenter) clearRandomlySelectedUser();
|
||||
mountModal(null);
|
||||
}}
|
||||
contentLabel={intl.formatMessage(messages.ariaModalTitle)}
|
||||
>
|
||||
{viewElement}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RandomUserSelect.propTypes = propTypes;
|
||||
export default injectIntl(RandomUserSelect);
|
@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/api/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { withModalMounter } from '/imports/ui/components/modal/service';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import RandomUserSelect from './component';
|
||||
|
||||
const RandomUserSelectContainer = props => <RandomUserSelect {...props} />;
|
||||
|
||||
export default withModalMounter(withTracker(({ mountModal }) => {
|
||||
const viewerPool = Users.find({
|
||||
meetingId: Auth.meetingID,
|
||||
presenter: { $ne: true },
|
||||
role: { $eq: 'VIEWER' },
|
||||
}, {
|
||||
fields: {
|
||||
userId: 1,
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
const meeting = Meetings.findOne({ meetingId: Auth.meetingID }, {
|
||||
fields: {
|
||||
randomlySelectedUser: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const selectedUser = Users.findOne({
|
||||
meetingId: Auth.meetingID,
|
||||
userId: meeting.randomlySelectedUser,
|
||||
}, {
|
||||
fields: {
|
||||
userId: 1,
|
||||
avatar: 1,
|
||||
color: 1,
|
||||
name: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const currentUser = Users.findOne(
|
||||
{ userId: Auth.userID },
|
||||
{ fields: { userId: 1, presenter: 1 } },
|
||||
);
|
||||
|
||||
const randomUserReq = () => makeCall('setRandomUser');
|
||||
|
||||
const clearRandomlySelectedUser = () => makeCall('clearRandomlySelectedUser');
|
||||
|
||||
return ({
|
||||
closeModal: () => mountModal(null),
|
||||
numAvailableViewers: viewerPool.length,
|
||||
randomUserReq,
|
||||
selectedUser,
|
||||
currentUser,
|
||||
clearRandomlySelectedUser,
|
||||
});
|
||||
})(RandomUserSelectContainer));
|
@ -0,0 +1,41 @@
|
||||
.modalViewContainer {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.modalViewTitle {
|
||||
font-weight: 600;
|
||||
font-size: var(--font-size-large);
|
||||
margin-bottom: var(--md-padding-x);
|
||||
}
|
||||
|
||||
.modalAvatar {
|
||||
height: 6rem;
|
||||
width: 6rem;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
font-size: var(--font-size-xxl);
|
||||
font-weight: 400;
|
||||
margin-bottom: var(--sm-padding-x);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.selectedUserName {
|
||||
margin-bottom: var(--md-padding-x);;
|
||||
font-weight: var(--headings-font-weight);
|
||||
font-size: 2rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.selectBtn {
|
||||
margin-bottom: var(--md-padding-x);
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
--font-family-base: var(--font-family-sans-serif);
|
||||
|
||||
--font-size-base: 1rem;
|
||||
--font-size-xxl: 2.75rem;
|
||||
--font-size-xl: 1.75rem;
|
||||
--font-size-larger: 1.5rem;
|
||||
--font-size-large: 1.25rem;
|
||||
|
@ -370,6 +370,8 @@
|
||||
"app.actionsBar.actionsDropdown.captionsDesc": "Toggles captions pane",
|
||||
"app.actionsBar.actionsDropdown.takePresenter": "Take presenter",
|
||||
"app.actionsBar.actionsDropdown.takePresenterDesc": "Assign yourself as the new presenter",
|
||||
"app.actionsBar.actionsDropdown.selectRandUserLabel": "Select Random User",
|
||||
"app.actionsBar.actionsDropdown.selectRandUserDesc": "Chooses a user from available viewers at random",
|
||||
"app.actionsBar.emojiMenu.statusTriggerLabel": "Set status",
|
||||
"app.actionsBar.emojiMenu.awayLabel": "Away",
|
||||
"app.actionsBar.emojiMenu.awayDesc": "Change your status to away",
|
||||
@ -487,6 +489,11 @@
|
||||
"app.modal.confirm": "Done",
|
||||
"app.modal.newTab": "(opens new tab)",
|
||||
"app.modal.confirm.description": "Saves changes and closes the modal",
|
||||
"app.modal.randomUser.noViewers.description": "No viewers available to randomly select from",
|
||||
"app.modal.randomUser.selected.description": "You have been randomly selected",
|
||||
"app.modal.randomUser.title": "Randomly selected user",
|
||||
"app.modal.randomUser.reselect.label": "Select again",
|
||||
"app.modal.randomUser.ariaLabel.title": "Randomly selected User Modal",
|
||||
"app.dropdown.close": "Close",
|
||||
"app.dropdown.list.item.activeLabel": "Active",
|
||||
"app.error.400": "Bad Request",
|
||||
|
Loading…
Reference in New Issue
Block a user