2022-04-07 21:42:26 +08:00
|
|
|
import React from 'react';
|
2021-10-20 04:35:39 +08:00
|
|
|
import Users from '/imports/api/users';
|
2019-08-22 23:19:54 +08:00
|
|
|
import VoiceUsers from '/imports/api/voice-users';
|
2021-10-20 04:35:39 +08:00
|
|
|
import Breakouts from '/imports/api/breakouts';
|
|
|
|
import Meetings from '/imports/api/meetings';
|
2016-07-07 20:50:32 +08:00
|
|
|
import Auth from '/imports/ui/services/auth';
|
2017-03-01 06:40:16 +08:00
|
|
|
import Storage from '/imports/ui/services/storage/session';
|
2017-11-21 18:50:20 +08:00
|
|
|
import { EMOJI_STATUSES } from '/imports/utils/statuses';
|
2017-12-06 03:13:11 +08:00
|
|
|
import KEY_CODES from '/imports/utils/keyCodes';
|
2019-07-26 02:41:24 +08:00
|
|
|
import AudioService from '/imports/ui/components/audio/service';
|
|
|
|
import logger from '/imports/startup/client/logger';
|
2021-05-21 20:17:24 +08:00
|
|
|
import { Session } from 'meteor/session';
|
2024-05-29 21:26:11 +08:00
|
|
|
import { getSettingsSingletonInstance } from '/imports/ui/services/settings';
|
2022-04-06 21:21:49 +08:00
|
|
|
import { notify } from '/imports/ui/services/notification';
|
|
|
|
import { FormattedMessage } from 'react-intl';
|
2022-05-20 23:00:28 +08:00
|
|
|
import { getDateString } from '/imports/utils/string-utils';
|
2023-12-07 21:45:13 +08:00
|
|
|
import { isEmpty } from 'radash';
|
2016-06-07 00:45:30 +08:00
|
|
|
|
2019-08-10 06:28:49 +08:00
|
|
|
const DIAL_IN_CLIENT_TYPE = 'dial-in-user';
|
|
|
|
|
2017-03-17 23:27:37 +08:00
|
|
|
// session for closed chat list
|
|
|
|
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
2021-05-20 01:46:04 +08:00
|
|
|
// session for chats the current user started
|
|
|
|
const STARTED_CHAT_LIST_KEY = 'startedChatList';
|
2017-03-17 23:27:37 +08:00
|
|
|
|
2018-03-21 03:35:28 +08:00
|
|
|
const CUSTOM_LOGO_URL_KEY = 'CustomLogoUrl';
|
|
|
|
|
2021-12-08 00:22:11 +08:00
|
|
|
export const setCustomLogoUrl = (path) => Storage.setItem(CUSTOM_LOGO_URL_KEY, path);
|
2018-03-21 03:35:28 +08:00
|
|
|
|
2021-12-08 00:22:11 +08:00
|
|
|
export const setModeratorOnlyMessage = (msg) => Storage.setItem('ModeratorOnlyMessage', msg);
|
2020-05-21 03:54:08 +08:00
|
|
|
|
2018-03-21 03:35:28 +08:00
|
|
|
const getCustomLogoUrl = () => Storage.getItem(CUSTOM_LOGO_URL_KEY);
|
|
|
|
|
2020-08-18 23:18:20 +08:00
|
|
|
const sortByWhiteboardAccess = (a, b) => {
|
|
|
|
const _a = a.whiteboardAccess;
|
|
|
|
const _b = b.whiteboardAccess;
|
|
|
|
if (!_b && _a) return -1;
|
|
|
|
if (!_a && _b) return 1;
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2021-04-09 04:16:36 +08:00
|
|
|
const sortUsersByUserId = (a, b) => {
|
|
|
|
if (a.userId > b.userId) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return -1;
|
2019-08-10 06:28:49 +08:00
|
|
|
} if (a.userId < b.userId) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return 1;
|
2016-06-07 21:58:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2021-04-09 04:16:36 +08:00
|
|
|
const sortUsersByName = (a, b) => {
|
2024-04-17 20:27:10 +08:00
|
|
|
const aName = a.sortName || a.nameSortable || '';
|
|
|
|
const bName = b.sortName || b.nameSortable || '';
|
2021-04-09 04:16:36 +08:00
|
|
|
|
|
|
|
// Extending for sorting strings with non-ASCII characters
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#sorting_non-ascii_characters
|
|
|
|
return aName.localeCompare(bName);
|
|
|
|
};
|
|
|
|
|
2023-06-08 09:10:07 +08:00
|
|
|
const sortByPropTime = (propName, propTimeName, nullValue, a, b) => {
|
|
|
|
const aObjTime = a[propName] && a[propName] !== nullValue && a[propTimeName]
|
|
|
|
? a[propTimeName] : Number.MAX_SAFE_INTEGER;
|
|
|
|
|
|
|
|
const bObjTime = b[propName] && b[propName] !== nullValue && b[propTimeName]
|
|
|
|
? b[propTimeName] : Number.MAX_SAFE_INTEGER;
|
|
|
|
|
|
|
|
if (aObjTime < bObjTime) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return -1;
|
2023-06-08 09:10:07 +08:00
|
|
|
} if (aObjTime > bObjTime) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2016-06-07 21:58:51 +08:00
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2023-06-08 09:10:07 +08:00
|
|
|
const sortUsersByEmoji = (a, b) => sortByPropTime('emoji', 'emojiTime', 'none', a, b);
|
|
|
|
const sortUsersByAway = (a, b) => sortByPropTime('away', 'awayTime', false, a, b);
|
|
|
|
const sortUsersByRaiseHand = (a, b) => sortByPropTime('raiseHand', 'raiseHandTime', false, a, b);
|
|
|
|
const sortUsersByReaction = (a, b) => sortByPropTime('reaction', 'reactionTime', 'none', a, b);
|
|
|
|
|
2016-06-07 21:58:51 +08:00
|
|
|
const sortUsersByModerator = (a, b) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const ROLE_MODERATOR = window.meetingClientSettings.public.user.role_moderator;
|
|
|
|
|
2019-08-10 06:28:49 +08:00
|
|
|
if (a.role === ROLE_MODERATOR && b.role === ROLE_MODERATOR) {
|
|
|
|
return 0;
|
|
|
|
} if (a.role === ROLE_MODERATOR) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return -1;
|
2019-08-10 06:28:49 +08:00
|
|
|
} if (b.role === ROLE_MODERATOR) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return 1;
|
2016-06-07 21:58:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
const sortUsersByPhoneUser = (a, b) => {
|
2019-08-10 06:28:49 +08:00
|
|
|
if (!a.clientType === DIAL_IN_CLIENT_TYPE && !b.clientType === DIAL_IN_CLIENT_TYPE) {
|
2017-12-12 23:03:33 +08:00
|
|
|
return 0;
|
2019-08-10 06:28:49 +08:00
|
|
|
} if (!a.clientType === DIAL_IN_CLIENT_TYPE) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return -1;
|
2019-08-10 06:28:49 +08:00
|
|
|
} if (!b.clientType === DIAL_IN_CLIENT_TYPE) {
|
2016-06-07 00:45:30 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2018-07-05 01:27:15 +08:00
|
|
|
// current user's name is always on top
|
|
|
|
const sortUsersByCurrent = (a, b) => {
|
2019-08-10 06:28:49 +08:00
|
|
|
if (a.userId === Auth.userID) {
|
2018-07-05 01:48:58 +08:00
|
|
|
return -1;
|
2019-08-10 06:28:49 +08:00
|
|
|
} if (b.userId === Auth.userID) {
|
2018-07-05 01:51:59 +08:00
|
|
|
return 1;
|
2018-07-05 01:27:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2016-06-07 21:58:51 +08:00
|
|
|
const sortUsers = (a, b) => {
|
2018-07-05 01:27:15 +08:00
|
|
|
let sort = sortUsersByCurrent(a, b);
|
2023-06-08 09:10:07 +08:00
|
|
|
if (sort === 0) sort = sortUsersByModerator(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByRaiseHand(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByAway(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByReaction(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByEmoji(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByPhoneUser(a, b);
|
|
|
|
if (sort === 0) sort = sortByWhiteboardAccess(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByName(a, b);
|
|
|
|
if (sort === 0) sort = sortUsersByUserId(a, b);
|
2021-04-09 04:16:36 +08:00
|
|
|
|
2016-06-07 21:58:51 +08:00
|
|
|
return sort;
|
|
|
|
};
|
|
|
|
|
2024-05-29 21:26:11 +08:00
|
|
|
const isPublicChat = (chat) => {
|
|
|
|
const CHAT_CONFIG = window.meetingClientSettings.public.chat;
|
|
|
|
return chat.userId === CHAT_CONFIG.public_id;
|
|
|
|
};
|
2017-09-22 22:24:24 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const getUserCount = () => Users.find({ meetingId: Auth.meetingID }).count();
|
2016-05-20 02:22:56 +08:00
|
|
|
|
2019-08-22 20:05:06 +08:00
|
|
|
const hasBreakoutRoom = () => Breakouts.find({ parentMeetingId: Auth.meetingID },
|
|
|
|
{ fields: {} }).count() > 0;
|
2018-10-02 21:48:12 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const isMe = (userId) => userId === Auth.userID;
|
2018-08-01 01:13:36 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
const getActiveChats = ({ groupChatsMessages, groupChats, users }) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const PUBLIC_GROUP_CHAT_ID = window.meetingClientSettings.public.chat.public_group_id;
|
|
|
|
const PUBLIC_CHAT_ID = window.meetingClientSettings.public.chat.public_id;
|
|
|
|
|
2023-02-23 21:27:16 +08:00
|
|
|
if (isEmpty(groupChats) && isEmpty(users)) return [];
|
2021-05-18 04:25:07 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
const chatIds = Object.keys(groupChats);
|
|
|
|
const lastTimeWindows = chatIds.reduce((acc, chatId) => {
|
|
|
|
const chat = groupChatsMessages[chatId];
|
|
|
|
const lastTimewindowKey = chat?.lastTimewindow;
|
|
|
|
const lastTimeWindow = lastTimewindowKey?.split('-')[1];
|
|
|
|
return {
|
|
|
|
...acc,
|
|
|
|
chatId: lastTimeWindow,
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2021-02-02 06:12:04 +08:00
|
|
|
}, {});
|
2017-03-01 06:40:16 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
chatIds.sort((a, b) => {
|
2021-02-02 06:12:04 +08:00
|
|
|
if (a === PUBLIC_GROUP_CHAT_ID) {
|
|
|
|
return -1;
|
|
|
|
}
|
2021-05-18 04:25:07 +08:00
|
|
|
|
|
|
|
if (lastTimeWindows[a] === lastTimeWindows[b]) {
|
2021-02-02 06:12:04 +08:00
|
|
|
return 0;
|
2017-03-01 06:40:16 +08:00
|
|
|
}
|
2021-05-18 04:25:07 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
return 1;
|
2017-03-03 04:33:49 +08:00
|
|
|
});
|
2021-05-18 04:25:07 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
const chatInfo = chatIds.map((chatId) => {
|
|
|
|
const contextChat = groupChatsMessages[chatId];
|
2021-05-22 01:52:08 +08:00
|
|
|
const isPublicChatId = chatId === PUBLIC_GROUP_CHAT_ID;
|
2021-02-02 06:12:04 +08:00
|
|
|
let unreadMessagesCount = 0;
|
|
|
|
if (contextChat) {
|
|
|
|
const unreadTimewindows = contextChat.unreadTimeWindows;
|
2021-05-18 04:25:07 +08:00
|
|
|
// eslint-disable-next-line
|
2021-02-02 06:12:04 +08:00
|
|
|
for (const unreadTimeWindowId of unreadTimewindows) {
|
2021-05-22 01:52:08 +08:00
|
|
|
const timeWindow = (isPublicChatId
|
2021-05-18 04:25:07 +08:00
|
|
|
? contextChat?.preJoinMessages[unreadTimeWindowId]
|
|
|
|
|| contextChat?.posJoinMessages[unreadTimeWindowId]
|
2021-03-18 01:49:44 +08:00
|
|
|
: contextChat?.messageGroups[unreadTimeWindowId]);
|
2021-02-02 06:12:04 +08:00
|
|
|
unreadMessagesCount += timeWindow.content.length;
|
|
|
|
}
|
|
|
|
}
|
2017-03-03 04:33:49 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
if (chatId !== PUBLIC_GROUP_CHAT_ID) {
|
|
|
|
const groupChatsParticipants = groupChats[chatId].participants;
|
2021-05-22 01:56:10 +08:00
|
|
|
const otherParticipant = groupChatsParticipants.filter((user) => user.id !== Auth.userID)[0];
|
2021-05-13 03:33:20 +08:00
|
|
|
const user = users[otherParticipant.id];
|
2021-05-21 20:17:24 +08:00
|
|
|
const startedChats = Session.get(STARTED_CHAT_LIST_KEY) || [];
|
2020-08-20 00:22:43 +08:00
|
|
|
|
2024-05-29 21:26:11 +08:00
|
|
|
const ROLE_MODERATOR = window.meetingClientSettings.public.user.role_moderator;
|
|
|
|
|
2021-05-13 03:33:20 +08:00
|
|
|
return {
|
|
|
|
color: user?.color || '#7b1fa2',
|
|
|
|
isModerator: user?.role === ROLE_MODERATOR,
|
|
|
|
name: user?.name || otherParticipant.name,
|
|
|
|
avatar: user?.avatar,
|
|
|
|
chatId,
|
|
|
|
unreadCounter: unreadMessagesCount,
|
|
|
|
userId: user?.userId || otherParticipant.id,
|
2021-05-22 01:56:10 +08:00
|
|
|
shouldDisplayInChatList: groupChats[chatId].createdBy === Auth.userID
|
|
|
|
|| startedChats.includes(chatId)
|
|
|
|
|| !!contextChat,
|
2021-05-13 03:33:20 +08:00
|
|
|
};
|
2017-03-03 04:33:49 +08:00
|
|
|
}
|
2016-06-28 21:10:20 +08:00
|
|
|
|
2021-02-02 06:12:04 +08:00
|
|
|
return {
|
2021-05-18 04:25:07 +08:00
|
|
|
userId: PUBLIC_CHAT_ID,
|
2021-02-02 06:12:04 +08:00
|
|
|
name: 'Public Chat',
|
|
|
|
icon: 'group_chat',
|
2021-05-18 04:25:07 +08:00
|
|
|
chatId: PUBLIC_CHAT_ID,
|
2021-02-02 06:12:04 +08:00
|
|
|
unreadCounter: unreadMessagesCount,
|
2021-05-22 01:56:10 +08:00
|
|
|
shouldDisplayInChatList: true,
|
2021-02-02 06:12:04 +08:00
|
|
|
};
|
2016-06-28 21:10:20 +08:00
|
|
|
});
|
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const currentClosedChats = Storage.getItem(CLOSED_CHAT_LIST_KEY) || [];
|
2023-07-11 22:32:15 +08:00
|
|
|
const removeClosedChats = chatInfo.filter((chat) => !currentClosedChats.find(closedChat => closedChat.chatId === chat.chatId)
|
2021-05-22 01:56:10 +08:00
|
|
|
&& chat.shouldDisplayInChatList);
|
2024-05-29 21:26:11 +08:00
|
|
|
|
2023-04-05 21:38:59 +08:00
|
|
|
const sortByChatIdAndUnread = removeClosedChats.sort((a, b) => {
|
|
|
|
if (a.chatId === PUBLIC_GROUP_CHAT_ID) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (b.chatId === PUBLIC_CHAT_ID) {
|
|
|
|
return 0;
|
|
|
|
}
|
2023-04-06 02:20:49 +08:00
|
|
|
if (a.unreadCounter > b.unreadCounter) {
|
2023-04-05 21:38:59 +08:00
|
|
|
return -1;
|
2023-04-06 02:20:49 +08:00
|
|
|
} else if (b.unreadCounter > a.unreadCounter) {
|
2023-04-05 21:38:59 +08:00
|
|
|
return 1;
|
|
|
|
} else {
|
2023-05-11 02:33:51 +08:00
|
|
|
if (a.name.toLowerCase() < b.name.toLowerCase()) {
|
|
|
|
return -1;
|
2023-04-05 21:38:59 +08:00
|
|
|
}
|
2023-05-11 02:33:51 +08:00
|
|
|
if (a.name.toLowerCase() > b.name.toLowerCase()) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2023-04-05 21:38:59 +08:00
|
|
|
});
|
|
|
|
return sortByChatIdAndUnread;
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2016-06-28 21:10:20 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const isVoiceOnlyUser = (userId) => userId.toString().startsWith('v_');
|
2017-12-09 00:40:52 +08:00
|
|
|
|
2019-01-29 23:17:09 +08:00
|
|
|
const isMeetingLocked = (id) => {
|
2021-07-08 02:30:41 +08:00
|
|
|
const meeting = Meetings.findOne({ meetingId: id },
|
2024-03-26 19:57:28 +08:00
|
|
|
{ fields: { lockSettings: 1, usersPolicies: 1 } });
|
2019-01-29 23:17:09 +08:00
|
|
|
let isLocked = false;
|
|
|
|
|
2024-03-26 19:57:28 +08:00
|
|
|
if (meeting.lockSettings !== undefined) {
|
|
|
|
const { lockSettings, usersPolicies } = meeting;
|
2019-01-29 23:17:09 +08:00
|
|
|
|
|
|
|
if (lockSettings.disableCam
|
|
|
|
|| lockSettings.disableMic
|
2019-04-10 21:44:34 +08:00
|
|
|
|| lockSettings.disablePrivateChat
|
2021-07-08 02:30:41 +08:00
|
|
|
|| lockSettings.disablePublicChat
|
2021-10-16 03:07:13 +08:00
|
|
|
|| lockSettings.disableNotes
|
2022-01-08 01:54:08 +08:00
|
|
|
|| lockSettings.hideUserList
|
2022-03-24 03:11:41 +08:00
|
|
|
|| lockSettings.hideViewersCursor
|
2023-06-19 22:17:07 +08:00
|
|
|
|| lockSettings.hideViewersAnnotation
|
2024-03-26 19:57:28 +08:00
|
|
|
|| usersPolicies.webcamsOnlyForModerator) {
|
2019-01-29 23:17:09 +08:00
|
|
|
isLocked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return isLocked;
|
|
|
|
};
|
|
|
|
|
2021-01-11 21:39:02 +08:00
|
|
|
const getUsersProp = () => {
|
|
|
|
const meeting = Meetings.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{
|
|
|
|
fields: {
|
2024-03-26 19:57:28 +08:00
|
|
|
'usersPolicies.allowModsToUnmuteUsers': 1,
|
|
|
|
'usersPolicies.allowModsToEjectCameras': 1,
|
|
|
|
'usersPolicies.authenticatedGuest': 1,
|
2021-01-11 21:39:02 +08:00
|
|
|
},
|
2021-05-18 04:25:07 +08:00
|
|
|
},
|
2021-01-11 21:39:02 +08:00
|
|
|
);
|
|
|
|
|
2024-03-26 19:57:28 +08:00
|
|
|
if (meeting.usersPolicies) return meeting.usersPolicies;
|
2021-01-11 21:39:02 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
allowModsToUnmuteUsers: false,
|
2021-12-14 03:52:09 +08:00
|
|
|
allowModsToEjectCameras: false,
|
2021-01-11 21:39:02 +08:00
|
|
|
authenticatedGuest: false,
|
2021-05-18 04:25:07 +08:00
|
|
|
};
|
2019-06-29 04:52:19 +08:00
|
|
|
};
|
2019-04-11 04:55:24 +08:00
|
|
|
|
2024-03-07 00:08:32 +08:00
|
|
|
const curatedVoiceUser = (userId) => {
|
|
|
|
const voiceUser = VoiceUsers.findOne({ userId });
|
2019-08-22 23:19:54 +08:00
|
|
|
return {
|
|
|
|
isVoiceUser: voiceUser ? voiceUser.joined : false,
|
|
|
|
isMuted: voiceUser ? voiceUser.muted && !voiceUser.listenOnly : false,
|
|
|
|
isTalking: voiceUser ? voiceUser.talking && !voiceUser.muted : false,
|
|
|
|
isListenOnly: voiceUser ? voiceUser.listenOnly : false,
|
|
|
|
};
|
|
|
|
};
|
2017-12-08 06:07:02 +08:00
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const getAvailableActions = (
|
|
|
|
amIModerator, isBreakoutRoom, subjectUser, subjectVoiceUser, usersProp, amIPresenter,
|
|
|
|
) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const ROLE_MODERATOR = window.meetingClientSettings.public.user.role_moderator;
|
|
|
|
const USER_STATUS_ENABLED = window.meetingClientSettings.public.userStatus.enabled;
|
|
|
|
|
2019-08-22 23:19:54 +08:00
|
|
|
const isDialInUser = isVoiceOnlyUser(subjectUser.userId) || subjectUser.phone_user;
|
2019-09-05 02:32:58 +08:00
|
|
|
const amISubjectUser = isMe(subjectUser.userId);
|
2019-09-06 02:21:07 +08:00
|
|
|
const isSubjectUserModerator = subjectUser.role === ROLE_MODERATOR;
|
2021-01-11 21:39:02 +08:00
|
|
|
const isSubjectUserGuest = subjectUser.guest;
|
2019-08-22 23:19:54 +08:00
|
|
|
|
2019-09-05 02:32:58 +08:00
|
|
|
const hasAuthority = amIModerator || amISubjectUser;
|
|
|
|
const allowedToChatPrivately = !amISubjectUser && !isDialInUser;
|
2017-10-31 02:27:17 +08:00
|
|
|
const allowedToMuteAudio = hasAuthority
|
2019-09-06 02:21:07 +08:00
|
|
|
&& subjectVoiceUser.isVoiceUser
|
|
|
|
&& !subjectVoiceUser.isMuted
|
|
|
|
&& !subjectVoiceUser.isListenOnly;
|
2017-12-08 06:07:02 +08:00
|
|
|
|
2017-10-26 20:50:55 +08:00
|
|
|
const allowedToUnmuteAudio = hasAuthority
|
2019-09-06 02:21:07 +08:00
|
|
|
&& subjectVoiceUser.isVoiceUser
|
|
|
|
&& !subjectVoiceUser.isListenOnly
|
|
|
|
&& subjectVoiceUser.isMuted
|
2021-03-31 21:06:54 +08:00
|
|
|
&& (amISubjectUser || usersProp.allowModsToUnmuteUsers);
|
2017-12-08 06:07:02 +08:00
|
|
|
|
|
|
|
const allowedToResetStatus = hasAuthority
|
2019-08-22 23:19:54 +08:00
|
|
|
&& subjectUser.emoji !== EMOJI_STATUSES.none
|
2019-01-14 21:23:35 +08:00
|
|
|
&& !isDialInUser;
|
2017-08-16 22:56:31 +08:00
|
|
|
|
2018-01-10 06:28:48 +08:00
|
|
|
// if currentUser is a moderator, allow removing other users
|
2019-09-05 02:32:58 +08:00
|
|
|
const allowedToRemove = amIModerator
|
|
|
|
&& !amISubjectUser
|
2019-08-22 23:19:54 +08:00
|
|
|
&& !isBreakoutRoom;
|
2017-08-16 22:56:31 +08:00
|
|
|
|
2019-09-05 02:32:58 +08:00
|
|
|
const allowedToSetPresenter = amIModerator
|
2019-08-22 23:19:54 +08:00
|
|
|
&& !subjectUser.presenter
|
2019-01-14 21:23:35 +08:00
|
|
|
&& !isDialInUser;
|
2017-12-08 06:07:02 +08:00
|
|
|
|
2019-09-05 02:32:58 +08:00
|
|
|
const allowedToPromote = amIModerator
|
|
|
|
&& !amISubjectUser
|
|
|
|
&& !isSubjectUserModerator
|
2019-03-12 21:15:08 +08:00
|
|
|
&& !isDialInUser
|
2021-01-11 21:39:02 +08:00
|
|
|
&& !isBreakoutRoom
|
|
|
|
&& !(isSubjectUserGuest && usersProp.authenticatedGuest);
|
2017-08-16 22:56:31 +08:00
|
|
|
|
2019-09-05 02:32:58 +08:00
|
|
|
const allowedToDemote = amIModerator
|
|
|
|
&& !amISubjectUser
|
|
|
|
&& isSubjectUserModerator
|
2019-03-12 21:15:08 +08:00
|
|
|
&& !isDialInUser
|
2021-01-11 21:39:02 +08:00
|
|
|
&& !isBreakoutRoom
|
|
|
|
&& !(isSubjectUserGuest && usersProp.authenticatedGuest);
|
2017-08-23 02:39:34 +08:00
|
|
|
|
2023-04-28 05:37:18 +08:00
|
|
|
const allowedToChangeStatus = amISubjectUser && USER_STATUS_ENABLED;
|
2018-07-03 09:48:28 +08:00
|
|
|
|
2019-09-05 02:32:58 +08:00
|
|
|
const allowedToChangeUserLockStatus = amIModerator
|
|
|
|
&& !isSubjectUserModerator
|
|
|
|
&& isMeetingLocked(Auth.meetingID);
|
2019-01-29 23:17:09 +08:00
|
|
|
|
2021-03-05 06:26:25 +08:00
|
|
|
const allowedToChangeWhiteboardAccess = amIPresenter
|
|
|
|
&& !amISubjectUser;
|
|
|
|
|
2021-12-14 03:52:09 +08:00
|
|
|
const allowedToEjectCameras = amIModerator
|
|
|
|
&& !amISubjectUser
|
|
|
|
&& usersProp.allowModsToEjectCameras;
|
|
|
|
|
2023-06-23 21:39:40 +08:00
|
|
|
const allowedToSetAway = amISubjectUser && !USER_STATUS_ENABLED;
|
|
|
|
|
2017-08-16 22:56:31 +08:00
|
|
|
return {
|
|
|
|
allowedToChatPrivately,
|
|
|
|
allowedToMuteAudio,
|
|
|
|
allowedToUnmuteAudio,
|
|
|
|
allowedToResetStatus,
|
2018-01-10 06:28:48 +08:00
|
|
|
allowedToRemove,
|
2017-08-16 22:56:31 +08:00
|
|
|
allowedToSetPresenter,
|
2017-08-23 02:39:34 +08:00
|
|
|
allowedToPromote,
|
|
|
|
allowedToDemote,
|
2018-07-03 09:48:28 +08:00
|
|
|
allowedToChangeStatus,
|
2019-01-29 23:17:09 +08:00
|
|
|
allowedToChangeUserLockStatus,
|
2021-03-05 06:26:25 +08:00
|
|
|
allowedToChangeWhiteboardAccess,
|
2021-12-14 03:52:09 +08:00
|
|
|
allowedToEjectCameras,
|
2023-06-23 21:39:40 +08:00
|
|
|
allowedToSetAway,
|
2017-08-16 22:56:31 +08:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2021-05-18 04:25:07 +08:00
|
|
|
const normalizeEmojiName = (emoji) => (
|
2017-11-21 18:50:20 +08:00
|
|
|
emoji in EMOJI_STATUSES ? EMOJI_STATUSES[emoji] : emoji
|
2017-09-23 01:03:14 +08:00
|
|
|
);
|
2017-08-16 22:56:31 +08:00
|
|
|
|
2024-01-29 20:49:40 +08:00
|
|
|
const toggleVoice = (userId, voiceToggle) => {
|
2019-01-14 21:23:35 +08:00
|
|
|
if (userId === Auth.userID) {
|
2024-01-29 20:49:40 +08:00
|
|
|
AudioService.toggleMuteMicrophone(voiceToggle);
|
2019-01-14 21:23:35 +08:00
|
|
|
} else {
|
2024-01-29 20:49:40 +08:00
|
|
|
voiceToggle(userId);
|
2019-07-26 02:41:24 +08:00
|
|
|
logger.info({
|
2020-06-27 02:51:01 +08:00
|
|
|
logCode: 'usermenu_option_mute_toggle_audio',
|
|
|
|
extraInfo: { logType: 'moderator_action', userId },
|
2019-07-26 02:41:24 +08:00
|
|
|
}, 'moderator muted user microphone');
|
2019-01-14 21:23:35 +08:00
|
|
|
}
|
|
|
|
};
|
2018-02-09 00:06:41 +08:00
|
|
|
|
2021-01-21 00:22:17 +08:00
|
|
|
const getEmoji = () => {
|
|
|
|
const currentUser = Users.findOne({ userId: Auth.userID },
|
|
|
|
{ fields: { emoji: 1 } });
|
|
|
|
|
|
|
|
if (!currentUser) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentUser.emoji;
|
|
|
|
};
|
|
|
|
|
2020-03-31 03:41:36 +08:00
|
|
|
const focusFirstDropDownItem = () => {
|
|
|
|
const dropdownContent = document.querySelector('div[data-test="dropdownContent"][style="visibility: visible;"]');
|
|
|
|
if (!dropdownContent) return;
|
|
|
|
const list = dropdownContent.getElementsByTagName('li');
|
|
|
|
list[0].focus();
|
|
|
|
};
|
|
|
|
|
|
|
|
const roving = (...args) => {
|
|
|
|
const [
|
|
|
|
event,
|
|
|
|
changeState,
|
|
|
|
elementsList,
|
|
|
|
element,
|
|
|
|
] = args;
|
|
|
|
|
2019-08-14 05:16:14 +08:00
|
|
|
this.selectedElement = element;
|
2020-03-31 03:41:36 +08:00
|
|
|
const numberOfChilds = elementsList.childElementCount;
|
2019-08-14 05:16:14 +08:00
|
|
|
const menuOpen = Session.get('dropdownOpen') || false;
|
|
|
|
|
|
|
|
if (menuOpen) {
|
2018-05-09 01:47:55 +08:00
|
|
|
const menuChildren = document.activeElement.getElementsByTagName('li');
|
|
|
|
|
|
|
|
if ([KEY_CODES.ESCAPE, KEY_CODES.ARROW_LEFT].includes(event.keyCode)) {
|
2020-03-31 03:41:36 +08:00
|
|
|
Session.set('dropdownOpen', false);
|
2018-05-09 01:47:55 +08:00
|
|
|
document.activeElement.click();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([KEY_CODES.ARROW_UP].includes(event.keyCode)) {
|
|
|
|
menuChildren[menuChildren.length - 1].focus();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([KEY_CODES.ARROW_DOWN].includes(event.keyCode)) {
|
|
|
|
for (let i = 0; i < menuChildren.length; i += 1) {
|
|
|
|
if (menuChildren[i].hasAttribute('tabIndex')) {
|
|
|
|
menuChildren[i].focus();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-06 03:13:11 +08:00
|
|
|
if ([KEY_CODES.ESCAPE, KEY_CODES.TAB].includes(event.keyCode)) {
|
2020-03-31 03:41:36 +08:00
|
|
|
Session.set('dropdownOpen', false);
|
2019-08-14 05:16:14 +08:00
|
|
|
changeState(null);
|
2017-12-06 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (event.keyCode === KEY_CODES.ARROW_DOWN) {
|
2019-08-14 05:16:14 +08:00
|
|
|
const firstElement = elementsList.firstChild;
|
2020-03-31 03:41:36 +08:00
|
|
|
let elRef = element && numberOfChilds > 1 ? element.nextSibling : firstElement;
|
|
|
|
|
2019-08-14 05:16:14 +08:00
|
|
|
elRef = elRef || firstElement;
|
|
|
|
changeState(elRef);
|
2017-12-06 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (event.keyCode === KEY_CODES.ARROW_UP) {
|
2019-08-14 05:16:14 +08:00
|
|
|
const lastElement = elementsList.lastChild;
|
|
|
|
let elRef = element ? element.previousSibling : lastElement;
|
|
|
|
elRef = elRef || lastElement;
|
|
|
|
changeState(elRef);
|
2017-12-06 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
2018-05-09 01:47:55 +08:00
|
|
|
if ([KEY_CODES.ARROW_RIGHT, KEY_CODES.SPACE, KEY_CODES.ENTER].includes(event.keyCode)) {
|
2020-03-31 03:41:36 +08:00
|
|
|
const tether = document.activeElement.firstChild;
|
|
|
|
const dropdownTrigger = tether.firstChild;
|
2021-07-19 01:30:27 +08:00
|
|
|
dropdownTrigger?.click();
|
2020-03-31 03:41:36 +08:00
|
|
|
focusFirstDropDownItem();
|
2017-12-06 03:13:11 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-08-29 10:17:04 +08:00
|
|
|
const sortUsersByFirstName = (a, b) => {
|
2023-05-11 02:33:51 +08:00
|
|
|
const aUser = { sortName: a.firstName ? a.firstName : '' };
|
|
|
|
const bUser = { sortName: b.firstName ? b.firstName : '' };
|
2021-04-09 04:16:36 +08:00
|
|
|
|
|
|
|
return sortUsersByName(aUser, bUser);
|
2020-08-29 10:17:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const sortUsersByLastName = (a, b) => {
|
2023-05-11 02:33:51 +08:00
|
|
|
const aUser = { sortName: a.lastName ? a.lastName : '' };
|
|
|
|
const bUser = { sortName: b.lastName ? b.lastName : '' };
|
2020-08-29 10:17:04 +08:00
|
|
|
|
2021-04-09 04:16:36 +08:00
|
|
|
return sortUsersByName(aUser, bUser);
|
2020-08-29 10:17:04 +08:00
|
|
|
};
|
|
|
|
|
2022-06-23 21:05:11 +08:00
|
|
|
const isUserPresenter = (userId = Auth.userID) => {
|
2020-09-10 22:11:18 +08:00
|
|
|
const user = Users.findOne({ userId },
|
|
|
|
{ fields: { presenter: 1 } });
|
|
|
|
return user ? user.presenter : false;
|
|
|
|
};
|
|
|
|
|
2024-04-09 20:58:50 +08:00
|
|
|
export const getUserNamesLink = (docTitle, fnSortedLabel, lnSortedLabel, users) => {
|
2019-09-07 04:28:02 +08:00
|
|
|
const mimeType = 'text/plain';
|
2024-04-09 20:58:50 +08:00
|
|
|
const userNamesObj = users
|
2020-08-29 10:17:04 +08:00
|
|
|
.map((u) => {
|
2020-09-17 21:26:38 +08:00
|
|
|
const name = u.name.split(' ');
|
2020-08-29 10:17:04 +08:00
|
|
|
return ({
|
|
|
|
firstName: name[0],
|
|
|
|
middleNames: name.length > 2 ? name.slice(1, name.length - 1) : null,
|
|
|
|
lastName: name.length > 1 ? name[name.length - 1] : null,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const getUsernameString = (user) => {
|
|
|
|
const { firstName, middleNames, lastName } = user;
|
|
|
|
return `${firstName || ''} ${middleNames && middleNames.length > 0 ? middleNames.join(' ') : ''} ${lastName || ''}`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const namesByFirstName = userNamesObj.sort(sortUsersByFirstName)
|
2021-05-18 04:25:07 +08:00
|
|
|
.map((u) => getUsernameString(u)).join('\r\n');
|
2020-08-29 10:17:04 +08:00
|
|
|
|
|
|
|
const namesByLastName = userNamesObj.sort(sortUsersByLastName)
|
2021-05-18 04:25:07 +08:00
|
|
|
.map((u) => getUsernameString(u)).join('\r\n');
|
2020-08-29 10:17:04 +08:00
|
|
|
|
2020-08-30 00:39:29 +08:00
|
|
|
const namesListsString = `${docTitle}\r\n\r\n${fnSortedLabel}\r\n${namesByFirstName}
|
|
|
|
\r\n\r\n${lnSortedLabel}\r\n${namesByLastName}`.replace(/ {2}/g, ' ');
|
2020-08-29 10:17:04 +08:00
|
|
|
|
2019-09-07 04:28:02 +08:00
|
|
|
const link = document.createElement('a');
|
2020-08-04 21:27:32 +08:00
|
|
|
const meeting = Meetings.findOne({ meetingId: Auth.meetingID },
|
2024-03-26 19:57:28 +08:00
|
|
|
{ fields: { name: 1 } });
|
|
|
|
link.setAttribute('download', `bbb-${meeting.name}[users-list]_${getDateString()}.txt`);
|
2019-09-07 04:28:02 +08:00
|
|
|
link.setAttribute(
|
|
|
|
'href',
|
2022-05-20 23:00:28 +08:00
|
|
|
`data: ${mimeType};charset=utf-16,${encodeURIComponent(namesListsString)}`,
|
2019-09-07 04:28:02 +08:00
|
|
|
);
|
|
|
|
return link;
|
|
|
|
};
|
|
|
|
|
2022-04-06 21:21:49 +08:00
|
|
|
const UserJoinedMeetingAlert = (obj) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const Settings = getSettingsSingletonInstance();
|
2022-04-06 21:21:49 +08:00
|
|
|
const {
|
|
|
|
userJoinAudioAlerts,
|
|
|
|
userJoinPushAlerts,
|
|
|
|
} = Settings.application;
|
|
|
|
|
|
|
|
if (!userJoinAudioAlerts && !userJoinPushAlerts) return;
|
|
|
|
|
|
|
|
if (userJoinAudioAlerts) {
|
2024-03-07 01:28:18 +08:00
|
|
|
AudioService.playAlertSound(`${window.meetingClientSettings.public.app.cdn
|
|
|
|
+ window.meetingClientSettings.public.app.basename
|
|
|
|
+ window.meetingClientSettings.public.app.instanceId}`
|
2022-04-06 21:21:49 +08:00
|
|
|
+ '/resources/sounds/userJoin.mp3');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userJoinPushAlerts) {
|
|
|
|
notify(
|
|
|
|
<FormattedMessage
|
|
|
|
id={obj.messageId}
|
|
|
|
values={obj.messageValues}
|
|
|
|
description={obj.messageDescription}
|
|
|
|
/>,
|
|
|
|
obj.notificationType,
|
|
|
|
obj.icon,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const UserLeftMeetingAlert = (obj) => {
|
2024-05-29 21:26:11 +08:00
|
|
|
const Settings = getSettingsSingletonInstance();
|
2022-04-06 21:21:49 +08:00
|
|
|
const {
|
|
|
|
userLeaveAudioAlerts,
|
|
|
|
userLeavePushAlerts,
|
|
|
|
} = Settings.application;
|
|
|
|
|
|
|
|
if (!userLeaveAudioAlerts && !userLeavePushAlerts) return;
|
|
|
|
|
|
|
|
if (userLeaveAudioAlerts) {
|
2024-03-07 01:28:18 +08:00
|
|
|
AudioService.playAlertSound(`${window.meetingClientSettings.public.app.cdn
|
|
|
|
+ window.meetingClientSettings.public.app.basename
|
|
|
|
+ window.meetingClientSettings.public.app.instanceId}`
|
2022-04-06 21:21:49 +08:00
|
|
|
+ '/resources/sounds/notify.mp3');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (userLeavePushAlerts) {
|
|
|
|
notify(
|
|
|
|
<FormattedMessage
|
|
|
|
id={obj.messageId}
|
|
|
|
values={obj.messageValues}
|
|
|
|
description={obj.messageDescription}
|
|
|
|
/>,
|
|
|
|
obj.notificationType,
|
|
|
|
obj.icon,
|
|
|
|
);
|
|
|
|
}
|
2024-04-25 02:45:28 +08:00
|
|
|
};
|
2022-04-06 21:21:49 +08:00
|
|
|
|
2016-06-01 04:25:42 +08:00
|
|
|
export default {
|
2019-09-18 20:32:00 +08:00
|
|
|
sortUsersByName,
|
2019-08-10 06:28:49 +08:00
|
|
|
sortUsers,
|
2017-09-29 02:19:57 +08:00
|
|
|
toggleVoice,
|
2019-01-14 21:23:35 +08:00
|
|
|
getActiveChats,
|
2017-08-16 22:56:31 +08:00
|
|
|
getAvailableActions,
|
2019-08-22 23:19:54 +08:00
|
|
|
curatedVoiceUser,
|
2017-08-16 22:56:31 +08:00
|
|
|
normalizeEmojiName,
|
2017-09-12 03:11:03 +08:00
|
|
|
isMeetingLocked,
|
2017-09-22 22:24:24 +08:00
|
|
|
isPublicChat,
|
2017-12-06 03:13:11 +08:00
|
|
|
roving,
|
2018-03-21 03:35:28 +08:00
|
|
|
getCustomLogoUrl,
|
2018-10-02 21:48:12 +08:00
|
|
|
hasBreakoutRoom,
|
2018-07-12 04:08:22 +08:00
|
|
|
getEmojiList: () => EMOJI_STATUSES,
|
2021-01-21 00:22:17 +08:00
|
|
|
getEmoji,
|
2020-03-31 03:41:36 +08:00
|
|
|
focusFirstDropDownItem,
|
2020-09-10 22:11:18 +08:00
|
|
|
isUserPresenter,
|
2021-01-11 21:39:02 +08:00
|
|
|
getUsersProp,
|
2021-03-17 00:27:34 +08:00
|
|
|
getUserCount,
|
2021-04-09 10:58:13 +08:00
|
|
|
sortUsersByCurrent,
|
2022-04-06 21:21:49 +08:00
|
|
|
UserJoinedMeetingAlert,
|
|
|
|
UserLeftMeetingAlert,
|
2016-06-01 04:25:42 +08:00
|
|
|
};
|