Merge pull request #7908 from capilkey/improve-users-find
Improve user fetch and sort, and webcam fetch
This commit is contained in:
commit
1ea030e31d
5
bigbluebutton-html5/imports/api/voice-users/server/handlers/mutedVoiceUser.js
Normal file → Executable file
5
bigbluebutton-html5/imports/api/voice-users/server/handlers/mutedVoiceUser.js
Normal file → Executable file
@ -7,5 +7,10 @@ export default function handleVoiceUpdate({ body }, meetingId) {
|
||||
|
||||
check(meetingId, String);
|
||||
|
||||
// If a person is muted we have to force them to not talking
|
||||
if (voiceUser.muted) {
|
||||
voiceUser.talking = false;
|
||||
}
|
||||
|
||||
return updateVoiceUser(meetingId, voiceUser);
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ export default withModalMounter(withTracker(() => {
|
||||
data.children = <ScreenshareContainer />;
|
||||
}
|
||||
|
||||
const usersVideo = VideoService.getAllUsersVideo();
|
||||
const usersVideo = VideoService.getAllWebcamUsers();
|
||||
data.usersVideo = usersVideo;
|
||||
|
||||
if (MediaService.shouldShowOverlay() && usersVideo.length && viewParticipantsWebcams) {
|
||||
|
@ -59,7 +59,7 @@ const toggleSwapLayout = () => {
|
||||
|
||||
export const shouldEnableSwapLayout = () => {
|
||||
const { viewParticipantsWebcams } = Settings.dataSaving;
|
||||
const usersVideo = VideoService.getAllUsersVideo();
|
||||
const usersVideo = VideoService.getAllWebcamUsers();
|
||||
const poll = PollingService.mapPolls();
|
||||
|
||||
return usersVideo.length > 0 // prevent swap without any webcams
|
||||
|
@ -14,7 +14,6 @@ const propTypes = {
|
||||
}).isRequired,
|
||||
CustomLogoUrl: PropTypes.string.isRequired,
|
||||
handleEmojiChange: PropTypes.func.isRequired,
|
||||
getUsersId: PropTypes.func.isRequired,
|
||||
isBreakoutRoom: PropTypes.bool,
|
||||
getAvailableActions: PropTypes.func.isRequired,
|
||||
normalizeEmojiName: PropTypes.func.isRequired,
|
||||
@ -65,7 +64,6 @@ class UserList extends PureComponent {
|
||||
getEmoji,
|
||||
showBranding,
|
||||
hasBreakoutRoom,
|
||||
getUsersId,
|
||||
hasPrivateChatBetweenUsers,
|
||||
toggleUserLock,
|
||||
requestUserInformation,
|
||||
@ -102,7 +100,6 @@ class UserList extends PureComponent {
|
||||
getEmojiList,
|
||||
getEmoji,
|
||||
hasBreakoutRoom,
|
||||
getUsersId,
|
||||
hasPrivateChatBetweenUsers,
|
||||
toggleUserLock,
|
||||
requestUserInformation,
|
||||
|
@ -8,7 +8,6 @@ import UserList from './component';
|
||||
|
||||
const propTypes = {
|
||||
activeChats: PropTypes.arrayOf(String).isRequired,
|
||||
getUsersId: PropTypes.func.isRequired,
|
||||
isBreakoutRoom: PropTypes.bool.isRequired,
|
||||
getAvailableActions: PropTypes.func.isRequired,
|
||||
normalizeEmojiName: PropTypes.func.isRequired,
|
||||
@ -33,7 +32,6 @@ UserListContainer.propTypes = propTypes;
|
||||
|
||||
export default withTracker(({ chatID, compact }) => ({
|
||||
hasBreakoutRoom: Service.hasBreakoutRoom(),
|
||||
getUsersId: Service.getUsersId,
|
||||
activeChats: Service.getActiveChats(chatID),
|
||||
isBreakoutRoom: meetingIsBreakout(),
|
||||
getAvailableActions: Service.getAvailableActions,
|
||||
|
@ -18,6 +18,8 @@ const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
const DIAL_IN_CLIENT_TYPE = 'dial-in-user';
|
||||
|
||||
// session for closed chat list
|
||||
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
||||
|
||||
@ -42,13 +44,16 @@ export const setCustomLogoUrl = path => Storage.setItem(CUSTOM_LOGO_URL_KEY, pat
|
||||
const getCustomLogoUrl = () => Storage.getItem(CUSTOM_LOGO_URL_KEY);
|
||||
|
||||
const sortUsersByName = (a, b) => {
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
||||
const aName = a.name.toLowerCase();
|
||||
const bName = b.name.toLowerCase();
|
||||
|
||||
if (aName < bName) {
|
||||
return -1;
|
||||
} if (a.name.toLowerCase() > b.name.toLowerCase()) {
|
||||
} if (aName > bName) {
|
||||
return 1;
|
||||
} if (a.id.toLowerCase() > b.id.toLowerCase()) {
|
||||
} if (a.userId > b.userId) {
|
||||
return -1;
|
||||
} if (a.id.toLowerCase() < b.id.toLowerCase()) {
|
||||
} if (a.userId < b.userId) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -56,32 +61,26 @@ const sortUsersByName = (a, b) => {
|
||||
};
|
||||
|
||||
const sortUsersByEmoji = (a, b) => {
|
||||
const { status: statusA } = a.emoji;
|
||||
const { status: statusB } = b.emoji;
|
||||
|
||||
const emojiA = statusA in EMOJI_STATUSES ? EMOJI_STATUSES[statusA] : statusA;
|
||||
const emojiB = statusB in EMOJI_STATUSES ? EMOJI_STATUSES[statusB] : statusB;
|
||||
|
||||
if (emojiA && emojiB && (emojiA !== 'none' && emojiB !== 'none')) {
|
||||
if (a.emoji.changedAt < b.emoji.changedAt) {
|
||||
if (a.emoji && b.emoji && (a.emoji !== 'none' && b.emoji !== 'none')) {
|
||||
if (a.emojiTime < b.emojiTime) {
|
||||
return -1;
|
||||
} if (a.emoji.changedAt > b.emoji.changedAt) {
|
||||
} if (a.emojiTime > b.emojiTime) {
|
||||
return 1;
|
||||
}
|
||||
} if (emojiA && emojiA !== 'none') {
|
||||
} if (a.emoji && a.emoji !== 'none') {
|
||||
return -1;
|
||||
} if (emojiB && emojiB !== 'none') {
|
||||
} if (b.emoji && b.emoji !== 'none') {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const sortUsersByModerator = (a, b) => {
|
||||
if (a.isModerator && b.isModerator) {
|
||||
return sortUsersByEmoji(a, b);
|
||||
} if (a.isModerator) {
|
||||
if (a.role === ROLE_MODERATOR && b.role === ROLE_MODERATOR) {
|
||||
return 0;
|
||||
} if (a.role === ROLE_MODERATOR) {
|
||||
return -1;
|
||||
} if (b.isModerator) {
|
||||
} if (b.role === ROLE_MODERATOR) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -89,11 +88,11 @@ const sortUsersByModerator = (a, b) => {
|
||||
};
|
||||
|
||||
const sortUsersByPhoneUser = (a, b) => {
|
||||
if (!a.isPhoneUser && !b.isPhoneUser) {
|
||||
if (!a.clientType === DIAL_IN_CLIENT_TYPE && !b.clientType === DIAL_IN_CLIENT_TYPE) {
|
||||
return 0;
|
||||
} if (!a.isPhoneUser) {
|
||||
} if (!a.clientType === DIAL_IN_CLIENT_TYPE) {
|
||||
return -1;
|
||||
} if (!b.isPhoneUser) {
|
||||
} if (!b.clientType === DIAL_IN_CLIENT_TYPE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -102,9 +101,9 @@ const sortUsersByPhoneUser = (a, b) => {
|
||||
|
||||
// current user's name is always on top
|
||||
const sortUsersByCurrent = (a, b) => {
|
||||
if (a.isCurrent) {
|
||||
if (a.userId === Auth.userID) {
|
||||
return -1;
|
||||
} if (b.isCurrent) {
|
||||
} if (b.userId === Auth.userID) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -189,13 +188,9 @@ const getUsers = () => {
|
||||
}, userFindSorting)
|
||||
.fetch();
|
||||
|
||||
return users
|
||||
.map(mapUser)
|
||||
.sort(sortUsers);
|
||||
return users.sort(sortUsers);
|
||||
};
|
||||
|
||||
const getUsersId = () => getUsers().map(u => u.id);
|
||||
|
||||
const hasBreakoutRoom = () => Breakouts.find({ parentMeetingId: Auth.meetingID }).count() > 0;
|
||||
|
||||
const getActiveChats = (chatID) => {
|
||||
@ -481,6 +476,7 @@ const requestUserInformation = (userId) => {
|
||||
};
|
||||
|
||||
export default {
|
||||
sortUsers,
|
||||
setEmojiStatus,
|
||||
assignPresenter,
|
||||
removeUser,
|
||||
@ -489,7 +485,6 @@ export default {
|
||||
muteAllExceptPresenter,
|
||||
changeRole,
|
||||
getUsers,
|
||||
getUsersId,
|
||||
getActiveChats,
|
||||
getCurrentUser,
|
||||
getAvailableActions,
|
||||
|
3
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx
Normal file → Executable file
3
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/component.jsx
Normal file → Executable file
@ -31,7 +31,6 @@ const propTypes = {
|
||||
roving: PropTypes.func.isRequired,
|
||||
getGroupChatPrivate: PropTypes.func.isRequired,
|
||||
handleEmojiChange: PropTypes.func.isRequired,
|
||||
getUsersId: PropTypes.func.isRequired,
|
||||
pollIsOpen: PropTypes.bool.isRequired,
|
||||
forcePollOpen: PropTypes.bool.isRequired,
|
||||
toggleUserLock: PropTypes.func.isRequired,
|
||||
@ -72,7 +71,6 @@ class UserContent extends PureComponent {
|
||||
pollIsOpen,
|
||||
forcePollOpen,
|
||||
hasBreakoutRoom,
|
||||
getUsersId,
|
||||
hasPrivateChatBetweenUsers,
|
||||
toggleUserLock,
|
||||
pendingUsers,
|
||||
@ -150,7 +148,6 @@ class UserContent extends PureComponent {
|
||||
getEmojiList,
|
||||
getEmoji,
|
||||
getGroupChatPrivate,
|
||||
getUsersId,
|
||||
hasPrivateChatBetweenUsers,
|
||||
toggleUserLock,
|
||||
requestUserInformation,
|
||||
|
@ -15,10 +15,9 @@ const propTypes = {
|
||||
}).isRequired,
|
||||
currentUser: PropTypes.shape({}).isRequired,
|
||||
meeting: PropTypes.shape({}).isRequired,
|
||||
users: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
users: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||
getGroupChatPrivate: PropTypes.func.isRequired,
|
||||
handleEmojiChange: PropTypes.func.isRequired,
|
||||
getUsersId: PropTypes.func.isRequired,
|
||||
isBreakoutRoom: PropTypes.bool,
|
||||
setEmojiStatus: PropTypes.func.isRequired,
|
||||
assignPresenter: PropTypes.func.isRequired,
|
||||
@ -145,7 +144,7 @@ class UserParticipants extends Component {
|
||||
timeout={0}
|
||||
component="div"
|
||||
className={cx(styles.participantsList)}
|
||||
key={u}
|
||||
key={u.userId}
|
||||
>
|
||||
<div ref={(node) => { this.userRefs[index += 1] = node; }}>
|
||||
<UserListItemContainer
|
||||
@ -170,7 +169,7 @@ class UserParticipants extends Component {
|
||||
requestUserInformation,
|
||||
currentUser,
|
||||
}}
|
||||
userId={u}
|
||||
user={u}
|
||||
getScrollContainerRef={this.getScrollContainerRef}
|
||||
/>
|
||||
</div>
|
||||
|
5
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/container.jsx
Normal file → Executable file
5
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/container.jsx
Normal file → Executable file
@ -1,11 +1,12 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import UserParticipants from './component';
|
||||
|
||||
const UserParticipantsContainer = props => <UserParticipants {...props} />;
|
||||
|
||||
export default withTracker(({ getUsersId }) => ({
|
||||
export default withTracker(() => ({
|
||||
meeting: Meetings.findOne({}),
|
||||
users: getUsersId(),
|
||||
users: UserListService.getUsers(),
|
||||
}))(UserParticipantsContainer);
|
||||
|
7
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
Normal file → Executable file
7
bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
Normal file → Executable file
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Users from '/imports/api/users';
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
@ -9,12 +8,12 @@ import UserListItem from './component';
|
||||
|
||||
const UserListItemContainer = props => <UserListItem {...props} />;
|
||||
|
||||
export default withTracker(({ userId }) => {
|
||||
const findUserInBreakout = Breakouts.findOne({ 'joinedUsers.userId': new RegExp(`^${userId}`) });
|
||||
export default withTracker(({ user }) => {
|
||||
const findUserInBreakout = Breakouts.findOne({ 'joinedUsers.userId': new RegExp(`^${user.userId}`) });
|
||||
const breakoutSequence = (findUserInBreakout || {}).sequence;
|
||||
const Meeting = Meetings.findOne({ MeetingId: Auth.meetingID }, { fields: { meetingProp: 1 } });
|
||||
return {
|
||||
user: mapUser(Users.findOne({ userId })),
|
||||
user: mapUser(user),
|
||||
userInBreakout: !!findUserInBreakout,
|
||||
breakoutSequence,
|
||||
meetignIsBreakout: Meeting && Meeting.meetingProp.isBreakout,
|
||||
|
@ -244,7 +244,7 @@ class VideoProvider extends Component {
|
||||
}
|
||||
|
||||
componentWillUpdate({ users, userId }) {
|
||||
const usersSharingIds = users.map(u => u.id);
|
||||
const usersSharingIds = users.map(u => u.userId);
|
||||
const usersConnected = Object.keys(this.webRtcPeers);
|
||||
|
||||
const usersToConnect = usersSharingIds.filter(id => !usersConnected.includes(id));
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import VideoProvider from './component';
|
||||
import VideoService from './service';
|
||||
|
||||
@ -9,16 +10,13 @@ const VideoProviderContainer = ({ children, ...props }) => {
|
||||
return (!users.length ? null : <VideoProvider {...props}>{children}</VideoProvider>);
|
||||
};
|
||||
|
||||
export default withTracker((props) => {
|
||||
return {
|
||||
cursor: props.cursor,
|
||||
swapLayout: props.swapLayout,
|
||||
meetingId: VideoService.meetingId(),
|
||||
users: VideoService.getAllUsersVideo(),
|
||||
userId: VideoService.userId(),
|
||||
sessionToken: VideoService.sessionToken(),
|
||||
userName: VideoService.userName(),
|
||||
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
|
||||
voiceBridge: VideoService.voiceBridge(),
|
||||
};
|
||||
})(VideoProviderContainer);
|
||||
export default withTracker(props => ({
|
||||
cursor: props.cursor,
|
||||
swapLayout: props.swapLayout,
|
||||
meetingId: VideoService.meetingId(),
|
||||
users: VideoService.getAllWebcamUsers(),
|
||||
userId: Auth.userID,
|
||||
sessionToken: VideoService.sessionToken(),
|
||||
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
|
||||
voiceBridge: VideoService.voiceBridge(),
|
||||
}))(VideoProviderContainer);
|
||||
|
@ -3,9 +3,11 @@ import { makeCall } from '/imports/ui/services/api';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Meetings from '/imports/api/meetings/';
|
||||
import Users from '/imports/api/users/';
|
||||
import mapUser from '/imports/ui/services/user/mapUser';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
|
||||
|
||||
class VideoService {
|
||||
constructor() {
|
||||
this.defineProperties({
|
||||
@ -70,40 +72,47 @@ class VideoService {
|
||||
makeCall('userUnshareWebcam', stream);
|
||||
}
|
||||
|
||||
getAllUsers() {
|
||||
// Use the same function as the user-list to share the sorting/mapping
|
||||
return UserListService.getUsers();
|
||||
}
|
||||
|
||||
getAllUsersVideo() {
|
||||
const userId = this.userId();
|
||||
const isLocked = this.isLocked();
|
||||
const currentUser = Users.findOne({ userId });
|
||||
const currentUserIsModerator = mapUser(currentUser).isModerator;
|
||||
getAllWebcamUsers() {
|
||||
const webcamsLocked = this.webcamsLocked();
|
||||
const webcamsOnlyForModerator = this.webcamsOnlyForModerator();
|
||||
const currentUser = Users.findOne({ userId: Auth.userID });
|
||||
const currentUserIsViewer = currentUser.role === ROLE_VIEWER;
|
||||
const sharedWebcam = this.isSharing;
|
||||
|
||||
const isSharingWebcam = user => user.isSharingWebcam || (sharedWebcam && user.isCurrent);
|
||||
const isNotLocked = user => !(isLocked && user.isLocked);
|
||||
let users = Users
|
||||
.find({
|
||||
meetingId: Auth.meetingID,
|
||||
connectionStatus: 'online',
|
||||
hasStream: true,
|
||||
userId: { $ne: Auth.userID },
|
||||
})
|
||||
.fetch();
|
||||
|
||||
const isWebcamOnlyModerator = this.webcamOnlyModerator();
|
||||
const allowedSeeViewersWebcams = !isWebcamOnlyModerator || currentUserIsModerator;
|
||||
const webcamOnlyModerator = (user) => {
|
||||
if (allowedSeeViewersWebcams) return true;
|
||||
return user.isModerator || user.isCurrent;
|
||||
};
|
||||
const userIsNotLocked = user => user.role === ROLE_MODERATOR || !user.locked;
|
||||
|
||||
return this.getAllUsers()
|
||||
.filter(isSharingWebcam)
|
||||
.filter(isNotLocked)
|
||||
.filter(webcamOnlyModerator);
|
||||
if (webcamsLocked) {
|
||||
users = users.filter(userIsNotLocked);
|
||||
}
|
||||
|
||||
const userIsModerator = user => user.role === ROLE_MODERATOR;
|
||||
|
||||
if (webcamsOnlyForModerator && currentUserIsViewer) {
|
||||
users = users.filter(userIsModerator);
|
||||
}
|
||||
|
||||
if (sharedWebcam) {
|
||||
users.unshift(currentUser);
|
||||
}
|
||||
|
||||
return users.sort(UserListService.sortUsers);
|
||||
}
|
||||
|
||||
webcamOnlyModerator() {
|
||||
webcamsOnlyForModerator() {
|
||||
const m = Meetings.findOne({ meetingId: Auth.meetingID }) || {};
|
||||
return m.usersProp ? m.usersProp.webcamsOnlyForModerator : false;
|
||||
}
|
||||
|
||||
isLocked() {
|
||||
webcamsLocked() {
|
||||
const m = Meetings.findOne({ meetingId: Auth.meetingID }) || {};
|
||||
return m.lockSettingsProps ? m.lockSettingsProps.disableCam : false;
|
||||
}
|
||||
@ -145,9 +154,8 @@ export default {
|
||||
exitVideo: () => videoService.exitVideo(),
|
||||
exitingVideo: () => videoService.exitingVideo(),
|
||||
exitedVideo: () => videoService.exitedVideo(),
|
||||
getAllUsers: () => videoService.getAllUsers(),
|
||||
webcamsLocked: () => videoService.webcamsLocked(),
|
||||
webcamOnlyModerator: () => videoService.webcamOnlyModerator(),
|
||||
isLocked: () => videoService.isLocked(),
|
||||
isSharing: () => videoService.isSharing,
|
||||
isConnected: () => videoService.isConnected,
|
||||
isWaitingResponse: () => videoService.isWaitingResponse,
|
||||
@ -156,10 +164,9 @@ export default {
|
||||
joinedVideo: () => videoService.joinedVideo(),
|
||||
sendUserShareWebcam: stream => videoService.sendUserShareWebcam(stream),
|
||||
sendUserUnshareWebcam: stream => videoService.sendUserUnshareWebcam(stream),
|
||||
userId: () => videoService.userId(),
|
||||
userName: () => videoService.userName(),
|
||||
meetingId: () => videoService.meetingId(),
|
||||
getAllUsersVideo: () => videoService.getAllUsersVideo(),
|
||||
getAllWebcamUsers: () => videoService.getAllWebcamUsers(),
|
||||
sessionToken: () => videoService.sessionToken(),
|
||||
voiceBridge: () => videoService.voiceBridge(),
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ const isDisabled = () => {
|
||||
const isWaitingResponse = VideoService.isWaitingResponse();
|
||||
const isConnected = VideoService.isConnected();
|
||||
|
||||
const lockCam = VideoService.isLocked();
|
||||
const lockCam = VideoService.webcamsLocked();
|
||||
const user = Users.findOne({ userId: Auth.userID });
|
||||
const userLocked = mapUser(user).isLocked;
|
||||
|
||||
|
18
bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
Normal file → Executable file
18
bigbluebutton-html5/imports/ui/components/video-provider/video-list/component.jsx
Normal file → Executable file
@ -4,7 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
||||
import cx from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import { styles } from './styles';
|
||||
import VideoListItem from './video-list-item/component';
|
||||
import VideoListItemContainer from './video-list-item/container';
|
||||
import { withDraggableConsumer } from '../../media/webcam-draggable-overlay/context';
|
||||
|
||||
const propTypes = {
|
||||
@ -158,7 +158,7 @@ class VideoList extends Component {
|
||||
const { focusedId } = this.state;
|
||||
|
||||
return users.map((user) => {
|
||||
const isFocused = focusedId === user.id;
|
||||
const isFocused = focusedId === user.userId;
|
||||
const isFocusedIntlKey = !isFocused ? 'focus' : 'unfocus';
|
||||
let actions = [];
|
||||
|
||||
@ -166,28 +166,28 @@ class VideoList extends Component {
|
||||
actions = [{
|
||||
label: intl.formatMessage(intlMessages[`${isFocusedIntlKey}Label`]),
|
||||
description: intl.formatMessage(intlMessages[`${isFocusedIntlKey}Desc`]),
|
||||
onClick: () => this.handleVideoFocus(user.id),
|
||||
onClick: () => this.handleVideoFocus(user.userId),
|
||||
}];
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={user.id}
|
||||
key={user.userId}
|
||||
className={cx({
|
||||
[styles.videoListItem]: true,
|
||||
[styles.focused]: focusedId === user.id && users.length > 2,
|
||||
[styles.focused]: focusedId === user.userId && users.length > 2,
|
||||
})}
|
||||
>
|
||||
<VideoListItem
|
||||
<VideoListItemContainer
|
||||
numOfUsers={users.length}
|
||||
user={user}
|
||||
actions={actions}
|
||||
onMount={(videoRef) => {
|
||||
this.handleCanvasResize();
|
||||
onMount(user.id, videoRef);
|
||||
onMount(user.userId, videoRef);
|
||||
}}
|
||||
getStats={(videoRef, callback) => getStats(user.id, videoRef, callback)}
|
||||
stopGettingStats={() => stopGettingStats(user.id)}
|
||||
getStats={(videoRef, callback) => getStats(user.userId, videoRef, callback)}
|
||||
stopGettingStats={() => stopGettingStats(user.userId)}
|
||||
enableVideoStats={enableVideoStats}
|
||||
/>
|
||||
</div>
|
||||
|
@ -127,11 +127,11 @@ class VideoListItem extends Component {
|
||||
return _.compact([
|
||||
<DropdownListTitle className={styles.hiddenDesktop} key="name">{user.name}</DropdownListTitle>,
|
||||
<DropdownListSeparator className={styles.hiddenDesktop} key="sep" />,
|
||||
...actions.map(action => (<DropdownListItem key={user.id} {...action} />)),
|
||||
...actions.map(action => (<DropdownListItem key={user.userId} {...action} />)),
|
||||
(enableVideoStats
|
||||
? (
|
||||
<DropdownListItem
|
||||
key={`list-item-stats-${user.id}`}
|
||||
key={`list-item-stats-${user.userId}`}
|
||||
onClick={() => { this.toggleStats(); }}
|
||||
label={intl.formatMessage(intlMessages.connectionStatsLabel)}
|
||||
/>
|
||||
@ -176,6 +176,7 @@ class VideoListItem extends Component {
|
||||
} = this.state;
|
||||
const {
|
||||
user,
|
||||
voiceUser,
|
||||
numOfUsers,
|
||||
webcamDraggableState,
|
||||
} = this.props;
|
||||
@ -188,7 +189,7 @@ class VideoListItem extends Component {
|
||||
return (
|
||||
<div className={cx({
|
||||
[styles.content]: true,
|
||||
[styles.talking]: user.isTalking,
|
||||
[styles.talking]: voiceUser.talking,
|
||||
})}
|
||||
>
|
||||
{
|
||||
@ -244,8 +245,8 @@ class VideoListItem extends Component {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{user.isMuted ? <Icon className={styles.muted} iconName="unmute_filled" /> : null}
|
||||
{user.isListenOnly ? <Icon className={styles.voice} iconName="listen" /> : null}
|
||||
{voiceUser.muted ? <Icon className={styles.muted} iconName="unmute_filled" /> : null}
|
||||
{voiceUser.listenOnly ? <Icon className={styles.voice} iconName="listen" /> : null}
|
||||
</div>
|
||||
{
|
||||
showStats
|
||||
|
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import VoiceUsers from '/imports/api/voice-users/';
|
||||
import VideoListItem from './component';
|
||||
|
||||
const VideoListItemContainer = props => (
|
||||
<VideoListItem {...props} />
|
||||
);
|
||||
|
||||
export default withTracker((props) => {
|
||||
const {
|
||||
user,
|
||||
} = props;
|
||||
|
||||
return {
|
||||
voiceUser: VoiceUsers.findOne({ intId: user.userId }),
|
||||
};
|
||||
})(VideoListItemContainer);
|
Loading…
Reference in New Issue
Block a user