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