diff --git a/bbb-learning-dashboard/src/index.css b/bbb-learning-dashboard/src/index.css
index a3319c71b3..4949bc1fe1 100644
--- a/bbb-learning-dashboard/src/index.css
+++ b/bbb-learning-dashboard/src/index.css
@@ -1,4 +1,34 @@
/* ./src/index.css */
@tailwind base;
@tailwind components;
-@tailwind utilities;
\ No newline at end of file
+@tailwind utilities;
+
+@layer utilities {
+ .bg-inherit {
+ background-color: inherit;
+ }
+}
+
+.translate-x-0 {
+ --tw-translate-x: 0px;
+}
+
+.translate-x-2 {
+ --tw-translate-x: 0.5rem;
+}
+
+.translate-x-4 {
+ --tw-translate-x: 1rem;
+}
+
+[dir="rtl"] .translate-x-0 {
+ --tw-translate-x: 0px;
+}
+
+[dir="rtl"] .translate-x-2 {
+ --tw-translate-x: -0.5rem;
+}
+
+[dir="rtl"] .translate-x-4 {
+ --tw-translate-x: -1rem;
+}
diff --git a/bbb-learning-dashboard/src/services/EmojiService.js b/bbb-learning-dashboard/src/services/EmojiService.js
index b64fdc98ff..20fdd737ce 100644
--- a/bbb-learning-dashboard/src/services/EmojiService.js
+++ b/bbb-learning-dashboard/src/services/EmojiService.js
@@ -60,3 +60,15 @@ export function getUserEmojisSummary(user, skipNames = null, start = null, end =
});
return userEmojis;
}
+
+export function filterUserEmojis(user, skipNames = null, start = null, end = null) {
+ const userEmojis = [];
+ user.emojis.forEach((emoji) => {
+ if (typeof emojiConfigs[emoji.name] === 'undefined') return;
+ if (skipNames != null && skipNames.split(',').indexOf(emoji.name) > -1) return;
+ if (start != null && emoji.sentOn < start) return;
+ if (end != null && emoji.sentOn > end) return;
+ userEmojis.push(emoji);
+ });
+ return userEmojis;
+}
diff --git a/bbb-learning-dashboard/src/services/UserService.js b/bbb-learning-dashboard/src/services/UserService.js
new file mode 100644
index 0000000000..84dc8a4c94
--- /dev/null
+++ b/bbb-learning-dashboard/src/services/UserService.js
@@ -0,0 +1,202 @@
+import { emojiConfigs, filterUserEmojis } from './EmojiService';
+
+export function getActivityScore(user, allUsers, totalOfPolls) {
+ if (user.isModerator) return 0;
+
+ const allUsersArr = Object.values(allUsers || {}).filter((currUser) => !currUser.isModerator);
+ let userPoints = 0;
+
+ // Calculate points of Talking
+ const usersTalkTime = allUsersArr.map((currUser) => currUser.talk.totalTime);
+ const maxTalkTime = Math.max(...usersTalkTime);
+ if (maxTalkTime > 0) {
+ userPoints += (user.talk.totalTime / maxTalkTime) * 2;
+ }
+
+ // Calculate points of Chatting
+ const usersTotalOfMessages = allUsersArr.map((currUser) => currUser.totalOfMessages);
+ const maxMessages = Math.max(...usersTotalOfMessages);
+ if (maxMessages > 0) {
+ userPoints += (user.totalOfMessages / maxMessages) * 2;
+ }
+
+ // Calculate points of Raise hand
+ const usersRaiseHand = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name === 'raiseHand').length);
+ const maxRaiseHand = Math.max(...usersRaiseHand);
+ const userRaiseHand = user.emojis.filter((emoji) => emoji.name === 'raiseHand').length;
+ if (maxRaiseHand > 0) {
+ userPoints += (userRaiseHand / maxRaiseHand) * 2;
+ }
+
+ // Calculate points of Emojis
+ const usersEmojis = allUsersArr.map((currUser) => currUser.emojis.filter((emoji) => emoji.name !== 'raiseHand').length);
+ const maxEmojis = Math.max(...usersEmojis);
+ const userEmojis = user.emojis.filter((emoji) => emoji.name !== 'raiseHand').length;
+ if (maxEmojis > 0) {
+ userPoints += (userEmojis / maxEmojis) * 2;
+ }
+
+ // Calculate points of Polls
+ if (totalOfPolls > 0) {
+ userPoints += (Object.values(user.answers || {}).length / totalOfPolls) * 2;
+ }
+
+ return userPoints;
+}
+
+export function getSumOfTime(eventsArr) {
+ return eventsArr.reduce((prevVal, elem) => {
+ if (elem.stoppedOn > 0) return prevVal + (elem.stoppedOn - elem.startedOn);
+ return prevVal + (new Date().getTime() - elem.startedOn);
+ }, 0);
+}
+
+export function tsToHHmmss(ts) {
+ return (new Date(ts).toISOString().substr(11, 8));
+}
+
+const tableHeaderFields = [
+ {
+ id: 'name',
+ defaultMessage: 'Name',
+ },
+ {
+ id: 'moderator',
+ defaultMessage: 'Moderator',
+ },
+ {
+ id: 'activityScore',
+ defaultMessage: 'Activity Score',
+ },
+ {
+ id: 'colTalk',
+ defaultMessage: 'Talk Time',
+ },
+ {
+ id: 'colWebcam',
+ defaultMessage: 'Webcam Time',
+ },
+ {
+ id: 'colMessages',
+ defaultMessage: 'Messages',
+ },
+ {
+ id: 'colEmojis',
+ defaultMessage: 'Emojis',
+ },
+ {
+ id: 'pollVotes',
+ defaultMessage: 'Poll Votes',
+ },
+ {
+ id: 'colRaiseHands',
+ defaultMessage: 'Raise Hands',
+ },
+ {
+ id: 'join',
+ defaultMessage: 'Join',
+ },
+ {
+ id: 'left',
+ defaultMessage: 'Left',
+ },
+ {
+ id: 'duration',
+ defaultMessage: 'Duration',
+ },
+];
+
+export function makeUserCSVData(users, polls, intl) {
+ const userRecords = {};
+ const userValues = Object.values(users || {});
+ const pollValues = Object.values(polls || {});
+ const skipEmojis = Object
+ .keys(emojiConfigs)
+ .filter((emoji) => emoji !== 'raiseHand')
+ .join(',');
+
+ for (let i = 0; i < userValues.length; i += 1) {
+ const user = userValues[i];
+ const webcam = getSumOfTime(user.webcams);
+ const duration = user.leftOn > 0
+ ? user.leftOn - user.registeredOn
+ : (new Date()).getTime() - user.registeredOn;
+
+ const userData = {
+ name: user.name,
+ moderator: user.isModerator.toString().toUpperCase(),
+ activityScore: intl.formatNumber(
+ getActivityScore(user, userValues, Object.values(polls || {}).length),
+ {
+ minimumFractionDigits: 0,
+ maximumFractionDigits: 1,
+ },
+ ),
+ talk: user.talk.totalTime > 0 ? tsToHHmmss(user.talk.totalTime) : '-',
+ webcam: webcam > 0 ? tsToHHmmss(webcam) : '-',
+ messages: user.totalOfMessages,
+ raiseHand: filterUserEmojis(user, 'raiseHand').length,
+ answers: Object.keys(user.answers).length,
+ emojis: filterUserEmojis(user, skipEmojis).length,
+ registeredOn: intl.formatDate(user.registeredOn, {
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ }),
+ leftOn: intl.formatDate(user.leftOn, {
+ year: 'numeric',
+ month: 'numeric',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ }),
+ duration: tsToHHmmss(duration),
+ };
+
+ for (let j = 0; j < pollValues.length; j += 1) {
+ userData[`Poll_${j}`] = user.answers[pollValues[j].pollId] || '-';
+ }
+
+ const userFields = Object
+ .values(userData)
+ .map((data) => `"${data}"`);
+
+ userRecords[user.intId] = userFields.join(',');
+ }
+
+ const tableHeaderFieldsTranslated = tableHeaderFields
+ .map(({ id, defaultMessage }) => intl.formatMessage({
+ id: `app.learningDashboard.usersTable.${id}`,
+ defaultMessage,
+ }));
+
+ let header = tableHeaderFieldsTranslated.join(',');
+ let anonymousRecord = `"${intl.formatMessage({
+ id: 'app.learningDashboard.pollsTable.anonymousRowName',
+ defaultMessage: 'Anonymous',
+ })}"`;
+
+ // Skip the fields for the anonymous record
+ for (let k = 0; k < header.split(',').length - 1; k += 1) {
+ // Empty fields
+ anonymousRecord += ',""';
+ }
+
+ for (let i = 0; i < pollValues.length; i += 1) {
+ // Add the poll question headers
+ header += `,${pollValues[i].question || `Poll ${i + 1}`}`;
+
+ // Add the anonymous answers
+ anonymousRecord += `,"${pollValues[i].anonymousAnswers.join('\r\n')}"`;
+ }
+ userRecords.Anonymous = anonymousRecord;
+
+ return [
+ header,
+ Object.values(userRecords).join('\r\n'),
+ ].join('\r\n');
+}
diff --git a/bbb-learning-dashboard/tailwind.config.js b/bbb-learning-dashboard/tailwind.config.js
index 7b39079d2d..664d9719fa 100644
--- a/bbb-learning-dashboard/tailwind.config.js
+++ b/bbb-learning-dashboard/tailwind.config.js
@@ -8,4 +8,4 @@ module.exports = {
extend: {},
},
plugins: [],
-}
+};
diff --git a/bigbluebutton-html5/client/collection-mirror-initializer.js b/bigbluebutton-html5/client/collection-mirror-initializer.js
new file mode 100644
index 0000000000..232381e665
--- /dev/null
+++ b/bigbluebutton-html5/client/collection-mirror-initializer.js
@@ -0,0 +1,92 @@
+import AbstractCollection from '/imports/ui/services/LocalCollectionSynchronizer/LocalCollectionSynchronizer';
+
+// Collections
+import Presentations from '/imports/api/presentations';
+import PresentationPods from '/imports/api/presentation-pods';
+import PresentationUploadToken from '/imports/api/presentation-upload-token';
+import Screenshare from '/imports/api/screenshare';
+import UserInfos from '/imports/api/users-infos';
+import Polls, { CurrentPoll } from '/imports/api/polls';
+import UsersPersistentData from '/imports/api/users-persistent-data';
+import UserSettings from '/imports/api/users-settings';
+import VideoStreams from '/imports/api/video-streams';
+import VoiceUsers from '/imports/api/voice-users';
+import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user';
+import Note from '/imports/api/note';
+import GroupChat from '/imports/api/group-chat';
+import ConnectionStatus from '/imports/api/connection-status';
+import Captions from '/imports/api/captions';
+import AuthTokenValidation from '/imports/api/auth-token-validation';
+import Annotations from '/imports/api/annotations';
+import Breakouts from '/imports/api/breakouts';
+import guestUsers from '/imports/api/guest-users';
+import Meetings, { RecordMeetings, ExternalVideoMeetings, MeetingTimeRemaining } from '/imports/api/meetings';
+import { UsersTyping } from '/imports/api/group-chat-msg';
+import Users, { CurrentUser } from '/imports/api/users';
+import { Slides, SlidePositions } from '/imports/api/slides';
+
+// Custom Publishers
+export const localCurrentPollSync = new AbstractCollection(CurrentPoll, CurrentPoll);
+export const localCurrentUserSync = new AbstractCollection(CurrentUser, CurrentUser);
+export const localSlidesSync = new AbstractCollection(Slides, Slides);
+export const localSlidePositionsSync = new AbstractCollection(SlidePositions, SlidePositions);
+export const localPollsSync = new AbstractCollection(Polls, Polls);
+export const localPresentationsSync = new AbstractCollection(Presentations, Presentations);
+export const localPresentationPodsSync = new AbstractCollection(PresentationPods, PresentationPods);
+export const localPresentationUploadTokenSync = new AbstractCollection(PresentationUploadToken, PresentationUploadToken);
+export const localScreenshareSync = new AbstractCollection(Screenshare, Screenshare);
+export const localUserInfosSync = new AbstractCollection(UserInfos, UserInfos);
+export const localUsersPersistentDataSync = new AbstractCollection(UsersPersistentData, UsersPersistentData);
+export const localUserSettingsSync = new AbstractCollection(UserSettings, UserSettings);
+export const localVideoStreamsSync = new AbstractCollection(VideoStreams, VideoStreams);
+export const localVoiceUsersSync = new AbstractCollection(VoiceUsers, VoiceUsers);
+export const localWhiteboardMultiUserSync = new AbstractCollection(WhiteboardMultiUser, WhiteboardMultiUser);
+export const localNoteSync = new AbstractCollection(Note, Note);
+export const localGroupChatSync = new AbstractCollection(GroupChat, GroupChat);
+export const localConnectionStatusSync = new AbstractCollection(ConnectionStatus, ConnectionStatus);
+export const localCaptionsSync = new AbstractCollection(Captions, Captions);
+export const localAuthTokenValidationSync = new AbstractCollection(AuthTokenValidation, AuthTokenValidation);
+export const localAnnotationsSync = new AbstractCollection(Annotations, Annotations);
+export const localRecordMeetingsSync = new AbstractCollection(RecordMeetings, RecordMeetings);
+export const localExternalVideoMeetingsSync = new AbstractCollection(ExternalVideoMeetings, ExternalVideoMeetings);
+export const localMeetingTimeRemainingSync = new AbstractCollection(MeetingTimeRemaining, MeetingTimeRemaining);
+export const localUsersTypingSync = new AbstractCollection(UsersTyping, UsersTyping);
+export const localBreakoutsSync = new AbstractCollection(Breakouts, Breakouts);
+export const localGuestUsersSync = new AbstractCollection(guestUsers, guestUsers);
+export const localMeetingsSync = new AbstractCollection(Meetings, Meetings);
+export const localUsersSync = new AbstractCollection(Users, Users);
+
+const collectionMirrorInitializer = () => {
+ localCurrentPollSync.setupListeners();
+ localCurrentUserSync.setupListeners();
+ localSlidesSync.setupListeners();
+ localSlidePositionsSync.setupListeners();
+ localPollsSync.setupListeners();
+ localPresentationsSync.setupListeners();
+ localPresentationPodsSync.setupListeners();
+ localPresentationUploadTokenSync.setupListeners();
+ localScreenshareSync.setupListeners();
+ localUserInfosSync.setupListeners();
+ localUsersPersistentDataSync.setupListeners();
+ localUserSettingsSync.setupListeners();
+ localVideoStreamsSync.setupListeners();
+ localVoiceUsersSync.setupListeners();
+ localWhiteboardMultiUserSync.setupListeners();
+ localNoteSync.setupListeners();
+ localGroupChatSync.setupListeners();
+ localConnectionStatusSync.setupListeners();
+ localCaptionsSync.setupListeners();
+ localAuthTokenValidationSync.setupListeners();
+ localAnnotationsSync.setupListeners();
+ localRecordMeetingsSync.setupListeners();
+ localExternalVideoMeetingsSync.setupListeners();
+ localMeetingTimeRemainingSync.setupListeners();
+ localUsersTypingSync.setupListeners();
+ localBreakoutsSync.setupListeners();
+ localGuestUsersSync.setupListeners();
+ localMeetingsSync.setupListeners();
+ localUsersSync.setupListeners();
+};
+
+export default collectionMirrorInitializer;
+// const localUsersSync = new AbstractCollection(CurrentUser, CurrentUser);
diff --git a/bigbluebutton-html5/client/main.jsx b/bigbluebutton-html5/client/main.jsx
index dca29dce58..05eabd656e 100755
--- a/bigbluebutton-html5/client/main.jsx
+++ b/bigbluebutton-html5/client/main.jsx
@@ -17,8 +17,6 @@
*/
/* eslint no-unused-vars: 0 */
-import '../imports/ui/services/collection-hooks/collection-hooks';
-
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
@@ -32,16 +30,15 @@ import ContextProviders from '/imports/ui/components/context-providers/component
import ChatAdapter from '/imports/ui/components/components-data/chat-context/adapter';
import UsersAdapter from '/imports/ui/components/components-data/users-context/adapter';
import GroupChatAdapter from '/imports/ui/components/components-data/group-chat-context/adapter';
+import { liveDataEventBrokerInitializer } from '/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker';
-import '/imports/ui/local-collections/meetings-collection/meetings';
-import '/imports/ui/local-collections/breakouts-collection/breakouts';
-import '/imports/ui/local-collections/guest-users-collection/guest-users';
-import '/imports/ui/local-collections/users-collection/users';
+import collectionMirrorInitializer from './collection-mirror-initializer';
import('/imports/api/audio/client/bridge/bridge-whitelist').catch(() => {
// bridge loading
});
-
+collectionMirrorInitializer();
+liveDataEventBrokerInitializer();
Meteor.startup(() => {
// Logs all uncaught exceptions to the client logger
window.addEventListener('error', (e) => {
diff --git a/bigbluebutton-html5/imports/api/annotations/index.js b/bigbluebutton-html5/imports/api/annotations/index.js
index 12e80e9ca4..ee550d0bb4 100755
--- a/bigbluebutton-html5/imports/api/annotations/index.js
+++ b/bigbluebutton-html5/imports/api/annotations/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Annotations = new Mongo.Collection('annotations');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Annotations = new Mongo.Collection('annotations', collectionOptions);
if (Meteor.isServer) {
// types of queries for the annotations (Total):
diff --git a/bigbluebutton-html5/imports/api/auth-token-validation/index.js b/bigbluebutton-html5/imports/api/auth-token-validation/index.js
index b7186e60a9..0feacc2fb4 100644
--- a/bigbluebutton-html5/imports/api/auth-token-validation/index.js
+++ b/bigbluebutton-html5/imports/api/auth-token-validation/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const AuthTokenValidation = new Mongo.Collection('auth-token-validation');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const AuthTokenValidation = new Mongo.Collection('auth-token-validation', collectionOptions);
if (Meteor.isServer) {
AuthTokenValidation._ensureIndex({ meetingId: 1, userId: 1 });
diff --git a/bigbluebutton-html5/imports/api/breakouts/index.js b/bigbluebutton-html5/imports/api/breakouts/index.js
index 41c6ed54e4..d0c53512d6 100644
--- a/bigbluebutton-html5/imports/api/breakouts/index.js
+++ b/bigbluebutton-html5/imports/api/breakouts/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Breakouts = new Mongo.Collection('breakouts');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Breakouts = new Mongo.Collection('breakouts', collectionOptions);
if (Meteor.isServer) {
// types of queries for the breakouts:
diff --git a/bigbluebutton-html5/imports/api/captions/index.js b/bigbluebutton-html5/imports/api/captions/index.js
index 85652495a2..859139869a 100644
--- a/bigbluebutton-html5/imports/api/captions/index.js
+++ b/bigbluebutton-html5/imports/api/captions/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Captions = new Mongo.Collection('captions');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Captions = new Mongo.Collection('captions', collectionOptions);
if (Meteor.isServer) {
Captions._ensureIndex({ meetingId: 1, padId: 1 });
diff --git a/bigbluebutton-html5/imports/api/connection-status/index.js b/bigbluebutton-html5/imports/api/connection-status/index.js
index d68450f84e..4e1b811805 100644
--- a/bigbluebutton-html5/imports/api/connection-status/index.js
+++ b/bigbluebutton-html5/imports/api/connection-status/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const ConnectionStatus = new Mongo.Collection('connection-status');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const ConnectionStatus = new Mongo.Collection('connection-status', collectionOptions);
if (Meteor.isServer) {
ConnectionStatus._ensureIndex({ meetingId: 1, userId: 1 });
diff --git a/bigbluebutton-html5/imports/api/group-chat-msg/index.js b/bigbluebutton-html5/imports/api/group-chat-msg/index.js
index 37803a12ae..42b006cb76 100644
--- a/bigbluebutton-html5/imports/api/group-chat-msg/index.js
+++ b/bigbluebutton-html5/imports/api/group-chat-msg/index.js
@@ -1,7 +1,11 @@
import { Meteor } from 'meteor/meteor';
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
const GroupChatMsg = new Mongo.Collection('group-chat-msg');
-const UsersTyping = new Mongo.Collection('users-typing');
+const UsersTyping = new Mongo.Collection('users-typing', collectionOptions);
if (Meteor.isServer) {
GroupChatMsg._ensureIndex({ meetingId: 1, chatId: 1 });
diff --git a/bigbluebutton-html5/imports/api/group-chat/index.js b/bigbluebutton-html5/imports/api/group-chat/index.js
index f26082d0a5..05381f370e 100644
--- a/bigbluebutton-html5/imports/api/group-chat/index.js
+++ b/bigbluebutton-html5/imports/api/group-chat/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const GroupChat = new Mongo.Collection('group-chat');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const GroupChat = new Mongo.Collection('group-chat', collectionOptions);
if (Meteor.isServer) {
GroupChat._ensureIndex({
diff --git a/bigbluebutton-html5/imports/api/guest-users/index.js b/bigbluebutton-html5/imports/api/guest-users/index.js
index 44ca2ae9c2..798317c4cf 100644
--- a/bigbluebutton-html5/imports/api/guest-users/index.js
+++ b/bigbluebutton-html5/imports/api/guest-users/index.js
@@ -1,5 +1,9 @@
import { Mongo } from 'meteor/mongo';
-const GuestUsers = new Mongo.Collection('guestUsers');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const GuestUsers = new Mongo.Collection('guestUsers', collectionOptions);
export default GuestUsers;
diff --git a/bigbluebutton-html5/imports/api/meetings/index.js b/bigbluebutton-html5/imports/api/meetings/index.js
index d3c20af68d..0e42b89333 100644
--- a/bigbluebutton-html5/imports/api/meetings/index.js
+++ b/bigbluebutton-html5/imports/api/meetings/index.js
@@ -1,9 +1,13 @@
import { Meteor } from 'meteor/meteor';
-const Meetings = new Mongo.Collection('meetings');
-const RecordMeetings = new Mongo.Collection('record-meetings');
-const ExternalVideoMeetings = new Mongo.Collection('external-video-meetings');
-const MeetingTimeRemaining = new Mongo.Collection('meeting-time-remaining');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Meetings = new Mongo.Collection('meetings', collectionOptions);
+const RecordMeetings = new Mongo.Collection('record-meetings', collectionOptions);
+const ExternalVideoMeetings = new Mongo.Collection('external-video-meetings', collectionOptions);
+const MeetingTimeRemaining = new Mongo.Collection('meeting-time-remaining', collectionOptions);
if (Meteor.isServer) {
// types of queries for the meetings:
diff --git a/bigbluebutton-html5/imports/api/note/index.js b/bigbluebutton-html5/imports/api/note/index.js
index be3e2877a0..27ed4cec27 100644
--- a/bigbluebutton-html5/imports/api/note/index.js
+++ b/bigbluebutton-html5/imports/api/note/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Note = new Mongo.Collection('note');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Note = new Mongo.Collection('note', collectionOptions);
if (Meteor.isServer) {
Note._ensureIndex({ meetingId: 1, noteId: 1 });
diff --git a/bigbluebutton-html5/imports/api/polls/index.js b/bigbluebutton-html5/imports/api/polls/index.js
index 9be6cb854a..ac7fd05e73 100644
--- a/bigbluebutton-html5/imports/api/polls/index.js
+++ b/bigbluebutton-html5/imports/api/polls/index.js
@@ -1,7 +1,11 @@
import { Meteor } from 'meteor/meteor';
-const Polls = new Mongo.Collection('polls');
-export const CurrentPoll = new Mongo.Collection('current-poll');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Polls = new Mongo.Collection('polls', collectionOptions);
+export const CurrentPoll = new Mongo.Collection('current-poll', { connection: null });
if (Meteor.isServer) {
// We can have just one active poll per meeting
diff --git a/bigbluebutton-html5/imports/api/presentation-pods/index.js b/bigbluebutton-html5/imports/api/presentation-pods/index.js
index 675d84e42c..14ced0fc9f 100644
--- a/bigbluebutton-html5/imports/api/presentation-pods/index.js
+++ b/bigbluebutton-html5/imports/api/presentation-pods/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const PresentationPods = new Mongo.Collection('presentation-pods');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const PresentationPods = new Mongo.Collection('presentation-pods', collectionOptions);
if (Meteor.isServer) {
// types of queries for the presentation pods:
diff --git a/bigbluebutton-html5/imports/api/presentation-upload-token/index.js b/bigbluebutton-html5/imports/api/presentation-upload-token/index.js
index 11fdf5f478..4800f0a928 100644
--- a/bigbluebutton-html5/imports/api/presentation-upload-token/index.js
+++ b/bigbluebutton-html5/imports/api/presentation-upload-token/index.js
@@ -1,3 +1,7 @@
-const PresentationUploadToken = new Mongo.Collection('presentation-upload-token');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const PresentationUploadToken = new Mongo.Collection('presentation-upload-token', collectionOptions);
export default PresentationUploadToken;
diff --git a/bigbluebutton-html5/imports/api/presentations/index.js b/bigbluebutton-html5/imports/api/presentations/index.js
index 0cd34458d4..d2c4f90ce2 100644
--- a/bigbluebutton-html5/imports/api/presentations/index.js
+++ b/bigbluebutton-html5/imports/api/presentations/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Presentations = new Mongo.Collection('presentations');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Presentations = new Mongo.Collection('presentations', collectionOptions);
if (Meteor.isServer) {
// types of queries for the presentations:
diff --git a/bigbluebutton-html5/imports/api/screenshare/index.js b/bigbluebutton-html5/imports/api/screenshare/index.js
index 10d5c956d0..a24677805d 100644
--- a/bigbluebutton-html5/imports/api/screenshare/index.js
+++ b/bigbluebutton-html5/imports/api/screenshare/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const Screenshare = new Mongo.Collection('screenshare');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Screenshare = new Mongo.Collection('screenshare', collectionOptions);
if (Meteor.isServer) {
// types of queries for the screenshare:
diff --git a/bigbluebutton-html5/imports/api/slides/index.js b/bigbluebutton-html5/imports/api/slides/index.js
index 29b8f27c74..f35508d063 100755
--- a/bigbluebutton-html5/imports/api/slides/index.js
+++ b/bigbluebutton-html5/imports/api/slides/index.js
@@ -1,7 +1,11 @@
import { Meteor } from 'meteor/meteor';
-const Slides = new Mongo.Collection('slides');
-const SlidePositions = new Mongo.Collection('slide-positions');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Slides = new Mongo.Collection('slides', collectionOptions);
+const SlidePositions = new Mongo.Collection('slide-positions', collectionOptions);
if (Meteor.isServer) {
// types of queries for the slides:
diff --git a/bigbluebutton-html5/imports/api/users-infos/index.js b/bigbluebutton-html5/imports/api/users-infos/index.js
index 1b5d71ed25..414714f95c 100644
--- a/bigbluebutton-html5/imports/api/users-infos/index.js
+++ b/bigbluebutton-html5/imports/api/users-infos/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const UserInfos = new Mongo.Collection('users-infos');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const UserInfos = new Mongo.Collection('users-infos', collectionOptions);
if (Meteor.isServer) {
UserInfos._ensureIndex({ meetingId: 1, userId: 1 });
diff --git a/bigbluebutton-html5/imports/api/users-persistent-data/index.js b/bigbluebutton-html5/imports/api/users-persistent-data/index.js
index ab5a6d5337..718557757d 100644
--- a/bigbluebutton-html5/imports/api/users-persistent-data/index.js
+++ b/bigbluebutton-html5/imports/api/users-persistent-data/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const UsersPersistentData = new Mongo.Collection('users-persistent-data');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const UsersPersistentData = new Mongo.Collection('users-persistent-data', collectionOptions);
if (Meteor.isServer) {
UsersPersistentData._ensureIndex({ meetingId: 1, userId: 1 });
diff --git a/bigbluebutton-html5/imports/api/users-settings/index.js b/bigbluebutton-html5/imports/api/users-settings/index.js
index fc9e49a53e..8e18a21805 100644
--- a/bigbluebutton-html5/imports/api/users-settings/index.js
+++ b/bigbluebutton-html5/imports/api/users-settings/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const UserSettings = new Mongo.Collection('users-settings');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const UserSettings = new Mongo.Collection('users-settings', collectionOptions);
if (Meteor.isServer) {
UserSettings._ensureIndex({
diff --git a/bigbluebutton-html5/imports/api/users/index.js b/bigbluebutton-html5/imports/api/users/index.js
index 0df489b279..fcd49fe108 100644
--- a/bigbluebutton-html5/imports/api/users/index.js
+++ b/bigbluebutton-html5/imports/api/users/index.js
@@ -1,13 +1,17 @@
import { Meteor } from 'meteor/meteor';
-const Users = new Mongo.Collection('users');
-export const CurrentUser = new Mongo.Collection('current-user');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const Users = new Mongo.Collection('users', collectionOptions);
+export const CurrentUser = new Mongo.Collection('current-user', { connection: null });
if (Meteor.isServer) {
// types of queries for the users:
// 1. meetingId
// 2. meetingId, userId
-
+ // { connection: Meteor.isClient ? null : true }
Users._ensureIndex({ meetingId: 1, userId: 1 });
}
diff --git a/bigbluebutton-html5/imports/api/video-streams/index.js b/bigbluebutton-html5/imports/api/video-streams/index.js
index eaf2e253b6..465316a2d1 100644
--- a/bigbluebutton-html5/imports/api/video-streams/index.js
+++ b/bigbluebutton-html5/imports/api/video-streams/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const VideoStreams = new Mongo.Collection('video-streams');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const VideoStreams = new Mongo.Collection('video-streams', collectionOptions);
if (Meteor.isServer) {
// types of queries for the video users:
diff --git a/bigbluebutton-html5/imports/api/voice-users/index.js b/bigbluebutton-html5/imports/api/voice-users/index.js
index 918050a46d..372109ff1c 100644
--- a/bigbluebutton-html5/imports/api/voice-users/index.js
+++ b/bigbluebutton-html5/imports/api/voice-users/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const VoiceUsers = new Mongo.Collection('voiceUsers');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const VoiceUsers = new Mongo.Collection('voiceUsers', collectionOptions);
if (Meteor.isServer) {
// types of queries for the voice users:
diff --git a/bigbluebutton-html5/imports/api/whiteboard-multi-user/index.js b/bigbluebutton-html5/imports/api/whiteboard-multi-user/index.js
index 098583539e..1ab393129e 100644
--- a/bigbluebutton-html5/imports/api/whiteboard-multi-user/index.js
+++ b/bigbluebutton-html5/imports/api/whiteboard-multi-user/index.js
@@ -1,6 +1,10 @@
import { Meteor } from 'meteor/meteor';
-const WhiteboardMultiUser = new Mongo.Collection('whiteboard-multi-user');
+const collectionOptions = Meteor.isClient ? {
+ connection: null,
+} : {};
+
+const WhiteboardMultiUser = new Mongo.Collection('whiteboard-multi-user', collectionOptions);
if (Meteor.isServer) {
// types of queries for the whiteboard-multi-user:
diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx
index 9264718cb7..4199139e63 100755
--- a/bigbluebutton-html5/imports/startup/client/base.jsx
+++ b/bigbluebutton-html5/imports/startup/client/base.jsx
@@ -8,14 +8,14 @@ import MeetingEnded from '/imports/ui/components/meeting-ended/component';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
import Settings from '/imports/ui/services/settings';
import logger from '/imports/startup/client/logger';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import { Session } from 'meteor/session';
import { FormattedMessage } from 'react-intl';
import { Meteor } from 'meteor/meteor';
import { RecordMeetings } from '../../api/meetings';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import AppService from '/imports/ui/components/app/service';
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
+import Breakouts from '/imports/api/breakouts';
import AudioService from '/imports/ui/components/audio/service';
import { notify } from '/imports/ui/services/notification';
import deviceInfo from '/imports/utils/deviceInfo';
@@ -218,6 +218,7 @@ class Base extends Component {
if (approved && loading) this.updateLoadingState(false);
if (prevProps.ejected || ejected) {
+ console.log(' if (prevProps.ejected || ejected) {');
Session.set('codeError', '403');
Session.set('isMeetingEnded', true);
}
@@ -227,6 +228,10 @@ class Base extends Component {
this.setMeetingExisted(false);
}
+ if ((prevProps.isMeteorConnected !== isMeteorConnected) && !isMeteorConnected) {
+ Session.set('globalIgnoreDeletes', true);
+ }
+
const enabled = HTML.classList.contains('animationsEnabled');
const disabled = HTML.classList.contains('animationsDisabled');
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/captions/reader-menu/styles.js b/bigbluebutton-html5/imports/ui/components/actions-bar/captions/reader-menu/styles.js
index 54a2565589..cffdface86 100644
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/captions/reader-menu/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/captions/reader-menu/styles.js
@@ -2,7 +2,7 @@ import styled from 'styled-components';
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
import Modal from '/imports/ui/components/modal/simple/component';
import {
- colorBackground,
+ colorGrayDark,
colorWhite,
colorGrayLabel,
colorGrayLight,
@@ -16,7 +16,7 @@ const ReaderMenuModal = styled(Modal)`
const Title = styled.header`
display: block;
- color: ${colorBackground};
+ color: ${colorGrayDark};
font-size: 1.4rem;
text-align: center;
`;
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/container.jsx
index 547f474a6a..ae0625ad6f 100644
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/container.jsx
@@ -29,6 +29,8 @@ const ActionsBarContainer = (props) => {
const currentUser = { userId: Auth.userID, emoji: users[Auth.meetingID][Auth.userID].emoji };
+ const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
+
return (
{
currentUser,
layoutContextDispatch,
actionsBarStyle,
+ amIPresenter,
}
}
/>
@@ -49,11 +52,10 @@ const RAISE_HAND_BUTTON_ENABLED = Meteor.settings.public.app.raiseHandActionButt
const OLD_MINIMIZE_BUTTON_ENABLED = Meteor.settings.public.presentation.oldMinimizeButton;
export default withTracker(() => ({
- amIPresenter: Service.amIPresenter(),
amIModerator: Service.amIModerator(),
stopExternalVideoShare: ExternalVideoService.stopWatching,
enableVideo: getFromUserSettings('bbb_enable_video', Meteor.settings.public.kurento.enableVideo),
- isLayoutSwapped: getSwapLayout()&& shouldEnableSwapLayout(),
+ isLayoutSwapped: getSwapLayout() && shouldEnableSwapLayout(),
toggleSwapLayout: MediaService.toggleSwapLayout,
handleTakePresenter: Service.takePresenterRole,
currentSlidHasContent: PresentationService.currentSlidHasContent(),
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/component.jsx
index 86e19e009e..8af753071d 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/component.jsx
@@ -183,7 +183,7 @@ const ScreenshareButton = ({
if (isSafari && !ScreenshareBridgeService.HAS_DISPLAY_MEDIA) {
renderScreenshareUnavailableModal();
} else {
- shareScreen(handleFailure);
+ shareScreen(amIPresenter, handleFailure);
}
}}
id={isVideoBroadcasting ? 'unshare-screen-button' : 'share-screen-button'}
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/styles.js b/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/styles.js
index 10a3bd33e2..2ee465ac81 100644
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/screenshare/styles.js
@@ -1,7 +1,7 @@
import styled from 'styled-components';
import Modal from '/imports/ui/components/modal/simple/component';
import Button from '/imports/ui/components/button/component';
-import { colorBackground, colorWhite } from '/imports/ui/stylesheets/styled-components/palette';
+import { colorGrayDark, colorWhite } from '/imports/ui/stylesheets/styled-components/palette';
import {
jumboPaddingY,
minModalHeight,
@@ -19,7 +19,7 @@ const ScreenShareModal = styled(Modal)`
const Title = styled.h3`
font-weight: ${headingsFontWeight};
font-size: ${fontSizeLarge};
- color: ${colorBackground};
+ color: ${colorGrayDark};
white-space: normal;
padding-bottom: ${mdPaddingX};
`;
diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/service.js b/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
index d6f784b1b9..2f295970af 100755
--- a/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
+++ b/bigbluebutton-html5/imports/ui/components/actions-bar/service.js
@@ -1,8 +1,8 @@
import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import { makeCall } from '/imports/ui/services/api';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
+import Meetings from '/imports/api/meetings';
+import Breakouts from '/imports/api/breakouts';
import { getVideoUrl } from '/imports/ui/components/external-video-player/service';
const USER_CONFIG = Meteor.settings.public.user;
@@ -13,27 +13,16 @@ const getBreakouts = () => Breakouts.find({ parentMeetingId: Auth.meetingID })
.fetch()
.sort((a, b) => a.sequence - b.sequence);
-const currentBreakoutUsers = user => !Breakouts.findOne({
+const currentBreakoutUsers = (user) => !Breakouts.findOne({
'joinedUsers.userId': new RegExp(`^${user.userId}`),
});
-const filterBreakoutUsers = filter => users => users.filter(filter);
+const filterBreakoutUsers = (filter) => (users) => users.filter(filter);
const getUsersNotAssigned = filterBreakoutUsers(currentBreakoutUsers);
const takePresenterRole = () => makeCall('assignPresenter', Auth.userID);
-const amIPresenter = () => {
- const currentUser = Users.findOne({ userId: Auth.userID },
- { fields: { presenter: 1 } });
-
- if (!currentUser) {
- return false;
- }
-
- return currentUser.presenter;
-};
-
const amIModerator = () => {
const currentUser = Users.findOne({ userId: Auth.userID },
{ fields: { role: 1 } });
@@ -45,11 +34,9 @@ const amIModerator = () => {
return currentUser.role === ROLE_MODERATOR;
};
-const isMe = intId => intId === Auth.userID;
-
+const isMe = (intId) => intId === Auth.userID;
export default {
- amIPresenter,
amIModerator,
isMe,
currentUser: () => Users.findOne({ meetingId: Auth.meetingID, userId: Auth.userID },
diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx
index 3509d7e217..88c07f6ba0 100755
--- a/bigbluebutton-html5/imports/ui/components/app/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx
@@ -3,8 +3,8 @@ import { withTracker } from 'meteor/react-meteor-data';
import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
import { notify } from '/imports/ui/services/notification';
import CaptionsContainer from '/imports/ui/components/captions/container';
import CaptionsService from '/imports/ui/components/captions/service';
@@ -13,11 +13,11 @@ import deviceInfo from '/imports/utils/deviceInfo';
import UserInfos from '/imports/api/users-infos';
import Settings from '/imports/ui/services/settings';
import MediaService from '/imports/ui/components/media/service';
-import {
- layoutSelect,
- layoutSelectInput,
- layoutSelectOutput,
- layoutDispatch
+import {
+ layoutSelect,
+ layoutSelectInput,
+ layoutSelectOutput,
+ layoutDispatch,
} from '../layout/context';
import {
@@ -119,10 +119,6 @@ const currentUserEmoji = (currentUser) => (currentUser
);
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
- if (Auth.connectionID !== Meteor.connection._lastSessionId) {
- endMeeting('403');
- }
-
Users.find({ userId: Auth.userID, meetingId: Auth.meetingID }).observe({
removed() {
endMeeting('403');
@@ -168,7 +164,7 @@ export default injectIntl(withModalMounter(withTracker(({ intl, baseControls })
const { viewScreenshare } = Settings.dataSaving;
const shouldShowExternalVideo = MediaService.shouldShowExternalVideo();
const shouldShowScreenshare = MediaService.shouldShowScreenshare()
- && (viewScreenshare || MediaService.isUserPresenter());
+ && (viewScreenshare || currentUser?.presenter);
let customStyleUrl = getFromUserSettings('bbb_custom_style_url', false);
if (!customStyleUrl && CUSTOM_STYLE_URL) {
diff --git a/bigbluebutton-html5/imports/ui/components/app/service.js b/bigbluebutton-html5/imports/ui/components/app/service.js
index 31505c0918..1de523d006 100644
--- a/bigbluebutton-html5/imports/ui/components/app/service.js
+++ b/bigbluebutton-html5/imports/ui/components/app/service.js
@@ -1,5 +1,5 @@
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Breakouts from '/imports/api/breakouts';
+import Meetings from '/imports/api/meetings';
import Settings from '/imports/ui/services/settings';
import Auth from '/imports/ui/services/auth/index';
import deviceInfo from '/imports/utils/deviceInfo';
diff --git a/bigbluebutton-html5/imports/ui/components/app/styles.js b/bigbluebutton-html5/imports/ui/components/app/styles.js
index 321d2fec9d..6957019541 100644
--- a/bigbluebutton-html5/imports/ui/components/app/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/app/styles.js
@@ -1,6 +1,7 @@
import styled from 'styled-components';
import { barsPadding } from '/imports/ui/stylesheets/styled-components/general';
import { FlexColumn } from '/imports/ui/stylesheets/styled-components/placeholders';
+import { colorBackground } from '/imports/ui/stylesheets/styled-components/palette';
const CaptionsWrapper = styled.div`
height: auto;
@@ -17,7 +18,7 @@ const ActionsBar = styled.section`
`;
const Layout = styled(FlexColumn)`
- background-color: #06172a;
+ background-color: ${colorBackground};
`;
export default {
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
index 485b085461..9d216ada0d 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/container.jsx
@@ -4,7 +4,7 @@ import { withModalMounter } from '/imports/ui/components/modal/service';
import browserInfo from '/imports/utils/browserInfo';
import getFromUserSettings from '/imports/ui/services/users-settings';
import AudioModal from './component';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
import AudioError from '/imports/ui/services/audio-manager/error-codes';
diff --git a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js
index b4221ae359..d5e165fd1a 100644
--- a/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/audio/audio-modal/styles.js
@@ -4,7 +4,7 @@ import Modal from '/imports/ui/components/modal/simple/component';
import { smallOnly } from '/imports/ui/stylesheets/styled-components/breakpoints';
import {
colorPrimary,
- colorBackground,
+ colorGrayDark,
} from '/imports/ui/stylesheets/styled-components/palette';
import {
mdPaddingY,
@@ -113,7 +113,7 @@ const Title = styled.h2`
text-align: center;
font-weight: 400;
font-size: 1.3rem;
- color: ${colorBackground};
+ color: ${colorGrayDark};
white-space: normal;
@media ${smallOnly} {
diff --git a/bigbluebutton-html5/imports/ui/components/audio/container.jsx b/bigbluebutton-html5/imports/ui/components/audio/container.jsx
index fbc023ba62..c40a2e7d20 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/audio/container.jsx
@@ -5,7 +5,7 @@ import { Session } from 'meteor/session';
import { withModalMounter } from '/imports/ui/components/modal/service';
import { injectIntl, defineMessages } from 'react-intl';
import _ from 'lodash';
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
+import Breakouts from '/imports/api/breakouts';
import AppService from '/imports/ui/components/app/service';
import { notify } from '/imports/ui/services/notification';
import getFromUserSettings from '/imports/ui/services/users-settings';
diff --git a/bigbluebutton-html5/imports/ui/components/audio/service.js b/bigbluebutton-html5/imports/ui/components/audio/service.js
index c13193eaa4..19c9e1330c 100755
--- a/bigbluebutton-html5/imports/ui/components/audio/service.js
+++ b/bigbluebutton-html5/imports/ui/components/audio/service.js
@@ -1,8 +1,8 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import { debounce, throttle } from 'lodash';
import AudioManager from '/imports/ui/services/audio-manager';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import { makeCall } from '/imports/ui/services/api';
import VoiceUsers from '/imports/api/voice-users';
import logger from '/imports/startup/client/logger';
diff --git a/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx b/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx
index ce615f99ae..2cc93ea802 100644
--- a/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/authenticated-handler/component.jsx
@@ -17,7 +17,7 @@ class AuthenticatedHandler extends Component {
}
static updateStatus(status, lastStatus) {
- return status.retryCount > 0 && lastStatus !== STATUS_CONNECTING ? status.status : lastStatus;
+ return lastStatus !== STATUS_CONNECTING ? status.status : lastStatus;
}
static addReconnectObservable() {
@@ -27,6 +27,7 @@ class AuthenticatedHandler extends Component {
lastStatus = AuthenticatedHandler.updateStatus(Meteor.status(), lastStatus);
if (AuthenticatedHandler.shouldAuthenticate(Meteor.status(), lastStatus)) {
+ Session.set('userWillAuth', true);
Auth.authenticate(true);
lastStatus = Meteor.status().status;
}
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
index 6a5ab7cc97..5e30a02101 100755
--- a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/component.jsx
@@ -7,7 +7,6 @@ import PropTypes from 'prop-types';
import AudioService from '../audio/service';
import VideoService from '../video-provider/service';
import { screenshareHasEnded } from '/imports/ui/components/screenshare/service';
-import UserListService from '/imports/ui/components/user-list/service';
import Styled from './styles';
const intlMessages = defineMessages({
@@ -102,6 +101,7 @@ class BreakoutJoinConfirmation extends Component {
isFreeJoin,
voiceUserJoined,
requestJoinURL,
+ amIPresenter,
} = this.props;
const { selectValue } = this.state;
@@ -122,7 +122,7 @@ class BreakoutJoinConfirmation extends Component {
VideoService.storeDeviceIds();
VideoService.exitVideo();
- if (UserListService.amIPresenter()) screenshareHasEnded();
+ if (amIPresenter) screenshareHasEnded();
if (url === '') {
logger.error({
logCode: 'breakoutjoinconfirmation_redirecting_to_url',
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx
index 6803bcb00b..2bacb867dd 100755
--- a/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-join-confirmation/container.jsx
@@ -1,17 +1,23 @@
-import React from 'react';
+import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
+import Breakouts from '/imports/api/breakouts';
import Auth from '/imports/ui/services/auth';
import { makeCall } from '/imports/ui/services/api';
import breakoutService from '/imports/ui/components/breakout-room/service';
import AudioManager from '/imports/ui/services/audio-manager';
import BreakoutJoinConfirmationComponent from './component';
+import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
-const BreakoutJoinConfirmationContrainer = (props) => (
- {
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
+
+ return
-);
+};
const getURL = (breakoutId) => {
const currentUserId = Auth.userID;
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
index 8ff607566f..16cd82e013 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/component.jsx
@@ -10,7 +10,6 @@ import BreakoutRoomContainer from './breakout-remaining-time/container';
import VideoService from '/imports/ui/components/video-provider/service';
import { PANELS, ACTIONS } from '../layout/enums';
import { screenshareHasEnded } from '/imports/ui/components/screenshare/service';
-import UserListService from '/imports/ui/components/user-list/service';
import AudioManager from '/imports/ui/services/audio-manager';
import Settings from '/imports/ui/services/settings';
@@ -249,6 +248,7 @@ class BreakoutRoom extends PureComponent {
const {
isMicrophoneUser,
amIModerator,
+ amIPresenter,
intl,
isUserInBreakoutRoom,
exitAudio,
@@ -323,7 +323,7 @@ class BreakoutRoom extends PureComponent {
}, 'joining breakout room closed audio in the main room');
VideoService.storeDeviceIds();
VideoService.exitVideo();
- if (UserListService.amIPresenter()) screenshareHasEnded();
+ if (amIPresenter) screenshareHasEnded();
}}
disabled={disable}
/>
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
index 34481c2202..e6ec3cdb11 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/container.jsx
@@ -1,15 +1,23 @@
-import React from 'react';
+import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import AudioService from '/imports/ui/components/audio/service';
import AudioManager from '/imports/ui/services/audio-manager';
import BreakoutComponent from './component';
import Service from './service';
import { layoutDispatch } from '../layout/context';
+import Auth from '/imports/ui/services/auth';
+import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
const BreakoutContainer = (props) => {
const layoutContextDispatch = layoutDispatch();
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
- return ;
+ return ;
};
export default withTracker((props) => {
diff --git a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
index 5d3d33c06e..1313b4293e 100644
--- a/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
+++ b/bigbluebutton-html5/imports/ui/components/breakout-room/service.js
@@ -1,9 +1,9 @@
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
+import Breakouts from '/imports/api/breakouts';
import { MeetingTimeRemaining } from '/imports/api/meetings';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import { makeCall } from '/imports/ui/services/api';
import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import UserListService from '/imports/ui/components/user-list/service';
import fp from 'lodash/fp';
diff --git a/bigbluebutton-html5/imports/ui/components/button/base/component.jsx b/bigbluebutton-html5/imports/ui/components/button/base/component.jsx
index cad78aacf3..ed862ceea4 100644
--- a/bigbluebutton-html5/imports/ui/components/button/base/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/button/base/component.jsx
@@ -161,6 +161,7 @@ export default class ButtonBase extends React.Component {
'animations',
'small',
'full',
+ 'iconRight',
];
return (
diff --git a/bigbluebutton-html5/imports/ui/components/button/component.jsx b/bigbluebutton-html5/imports/ui/components/button/component.jsx
index 6e7665c045..659e05a9a0 100755
--- a/bigbluebutton-html5/imports/ui/components/button/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/button/component.jsx
@@ -155,11 +155,6 @@ export default class Button extends BaseButton {
const remainingProps = this._cleanProps(otherProps);
- /* TODO: We can change this and make the button with flexbox to avoid html
- changes */
- const renderLeftFuncName = !iconRight ? 'renderIcon' : 'renderLabel';
- const renderRightFuncName = !iconRight ? 'renderLabel' : 'renderIcon';
-
return (
- {this[renderLeftFuncName]()}
- {this[renderRightFuncName]()}
+ {this.renderIcon()}
+ {this.renderLabel()}
);
}
diff --git a/bigbluebutton-html5/imports/ui/components/button/styles.js b/bigbluebutton-html5/imports/ui/components/button/styles.js
index 682ac3e808..1ce5df8460 100644
--- a/bigbluebutton-html5/imports/ui/components/button/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/button/styles.js
@@ -599,9 +599,55 @@ const ButtonSpan = styled.span`
`;
const Button = styled(BaseButton)`
+ border: none;
+ overflow: visible;
+ display: inline-block;
+ border-radius: ${borderSize};
font-weight: ${btnFontWeight};
line-height: 1;
text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ cursor: pointer;
+ user-select: none;
+
+ &:-moz-focusring {
+ outline: none;
+ }
+
+ &:hover,
+ &:focus {
+ outline: transparent;
+ outline-style: dotted;
+ outline-width: ${borderSize};
+ text-decoration: none;
+ }
+
+ &:active,
+ &:focus {
+ outline: transparent;
+ outline-width: ${borderSize};
+ outline-style: solid;
+ }
+
+ &:active {
+ background-image: none;
+ }
+
+ &[aria-disabled="true"] {
+ cursor: not-allowed;
+ opacity: .65;
+ box-shadow: none;
+ }
+
+ &,
+ &:active {
+ &:focus {
+ span:first-of-type::before {
+ border-radius: ${borderSize};
+ }
+ }
+ }
${({ size }) => size === 'sm' && `
font-size: calc(${fontSizeSmall} * .85);
@@ -967,6 +1013,11 @@ const Button = styled(BaseButton)`
display: block;
width: 100%;
`}
+
+ ${({ iconRight }) => iconRight && `
+ display: flex;
+ flex-direction: row-reverse;
+ `}
`;
export default {
diff --git a/bigbluebutton-html5/imports/ui/components/captions/service.js b/bigbluebutton-html5/imports/ui/components/captions/service.js
index f56cb79bd6..f503836cbd 100644
--- a/bigbluebutton-html5/imports/ui/components/captions/service.js
+++ b/bigbluebutton-html5/imports/ui/components/captions/service.js
@@ -1,6 +1,6 @@
import _ from 'lodash';
import Captions from '/imports/api/captions';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import { makeCall } from '/imports/ui/services/api';
import { Meteor } from 'meteor/meteor';
diff --git a/bigbluebutton-html5/imports/ui/components/captions/writer-menu/styles.js b/bigbluebutton-html5/imports/ui/components/captions/writer-menu/styles.js
index c1be98766c..f0c4468188 100644
--- a/bigbluebutton-html5/imports/ui/components/captions/writer-menu/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/captions/writer-menu/styles.js
@@ -4,7 +4,7 @@ import { borderSize, borderSizeLarge } from '/imports/ui/stylesheets/styled-comp
import {
colorWhite,
colorLink,
- colorBackground,
+ colorGrayDark,
colorGrayLighter,
colorGrayLabel,
colorPrimary,
@@ -29,7 +29,7 @@ const Title = styled(HeaderElipsis)`
text-align: center;
font-weight: 400;
font-size: 1.3rem;
- color: ${colorBackground};
+ color: ${colorGrayDark};
white-space: normal;
flex: 1;
margin: 0;
diff --git a/bigbluebutton-html5/imports/ui/components/chat/chat-dropdown/container.jsx b/bigbluebutton-html5/imports/ui/components/chat/chat-dropdown/container.jsx
index 0496e4716d..c822a3a8ee 100644
--- a/bigbluebutton-html5/imports/ui/components/chat/chat-dropdown/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/chat/chat-dropdown/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
import ChatDropdown from './component';
diff --git a/bigbluebutton-html5/imports/ui/components/chat/message-form/typing-indicator/container.jsx b/bigbluebutton-html5/imports/ui/components/chat/message-form/typing-indicator/container.jsx
index b48fe65c65..5a3a617cd8 100644
--- a/bigbluebutton-html5/imports/ui/components/chat/message-form/typing-indicator/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/chat/message-form/typing-indicator/container.jsx
@@ -2,8 +2,8 @@ import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Auth from '/imports/ui/services/auth';
import { UsersTyping } from '/imports/api/group-chat-msg';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
import TypingIndicator from './component';
const CHAT_CONFIG = Meteor.settings.public.chat;
diff --git a/bigbluebutton-html5/imports/ui/components/chat/service.js b/bigbluebutton-html5/imports/ui/components/chat/service.js
index 10b0e14391..bd426b6277 100755
--- a/bigbluebutton-html5/imports/ui/components/chat/service.js
+++ b/bigbluebutton-html5/imports/ui/components/chat/service.js
@@ -1,5 +1,5 @@
import Users from '/imports/api/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import GroupChat from '/imports/api/group-chat';
import Auth from '/imports/ui/services/auth';
import UnreadMessages from '/imports/ui/services/unread-messages';
diff --git a/bigbluebutton-html5/imports/ui/components/components-data/chat-context/adapter.jsx b/bigbluebutton-html5/imports/ui/components/components-data/chat-context/adapter.jsx
index f6656aa2e8..a7d9a65b69 100644
--- a/bigbluebutton-html5/imports/ui/components/components-data/chat-context/adapter.jsx
+++ b/bigbluebutton-html5/imports/ui/components/components-data/chat-context/adapter.jsx
@@ -5,7 +5,7 @@ import { UsersContext } from '../users-context/context';
import { makeCall } from '/imports/ui/services/api';
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
import Auth from '/imports/ui/services/auth';
-import CollectionEventsBroker from '/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks';
+import CollectionEventsBroker from '/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker';
let prevUserData = {};
let currentUserData = {};
diff --git a/bigbluebutton-html5/imports/ui/components/components-data/chat-context/context.jsx b/bigbluebutton-html5/imports/ui/components/components-data/chat-context/context.jsx
index ba859023f8..738fcce96a 100644
--- a/bigbluebutton-html5/imports/ui/components/components-data/chat-context/context.jsx
+++ b/bigbluebutton-html5/imports/ui/components/components-data/chat-context/context.jsx
@@ -3,7 +3,7 @@ import React, {
useReducer,
} from 'react';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import Storage from '/imports/ui/services/storage/session';
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
diff --git a/bigbluebutton-html5/imports/ui/components/components-data/users-context/adapter.jsx b/bigbluebutton-html5/imports/ui/components/components-data/users-context/adapter.jsx
index 3328818731..dfa32a6eb3 100644
--- a/bigbluebutton-html5/imports/ui/components/components-data/users-context/adapter.jsx
+++ b/bigbluebutton-html5/imports/ui/components/components-data/users-context/adapter.jsx
@@ -1,6 +1,6 @@
import { useContext, useEffect } from 'react';
import { CurrentUser } from '/imports/api/users';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import UsersPersistentData from '/imports/api/users-persistent-data';
import { UsersContext, ACTIONS } from './context';
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/icon/styles.js b/bigbluebutton-html5/imports/ui/components/connection-status/icon/styles.js
index 42dee3c39c..81714a5160 100644
--- a/bigbluebutton-html5/imports/ui/components/connection-status/icon/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/icon/styles.js
@@ -46,6 +46,7 @@ const SignalBars = styled.div`
const Bar = styled.div`
width: 20%;
+ border-radius: .46875em;
`;
const FirstBar = styled(Bar)`
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx
index 6fef3010e7..981c8b5073 100644
--- a/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/modal/component.jsx
@@ -6,6 +6,7 @@ import Icon from '/imports/ui/components/connection-status/icon/component';
import Switch from '/imports/ui/components/switch/component';
import Service from '../service';
import Styled from './styles';
+import ConnectionStatusHelper from '../status-helper/container';
const NETWORK_MONITORING_INTERVAL_MS = 2000;
const MIN_TIMEOUT = 3000;
@@ -91,6 +92,42 @@ const intlMessages = defineMessages({
id: 'app.connection-status.lostPackets',
description: 'Number of lost packets',
},
+ audioUploadRate: {
+ id: 'app.connection-status.audioUploadRate',
+ description: 'Label for audio current upload rate',
+ },
+ audioDownloadRate: {
+ id: 'app.connection-status.audioDownloadRate',
+ description: 'Label for audio current download rate',
+ },
+ videoUploadRate: {
+ id: 'app.connection-status.videoUploadRate',
+ description: 'Label for video current upload rate',
+ },
+ videoDownloadRate: {
+ id: 'app.connection-status.videoDownloadRate',
+ description: 'Label for video current download rate',
+ },
+ connectionStats: {
+ id: 'app.connection-status.connectionStats',
+ description: 'Label for Connection Stats tab',
+ },
+ myLogs: {
+ id: 'app.connection-status.myLogs',
+ description: 'Label for My Logs tab',
+ },
+ sessionLogs: {
+ id: 'app.connection-status.sessionLogs',
+ description: 'Label for Session Logs tab',
+ },
+ next: {
+ id: 'app.connection-status.next',
+ description: 'Label for the next page of the connection stats tab',
+ },
+ prev: {
+ id: 'app.connection-status.prev',
+ description: 'Label for the previous page of the connection stats tab',
+ },
});
const propTypes = {
@@ -121,6 +158,8 @@ class ConnectionStatusComponent extends PureComponent {
this.help = Service.getHelp();
this.state = {
+ selectedTab: '1',
+ dataPage: '1',
dataSaving: props.dataSaving,
hasNetworkData: false,
networkData: {
@@ -142,9 +181,10 @@ class ConnectionStatusComponent extends PureComponent {
};
this.displaySettingsStatus = this.displaySettingsStatus.bind(this);
this.rateInterval = null;
-
- this.audioLabel = (intl.formatMessage(intlMessages.audioLabel)).charAt(0);
- this.videoLabel = (intl.formatMessage(intlMessages.videoLabel)).charAt(0);
+ this.audioUploadLabel = intl.formatMessage(intlMessages.audioUploadRate);
+ this.audioDownloadLabel = intl.formatMessage(intlMessages.audioDownloadRate);
+ this.videoUploadLabel = intl.formatMessage(intlMessages.videoUploadRate);
+ this.videoDownloadLabel = intl.formatMessage(intlMessages.videoDownloadRate);
}
async componentDidMount() {
@@ -222,13 +262,13 @@ class ConnectionStatusComponent extends PureComponent {
const { intl } = this.props;
return (
-
+
-
+
{intl.formatMessage(intlMessages.empty)}
-
+
);
@@ -280,15 +320,23 @@ class ConnectionStatusComponent extends PureComponent {
intl,
} = this.props;
+ const { selectedTab } = this.state;
+
if (isConnectionStatusEmpty(connectionStatus)) return this.renderEmpty();
- return connectionStatus.map((conn, index) => {
+ let connections = connectionStatus;
+ if (selectedTab === '2') {
+ connections = connections.filter(conn => conn.you);
+ if (isConnectionStatusEmpty(connections)) return this.renderEmpty();
+ }
+
+ return connections.map((conn, index) => {
const dateTime = new Date(conn.timestamp);
return (
@@ -411,13 +459,15 @@ class ConnectionStatusComponent extends PureComponent {
}
const {
- audioLabel,
- videoLabel,
+ audioUploadLabel,
+ audioDownloadLabel,
+ videoUploadLabel,
+ videoDownloadLabel,
} = this;
- const { intl } = this.props;
+ const { intl, closeModal } = this.props;
- const { networkData } = this.state;
+ const { networkData, dataSaving, dataPage } = this.state;
const {
audioCurrentUploadRate,
@@ -447,29 +497,103 @@ class ConnectionStatusComponent extends PureComponent {
}
}
+ function handlePaginationClick(action) {
+ if (action === 'next') {
+ this.setState({ dataPage: '2' });
+ }
+ else {
+ this.setState({ dataPage: '1' });
+ }
+ }
+
return (
-
- {`↑${audioLabel}: ${audioCurrentUploadRate} k`}
-
-
- {`↓${audioLabel}: ${audioCurrentDownloadRate} k`}
-
-
- {`↑${videoLabel}: ${videoCurrentUploadRate} k`}
-
-
- {`↓${videoLabel}: ${videoCurrentDownloadRate} k`}
-
-
- {`${intl.formatMessage(intlMessages.jitter)}: ${jitter} ms`}
-
-
- {`${intl.formatMessage(intlMessages.lostPackets)}: ${packetsLost}`}
-
-
- {`${intl.formatMessage(intlMessages.usingTurn)}: ${isUsingTurn}`}
-
+
+
+
+
+
+
+
+
+ closeModal(dataSaving, intl)} />
+
+
+
+
+ {`${audioUploadLabel}`}
+ {`${audioCurrentUploadRate}k ↑`}
+
+
+ {`${videoUploadLabel}`}
+ {`${videoCurrentUploadRate}k ↑`}
+
+
+ {`${intl.formatMessage(intlMessages.jitter)}`}
+ {`${jitter} ms`}
+
+
+ {`${intl.formatMessage(intlMessages.usingTurn)}`}
+ {`${isUsingTurn}`}
+
+
+
+
+
+ {`${audioDownloadLabel}`}
+ {`${audioCurrentDownloadRate}k ↓`}
+
+
+ {`${videoDownloadLabel}`}
+ {`${videoCurrentDownloadRate}k ↓`}
+
+
+ {`${intl.formatMessage(intlMessages.lostPackets)}`}
+ {`${packetsLost}`}
+
+
+ Content Hidden
+ 0
+
+
+
+
+
+
+
+
+
+
);
}
@@ -503,13 +627,69 @@ class ConnectionStatusComponent extends PureComponent {
);
}
+ /**
+ * The navigation bar.
+ * @returns {Object} The component to be renderized.
+ */
+ renderNavigation() {
+ const { intl } = this.props;
+
+ const handleTabClick = (event) => {
+ const activeTabElement = document.querySelector('.activeConnectionStatusTab');
+ const { target } = event;
+
+ if (activeTabElement) {
+ activeTabElement.classList.remove('activeConnectionStatusTab');
+ }
+
+ target.classList.add('activeConnectionStatusTab');
+ this.setState({
+ selectedTab: target.dataset.tab,
+ });
+ }
+
+ return (
+
+
+ {intl.formatMessage(intlMessages.connectionStats)}
+
+
+ {intl.formatMessage(intlMessages.myLogs)}
+
+ {Service.isModerator()
+ && (
+
+ {intl.formatMessage(intlMessages.sessionLogs)}
+
+ )
+ }
+
+ );
+ }
+
render() {
const {
closeModal,
intl,
} = this.props;
- const { dataSaving } = this.state;
+ const { dataSaving, selectedTab } = this.state;
return (
-
- {intl.formatMessage(intlMessages.description)}
- {' '}
- {this.help
- && (
-
- {`(${intl.formatMessage(intlMessages.more)})`}
-
- )
+ {this.renderNavigation()}
+
+
+ {selectedTab === '1'
+ ? this.renderNetworkData()
+ : this.renderConnections()
+ }
+
+ {selectedTab === '1' &&
+ this.renderCopyDataButton()
}
-
- {this.renderNetworkData()}
- {this.renderCopyDataButton()}
- {this.renderDataSaving()}
-
-
- {this.renderConnections()}
-
-
+
);
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.js b/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.js
index b992850a33..09248ba583 100644
--- a/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/modal/styles.js
@@ -5,28 +5,37 @@ import {
colorGray,
colorGrayDark,
colorGrayLabel,
+ colorGrayLightest,
+ colorPrimary,
} from '/imports/ui/stylesheets/styled-components/palette';
import {
smPaddingX,
smPaddingY,
lgPaddingY,
lgPaddingX,
- modalMargin,
titlePositionLeft,
+ mdPaddingX,
+ borderSizeLarge,
+ jumboPaddingY,
} from '/imports/ui/stylesheets/styled-components/general';
import {
fontSizeSmall,
- fontSizeLarge,
+ fontSizeXL,
} from '/imports/ui/stylesheets/styled-components/typography';
-import { hasPhoneDimentions } from '/imports/ui/stylesheets/styled-components/breakpoints';
+import {
+ hasPhoneDimentions,
+ mediumDown,
+ hasPhoneWidth,
+} from '/imports/ui/stylesheets/styled-components/breakpoints';
const Item = styled.div`
display: flex;
width: 100%;
height: 4rem;
+ border-bottom: 1px solid ${colorGrayLightest};
- ${({ even }) => even && `
- background-color: ${colorOffWhite};
+ ${({ last }) => last && `
+ border: none;
`}
`;
@@ -37,11 +46,19 @@ const Left = styled.div`
`;
const Name = styled.div`
- display: grid;
- width: 100%;
+ display: flex;
+ width: 27.5%;
height: 100%;
align-items: center;
- justify-content: center;
+ justify-content: flex-start;
+
+ @media ${hasPhoneDimentions} {
+ width: 100%;
+ }
+`;
+
+const FullName = styled(Name)`
+ width: 100%;
`;
const Text = styled.div`
@@ -51,8 +68,13 @@ const Text = styled.div`
text-overflow: ellipsis;
${({ offline }) => offline && `
- font-style: italic;
+ font-style: italic;
`}
+
+ [dir="rtl"] & {
+ padding: 0;
+ padding-right: .5rem;
+ }
`;
const ToggleLabel = styled.span`
@@ -65,7 +87,6 @@ const ToggleLabel = styled.span`
const Avatar = styled.div`
display: flex;
- width: 4rem;
height: 100%;
justify-content: center;
align-items: center;
@@ -87,6 +108,7 @@ const Time = styled.div`
align-items: center;
width: 100%;
height: 100%;
+ justify-content: flex-end;
`;
const DataSaving = styled.div`
@@ -149,30 +171,46 @@ const Label = styled.span`
const NetworkDataContainer = styled.div`
width: 100%;
+ height: 100%;
display: flex;
- background-color: ${colorOffWhite};
+
+ @media ${mediumDown} {
+ justify-content: space-between;
+ }
`;
const NetworkData = styled.div`
- float: left;
font-size: ${fontSizeSmall};
- margin-left: ${smPaddingX};
+
+ ${({ invisible }) => invisible && `
+ visibility: hidden;
+ `}
+
+ & :first-child {
+ font-weight: 600;
+ }
`;
const CopyContainer = styled.div`
width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ border: none;
+ border-top: 1px solid ${colorOffWhite};
+ padding: ${jumboPaddingY} 0 0;
`;
const ConnectionStatusModal = styled(Modal)`
- padding: ${smPaddingY};
+ padding: 1.5rem;
+ border-radius: 7.5px;
+
+ @media ${hasPhoneDimentions} {
+ padding: 1rem;
+ }
`;
const Container = styled.div`
- margin: 0 ${modalMargin} ${lgPaddingX};
-
- @media ${hasPhoneDimentions} {
- margin: 0 1rem;
- }
+ padding: 0 calc(${mdPaddingX} / 2 + ${borderSizeLarge});
`;
const Header = styled.div`
@@ -184,16 +222,14 @@ const Header = styled.div`
`;
const Title = styled.h2`
- left: ${titlePositionLeft};
- right: auto;
color: ${colorGrayDark};
- font-weight: bold;
- font-size: ${fontSizeLarge};
- text-align: center;
+ font-weight: 500;
+ font-size: ${fontSizeXL};
+ text-align: left;
+ margin: 0;
[dir="rtl"] & {
- left: auto;
- right: ${titlePositionLeft};
+ text-align: right;
}
`;
@@ -219,14 +255,11 @@ const Status = styled.div`
`;
const Copy = styled.span`
- float: right;
- text-decoration: underline;
cursor: pointer;
- margin-right: ${smPaddingX};
+ color: ${colorPrimary};
- [dir="rtl"] & {
- margin-left: ${smPaddingX};
- float: left;
+ &:hover {
+ text-decoration: underline;
}
${({ disabled }) => disabled && `
@@ -234,6 +267,175 @@ const Copy = styled.span`
`}
`;
+const Helper = styled.div`
+ width: 12.5rem;
+ height: 100%;
+ border-radius: .5rem;
+ background-color: ${colorOffWhite};
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+
+ @media ${mediumDown} {
+ ${({ page }) => page === '1'
+ ? 'display: flex;'
+ : 'display: none;'}
+ }
+`;
+
+const NetworkDataContent = styled.div`
+ margin: 0;
+ display: flex;
+ justify-content: space-around;
+ flex-grow: 1;
+
+ @media ${mediumDown} {
+ ${({ page }) => page === '2'
+ ? 'display: flex;'
+ : 'display: none;'}
+ }
+`;
+
+const DataColumn = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ @media ${hasPhoneWidth} {
+ flex-grow: 1;
+ }
+`;
+
+const Main = styled.div`
+ height: 19.5rem;
+ display: flex;
+ flex-direction: column;
+`;
+
+const Body = styled.div`
+ padding: ${jumboPaddingY} 0;
+ margin: 0;
+ flex-grow: 1;
+ overflow: auto;
+ position: relative;
+`;
+
+const Navigation = styled.div`
+ display: flex;
+ border: none;
+ border-bottom: 1px solid ${colorOffWhite};
+ user-select: none;
+ overflow-y: auto;
+ scrollbar-width: none;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ & :not(:last-child) {
+ margin: 0;
+ margin-right: ${lgPaddingX};
+ }
+
+ .activeConnectionStatusTab {
+ border: none;
+ border-bottom: 2px solid ${colorPrimary};
+ color: ${colorPrimary};
+ }
+
+ & * {
+ cursor: pointer;
+ white-space: nowrap;
+ }
+
+ [dir="rtl"] & {
+ & :not(:last-child) {
+ margin: 0;
+ margin-left: ${lgPaddingX};
+ }
+ }
+`;
+
+const Prev = styled.div`
+ display: none;
+ margin: 0 .5rem 0 .25rem;
+
+ @media ${mediumDown} {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ @media ${hasPhoneWidth} {
+ margin: 0;
+ }
+`;
+
+const Next = styled(Prev)`
+ margin: 0 .25rem 0 .5rem;
+
+ @media ${hasPhoneWidth} {
+ margin: 0;
+ }
+`;
+
+const Button = styled.button`
+ flex: 0;
+ margin: 0;
+ padding: 0;
+ border: none;
+ background: none;
+ color: inherit;
+ border-radius: 50%;
+ cursor: pointer;
+
+ &:disabled {
+ cursor: not-allowed;
+ opacity: .5;
+ }
+
+ &:hover {
+ opacity: .75;
+ }
+
+ &:focus {
+ outline: none;
+ }
+
+ @media ${hasPhoneWidth} {
+ position: absolute;
+ bottom: 0;
+ padding: .25rem;
+ }
+`;
+
+const ButtonLeft = styled(Button)`
+ left: calc(50% - 2rem);
+
+ [dir="rtl"] & {
+ left: calc(50%);
+ }
+`;
+
+const ButtonRight = styled(Button)`
+ right: calc(50% - 2rem);
+
+ [dir="rtl"] & {
+ right: calc(50%);
+ }
+`;
+
+const Chevron = styled.svg`
+ display: flex;
+ width: 1rem;
+ height: 1rem;
+
+ [dir="rtl"] & {
+ transform: rotate(180deg);
+ }
+`;
+
export default {
Item,
Left,
@@ -262,4 +464,16 @@ export default {
Wrapper,
Status,
Copy,
+ Helper,
+ NetworkDataContent,
+ Main,
+ Body,
+ Navigation,
+ FullName,
+ DataColumn,
+ Prev,
+ Next,
+ ButtonLeft,
+ ButtonRight,
+ Chevron,
};
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/service.js b/bigbluebutton-html5/imports/ui/components/connection-status/service.js
index d0a9bc5f1c..62a461630c 100644
--- a/bigbluebutton-html5/imports/ui/components/connection-status/service.js
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/service.js
@@ -1,6 +1,6 @@
import { defineMessages } from 'react-intl';
import ConnectionStatus from '/imports/api/connection-status';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import UsersPersistentData from '/imports/api/users-persistent-data';
import Auth from '/imports/ui/services/auth';
import Settings from '/imports/ui/services/settings';
@@ -570,6 +570,7 @@ const calculateBitsPerSecondFromMultipleData = (currentData, previousData) => {
};
export default {
+ isModerator,
getConnectionStatus,
getStats,
getHelp,
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/component.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/component.jsx
new file mode 100644
index 0000000000..945f42caf8
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/component.jsx
@@ -0,0 +1,89 @@
+import React, { Fragment, PureComponent } from 'react';
+import { defineMessages, injectIntl } from 'react-intl';
+import Styled from './styles';
+import { withModalMounter } from '/imports/ui/components/modal/service';
+import Icon from '/imports/ui/components/connection-status/icon/component';
+import SettingsMenuContainer from '/imports/ui/components/settings/container';
+
+const intlMessages = defineMessages({
+ label: {
+ id: 'app.connection-status.label',
+ description: 'Connection status label',
+ },
+ settings: {
+ id: 'app.connection-status.settings',
+ description: 'Connection settings label',
+ },
+});
+
+class ConnectionStatusIcon extends PureComponent {
+ renderIcon(level = 'normal') {
+ return(
+
+
+
+ );
+ }
+
+ openAdjustSettings() {
+ const {
+ closeModal,
+ mountModal,
+ } = this.props;
+
+ closeModal();
+ mountModal();
+ }
+
+ render() {
+ const {
+ intl,
+ stats,
+ } = this.props;
+
+ let color;
+ switch (stats) {
+ case 'warning':
+ color = 'success';
+ break;
+ case 'danger':
+ color = 'warning';
+ break;
+ case 'critical':
+ color = 'danger';
+ break;
+ default:
+ color = 'success';
+ }
+
+ const level = stats ? stats : 'normal';
+
+ return (
+
+
+ {this.renderIcon(level)}
+
+
+ {intl.formatMessage(intlMessages.label)}
+
+ {level === 'critical' || level === 'danger'
+ && (
+
+
+ {intl.formatMessage(intlMessages.settings)}
+
+
+ )
+ }
+
+ );
+ }
+}
+
+export default withModalMounter(injectIntl(ConnectionStatusIcon));
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/container.jsx b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/container.jsx
new file mode 100644
index 0000000000..c35110cbe8
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/container.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import { withTracker } from 'meteor/react-meteor-data';
+import ConnectionStatusService from '../service';
+import ConnectionStatusIconComponent from './component';
+
+const connectionStatusIconContainer = props => ;
+
+export default withTracker(() => {
+ return {
+ stats: ConnectionStatusService.getStats(),
+ };
+})(connectionStatusIconContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/styles.js b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/styles.js
new file mode 100644
index 0000000000..d64c2a8cc2
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/components/connection-status/status-helper/styles.js
@@ -0,0 +1,45 @@
+import styled from 'styled-components';
+import {
+ colorPrimary,
+ colorSuccess,
+ colorWarning,
+ colorDanger,
+} from '/imports/ui/stylesheets/styled-components/palette';
+
+const StatusIconWrapper = styled.div`
+ border-radius: 50%;
+ padding: 1.5rem;
+
+ ${(color) => {
+ let bgColor = colorSuccess;
+ backgroundColor = color === 'warning' ? colorWarning : bgColor;
+ backgroundColor = color === 'danger' ? colorDanger : bgColor;
+ return `background-color: ${bgColor};`
+ }}
+`;
+
+const IconWrapper = styled.div`
+ width: 2.25rem;
+ height: 2.25rem;
+`;
+
+const Label = styled.div`
+ font-weight: 600;
+ margin: .25rem 0 .5rem;
+`;
+
+const Settings = styled.span`
+ color: ${colorPrimary};
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+`;
+
+export default {
+ StatusIconWrapper,
+ IconWrapper,
+ Label,
+ Settings,
+};
diff --git a/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/service.js b/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/service.js
index 65fb2aa5ea..5817cdab2c 100644
--- a/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/service.js
+++ b/bigbluebutton-html5/imports/ui/components/end-meeting-confirmation/service.js
@@ -1,6 +1,6 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
const getMeetingTitle = () => {
const meeting = Meetings.findOne({
diff --git a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
index 41b3f57797..18d1902973 100755
--- a/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/join-handler/component.jsx
@@ -24,6 +24,7 @@ class JoinHandler extends Component {
this.state = {
joined: false,
+ hasAlreadyJoined: false,
};
}
@@ -38,8 +39,8 @@ class JoinHandler extends Component {
connected,
status,
} = Meteor.status();
-
- if (status === 'connecting') {
+ const { hasAlreadyJoined } = this.state;
+ if (status === 'connecting' && !hasAlreadyJoined) {
this.setState({ joined: false });
}
@@ -83,6 +84,7 @@ class JoinHandler extends Component {
}
async fetchToken() {
+ const { hasAlreadyJoined } = this.state;
if (!this._isMounted) return;
const urlParams = new URLSearchParams(window.location.search);
@@ -94,7 +96,9 @@ class JoinHandler extends Component {
}
// Old credentials stored in memory were being used when joining a new meeting
- Auth.clearCredentials();
+ if (!hasAlreadyJoined) {
+ Auth.clearCredentials();
+ }
const logUserInfo = () => {
const userInfo = window.navigator;
@@ -215,7 +219,10 @@ class JoinHandler extends Component {
},
}, 'User faced an error on main.joinRouteHandler.');
}
- this.setState({ joined: true });
+ this.setState({
+ joined: true,
+ hasAlreadyJoined: true,
+ });
}
render() {
diff --git a/bigbluebutton-html5/imports/ui/components/learning-dashboard/service.js b/bigbluebutton-html5/imports/ui/components/learning-dashboard/service.js
index b5e9281973..4b0c9b05b4 100644
--- a/bigbluebutton-html5/imports/ui/components/learning-dashboard/service.js
+++ b/bigbluebutton-html5/imports/ui/components/learning-dashboard/service.js
@@ -1,6 +1,6 @@
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
-import Meetings from '../../../api/meetings';
+import Meetings from '/imports/api/meetings';
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
index 3bd5568d92..a7146fc523 100755
--- a/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withModalMounter } from '/imports/ui/components/modal/service';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import LockViewersComponent from './component';
import { updateLockSettings, updateWebcamsOnlyForModerator } from './service';
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/context/container.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/context/container.jsx
index 4de7e2cacc..0f33d1620c 100644
--- a/bigbluebutton-html5/imports/ui/components/lock-viewers/context/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/context/container.jsx
@@ -1,5 +1,5 @@
import { withTracker } from 'meteor/react-meteor-data';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import { LockStruct } from './context';
import { withUsersConsumer } from '/imports/ui/components/components-data/users-context/context';
diff --git a/bigbluebutton-html5/imports/ui/components/lock-viewers/notify/container.jsx b/bigbluebutton-html5/imports/ui/components/lock-viewers/notify/container.jsx
index 509fcb1c64..9363621a94 100644
--- a/bigbluebutton-html5/imports/ui/components/lock-viewers/notify/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/lock-viewers/notify/container.jsx
@@ -1,5 +1,5 @@
import { withTracker } from 'meteor/react-meteor-data';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import LockViewersNotifyComponent from './component';
diff --git a/bigbluebutton-html5/imports/ui/components/media/service.js b/bigbluebutton-html5/imports/ui/components/media/service.js
index 5f91e1caab..794117dda0 100755
--- a/bigbluebutton-html5/imports/ui/components/media/service.js
+++ b/bigbluebutton-html5/imports/ui/components/media/service.js
@@ -1,8 +1,6 @@
import Presentations from '/imports/api/presentations';
import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service';
import { getVideoUrl } from '/imports/ui/components/external-video-player/service';
-import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
import Settings from '/imports/ui/services/settings';
import getFromUserSettings from '/imports/ui/services/users-settings';
import { ACTIONS } from '../layout/enums';
@@ -21,9 +19,6 @@ const getPresentationInfo = () => {
};
};
-const isUserPresenter = () => Users.findOne({ userId: Auth.userID },
- { fields: { presenter: 1 } }).presenter;
-
function shouldShowWhiteboard() {
return true;
}
@@ -91,7 +86,6 @@ export default {
shouldShowScreenshare,
shouldShowExternalVideo,
shouldShowOverlay,
- isUserPresenter,
isVideoBroadcasting,
toggleSwapLayout,
shouldEnableSwapLayout,
diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
index 2f9f016e5e..a5e52d20ee 100755
--- a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx
@@ -10,8 +10,8 @@ import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
import Rating from './rating/component';
import Styled from './styles';
import logger from '/imports/startup/client/logger';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
import AudioManager from '/imports/ui/services/audio-manager';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
diff --git a/bigbluebutton-html5/imports/ui/components/modal/random-user/container.jsx b/bigbluebutton-html5/imports/ui/components/modal/random-user/container.jsx
index 959dc020e7..ecbd6bc9ce 100644
--- a/bigbluebutton-html5/imports/ui/components/modal/random-user/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/random-user/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
-import Users from '/imports/ui/local-collections/users-collection/users';
+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';
diff --git a/bigbluebutton-html5/imports/ui/components/modal/remove-user/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/remove-user/component.jsx
index 165fd90230..bd4884baed 100644
--- a/bigbluebutton-html5/imports/ui/components/modal/remove-user/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/remove-user/component.jsx
@@ -69,7 +69,7 @@ class RemoveUserModal extends Component {
- {
diff --git a/bigbluebutton-html5/imports/ui/components/modal/remove-user/styles.js b/bigbluebutton-html5/imports/ui/components/modal/remove-user/styles.js
index d0cd41f9aa..3efea06b18 100644
--- a/bigbluebutton-html5/imports/ui/components/modal/remove-user/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/modal/remove-user/styles.js
@@ -69,7 +69,7 @@ const Footer = styled.div`
display:flex;
`;
-const ConfirmButtom = styled(Button)`
+const ConfirmButton = styled(Button)`
padding-right: ${jumboPaddingY};
padding-left: ${jumboPaddingY};
margin: 0 ${smPaddingX} 0 0;
@@ -79,7 +79,7 @@ const ConfirmButtom = styled(Button)`
}
`;
-const DismissButtom = styled(ConfirmButtom)`
+const DismissButton = styled(ConfirmButton)`
box-shadow: 0 0 0 1px ${colorGray};
`;
@@ -91,6 +91,6 @@ export default {
Description,
BanUserCheckBox,
Footer,
- ConfirmButtom,
- DismissButtom,
+ ConfirmButton,
+ DismissButton,
};
diff --git a/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx b/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
index 58a56ed6b7..fae17916a6 100755
--- a/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modal/simple/component.jsx
@@ -81,7 +81,7 @@ class ModalSimple extends Component {
{...otherProps}
>
- {title}
+ {title}
{shouldShowCloseButton ? (
hasLeftMargin && `
+ margin-left: 35px;
+ `}
`;
const DismissButton = styled(Button)`
@@ -61,7 +65,7 @@ const Content = styled.div`
overflow: auto;
color: ${colorText};
font-weight: normal;
- padding: ${lineHeightComputed} 0;
+ padding: 0;
`;
export default {
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
index 3b1ba1b332..ef299cdb53 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import getFromUserSettings from '/imports/ui/services/users-settings';
import userListService from '/imports/ui/components/user-list/service';
diff --git a/bigbluebutton-html5/imports/ui/components/nav-bar/service.js b/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
index 6b36c1da2e..6857f00219 100755
--- a/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
+++ b/bigbluebutton-html5/imports/ui/components/nav-bar/service.js
@@ -1,6 +1,6 @@
import Auth from '/imports/ui/services/auth';
import { makeCall } from '/imports/ui/services/api';
-import RecordMeetings from '/imports/api/meetings';
+import Meetings from '/imports/api/meetings';
const processOutsideToggleRecording = (e) => {
switch (e.data) {
@@ -9,7 +9,7 @@ const processOutsideToggleRecording = (e) => {
break;
}
case 'c_recording_status': {
- const recordingState = (RecordMeetings.findOne({ meetingId: Auth.meetingID })).recording;
+ const recordingState = (Meetings.findOne({ meetingId: Auth.meetingID })).recording;
const recordingMessage = recordingState ? 'recordingStarted' : 'recordingStopped';
this.window.parent.postMessage({ response: recordingMessage }, '*');
break;
diff --git a/bigbluebutton-html5/imports/ui/components/note/service.js b/bigbluebutton-html5/imports/ui/components/note/service.js
index 8f1721a649..38740b60eb 100644
--- a/bigbluebutton-html5/imports/ui/components/note/service.js
+++ b/bigbluebutton-html5/imports/ui/components/note/service.js
@@ -1,5 +1,5 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
import Note from '/imports/api/note';
import { makeCall } from '/imports/ui/services/api';
import Auth from '/imports/ui/services/auth';
diff --git a/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx b/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx
index 38a04e9507..271b32145e 100644
--- a/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/notifications-bar/container.jsx
@@ -5,7 +5,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import _ from 'lodash';
import Auth from '/imports/ui/services/auth';
import { MeetingTimeRemaining } from '/imports/api/meetings';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import BreakoutRemainingTime from '/imports/ui/components/breakout-room/breakout-remaining-time/container';
import Styled from './styles';
import { layoutSelectInput, layoutDispatch } from '../layout/context';
diff --git a/bigbluebutton-html5/imports/ui/components/poll/container.jsx b/bigbluebutton-html5/imports/ui/components/poll/container.jsx
index c6ad1ce479..1ceedb08ec 100644
--- a/bigbluebutton-html5/imports/ui/components/poll/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/poll/container.jsx
@@ -18,6 +18,7 @@ const PollContainer = ({ ...props }) => {
const usingUsersContext = useContext(UsersContext);
const { users } = usingUsersContext;
+ const amIPresenter = users[Auth.meetingID][Auth.userID].presenter;
const usernames = {};
@@ -25,7 +26,13 @@ const PollContainer = ({ ...props }) => {
usernames[user.userId] = { userId: user.userId, name: user.name };
});
- return ;
+ return (
+
+ );
};
export default withTracker(() => {
@@ -50,7 +57,6 @@ export default withTracker(() => {
return {
currentSlide,
- amIPresenter: Service.amIPresenter(),
pollTypes,
startPoll,
startCustomPoll,
diff --git a/bigbluebutton-html5/imports/ui/components/poll/service.js b/bigbluebutton-html5/imports/ui/components/poll/service.js
index f3f942321a..4586d83cc0 100644
--- a/bigbluebutton-html5/imports/ui/components/poll/service.js
+++ b/bigbluebutton-html5/imports/ui/components/poll/service.js
@@ -1,4 +1,3 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
import Auth from '/imports/ui/services/auth';
import { CurrentPoll } from '/imports/api/polls';
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';
@@ -212,10 +211,6 @@ const checkPollType = (
};
export default {
- amIPresenter: () => Users.findOne(
- { userId: Auth.userID },
- { fields: { presenter: 1 } },
- ).presenter,
pollTypes,
currentPoll: () => CurrentPoll.findOne({ meetingId: Auth.meetingID }),
pollAnswerIds,
diff --git a/bigbluebutton-html5/imports/ui/components/polling/container.jsx b/bigbluebutton-html5/imports/ui/components/polling/container.jsx
index a7c158288a..d9b8838b76 100644
--- a/bigbluebutton-html5/imports/ui/components/polling/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/polling/container.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import PollingService from './service';
import PollService from '/imports/ui/components/poll/service';
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
index a0d7692d7a..0f03081ba0 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/component.jsx
@@ -20,6 +20,7 @@ import Icon from '/imports/ui/components/icon/component';
import PollingContainer from '/imports/ui/components/polling/container';
import { ACTIONS, LAYOUT_TYPE } from '../layout/enums';
import DEFAULT_VALUES from '../layout/defaultValues';
+import { colorBackground } from '/imports/ui/stylesheets/styled-components/palette';
const intlMessages = defineMessages({
presentationLabel: {
@@ -186,7 +187,7 @@ class Presentation extends PureComponent {
this.currentPresentationToastId = toast(this.renderCurrentPresentationToast(), {
onClose: () => { this.currentPresentationToastId = null; },
autoClose: shouldCloseToast,
- className: "actionToast",
+ className: 'actionToast',
});
}
}
@@ -693,13 +694,14 @@ class Presentation extends PureComponent {
}
renderWhiteboardToolbar(svgDimensions) {
- const { currentSlide } = this.props;
+ const { currentSlide, userIsPresenter } = this.props;
if (!this.isPresentationAccessible()) return null;
return (
);
}
@@ -854,7 +856,7 @@ class Presentation extends PureComponent {
width: presentationBounds.width,
height: presentationBounds.height,
zIndex: fullscreenContext ? presentationBounds.zIndex : undefined,
- backgroundColor: '#06172A',
+ backgroundColor: colorBackground,
}}
>
{isFullscreen && }
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/container.jsx
index 469314178e..23ffed4dd9 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/container.jsx
@@ -11,7 +11,7 @@ import Presentation from '/imports/ui/components/presentation/component';
import PresentationToolbarService from './presentation-toolbar/service';
import { UsersContext } from '../components-data/users-context/context';
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import getFromUserSettings from '/imports/ui/services/users-settings';
import {
layoutSelect,
@@ -25,7 +25,7 @@ import { DEVICE_TYPE } from '../layout/enums';
const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
const PresentationContainer = ({ presentationPodIds, mountPresentation, ...props }) => {
- const { layoutSwapped, podId } = props;
+ const { layoutSwapped } = props;
const cameraDock = layoutSelectInput((i) => i.cameraDock);
const presentation = layoutSelectOutput((i) => i.presentation);
@@ -44,8 +44,7 @@ const PresentationContainer = ({ presentationPodIds, mountPresentation, ...props
const usingUsersContext = useContext(UsersContext);
const { users } = usingUsersContext;
const currentUser = users[Auth.meetingID][Auth.userID];
-
- const userIsPresenter = (podId === 'DEFAULT_PRESENTATION_POD') ? currentUser.presenter : props.isPresenter;
+ const userIsPresenter = currentUser.presenter;
return (
{
currentSlide,
slidePosition,
downloadPresentationUri: PresentationService.downloadPresentationUri(podId),
- isPresenter: PresentationService.isPresenter(podId),
multiUser: WhiteboardService.hasMultiUserAccess(currentSlide && currentSlide.id, Auth.userID)
&& !layoutSwapped,
presentationIsDownloadable,
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/cursor/service.js b/bigbluebutton-html5/imports/ui/components/presentation/cursor/service.js
index 5ef536974a..bdc1714b49 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/cursor/service.js
+++ b/bigbluebutton-html5/imports/ui/components/presentation/cursor/service.js
@@ -1,5 +1,5 @@
import Cursor from '/imports/ui/components/cursor/service';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
const getCurrentCursor = (cursorId) => {
const cursor = Cursor.findOne({ _id: cursorId });
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/container.jsx
index 8142766f6c..d68647a6c7 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-toolbar/container.jsx
@@ -1,21 +1,24 @@
-import React from 'react';
+import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
import PresentationService from '/imports/ui/components/presentation/service';
import MediaService from '/imports/ui/components/media/service';
-import Service from '/imports/ui/components/actions-bar/service';
import PollService from '/imports/ui/components/poll/service';
import { makeCall } from '/imports/ui/services/api';
import PresentationToolbar from './component';
import PresentationToolbarService from './service';
+import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
+import Auth from '/imports/ui/services/auth';
const POLLING_ENABLED = Meteor.settings.public.poll.enabled;
const PresentationToolbarContainer = (props) => {
- const {
- userIsPresenter,
- layoutSwapped,
- } = props;
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const currentUser = users[Auth.meetingID][Auth.userID];
+ const userIsPresenter = currentUser.presenter;
+
+ const { layoutSwapped } = props;
if (userIsPresenter && !layoutSwapped) {
// Only show controls if user is presenter and layout isn't swapped
@@ -23,6 +26,7 @@ const PresentationToolbarContainer = (props) => {
return (
);
}
@@ -44,9 +48,7 @@ export default withTracker((params) => {
};
return {
- amIPresenter: Service.amIPresenter(),
layoutSwapped: MediaService.getSwapLayout() && MediaService.shouldEnableSwapLayout(),
- userIsPresenter: PresentationService.isPresenter(podId),
numberOfSlides: PresentationToolbarService.getNumberOfSlides(podId, presentationId),
nextSlide: PresentationToolbarService.nextSlide,
previousSlide: PresentationToolbarService.previousSlide,
@@ -65,9 +67,6 @@ PresentationToolbarContainer.propTypes = {
zoom: PropTypes.number.isRequired,
zoomChanger: PropTypes.func.isRequired,
- // Is the user a presenter
- userIsPresenter: PropTypes.bool.isRequired,
-
// Total number of slides in this presentation
numberOfSlides: PropTypes.number.isRequired,
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/container.jsx b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/container.jsx
index 7796b9f7b3..699731ee2b 100644
--- a/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/presentation/presentation-uploader/container.jsx
@@ -1,30 +1,35 @@
-import React from 'react';
+import React, { useContext } from 'react';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import ErrorBoundary from '/imports/ui/components/error-boundary/component';
import FallbackModal from '/imports/ui/components/fallback-errors/fallback-modal/component';
import Service from './service';
-import PresentationService from '../service';
import PresentationUploader from './component';
+import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
+import Auth from '/imports/ui/services/auth';
const PRESENTATION_CONFIG = Meteor.settings.public.presentation;
-const PresentationUploaderContainer = (props) => (
- props.isPresenter
- && (
+const PresentationUploaderContainer = (props) => {
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const currentUser = users[Auth.meetingID][Auth.userID];
+ const userIsPresenter = currentUser.presenter;
+
+ return userIsPresenter && (
}>
-
+
- )
-);
+ );
+};
export default withTracker(() => {
const currentPresentations = Service.getPresentations();
const {
- dispatchDisableDownloadable,
- dispatchEnableDownloadable,
- dispatchTogglePresentationDownloadable,
- } = Service;
+ dispatchDisableDownloadable,
+ dispatchEnableDownloadable,
+ dispatchTogglePresentationDownloadable,
+ } = Service;
return {
presentations: currentPresentations,
@@ -41,6 +46,5 @@ export default withTracker(() => {
dispatchTogglePresentationDownloadable,
isOpen: Session.get('showUploadPresentationView') || false,
selectedToBeNextCurrent: Session.get('selectedToBeNextCurrent') || null,
- isPresenter: PresentationService.isPresenter('DEFAULT_PRESENTATION_POD'),
};
})(PresentationUploaderContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/presentation/service.js b/bigbluebutton-html5/imports/ui/components/presentation/service.js
index df6a9180d7..c8fff960cc 100755
--- a/bigbluebutton-html5/imports/ui/components/presentation/service.js
+++ b/bigbluebutton-html5/imports/ui/components/presentation/service.js
@@ -1,7 +1,5 @@
-import PresentationPods from '/imports/api/presentation-pods';
import Presentations from '/imports/api/presentations';
import { Slides, SlidePositions } from '/imports/api/slides';
-import Auth from '/imports/ui/services/auth';
import PollService from '/imports/ui/components/poll/service';
const POLL_SETTINGS = Meteor.settings.public.poll;
@@ -169,19 +167,9 @@ const parseCurrentSlideContent = (yesValue, noValue, abstentionValue, trueValue,
};
};
-const isPresenter = (podId) => {
- const selector = {
- meetingId: Auth.meetingID,
- podId,
- };
- const pod = PresentationPods.findOne(selector);
- return pod?.currentPresenterId === Auth.userID;
-};
-
export default {
getCurrentSlide,
getSlidePosition,
- isPresenter,
isPresentationDownloadable,
downloadPresentationUri,
currentSlidHasContent,
diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
index 2ba41a11c9..5ecc64d491 100755
--- a/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/screenshare/component.jsx
@@ -89,9 +89,10 @@ class ScreenshareComponent extends React.Component {
layoutContextDispatch,
intl,
hidePresentation,
+ isPresenter,
} = this.props;
- screenshareHasStarted();
+ screenshareHasStarted(isPresenter);
// Autoplay failure handling
window.addEventListener('screensharePlayFailed', this.handlePlayElementFailed);
// Stream health state tracker to propagate UI changes on reconnections
diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/container.jsx b/bigbluebutton-html5/imports/ui/components/screenshare/container.jsx
index ec2e1041b6..a03fdf7a55 100755
--- a/bigbluebutton-html5/imports/ui/components/screenshare/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/screenshare/container.jsx
@@ -1,6 +1,5 @@
-import React from 'react';
+import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
-import Users from '/imports/api/users/';
import Auth from '/imports/ui/services/auth';
import MediaService, {
getSwapLayout,
@@ -13,6 +12,7 @@ import {
import ScreenshareComponent from './component';
import { layoutSelect, layoutSelectOutput, layoutDispatch } from '../layout/context';
import getFromUserSettings from '/imports/ui/services/users-settings';
+import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
const ScreenshareContainer = (props) => {
const screenShare = layoutSelectOutput((i) => i.screenShare);
@@ -23,6 +23,11 @@ const ScreenshareContainer = (props) => {
const fullscreenElementId = 'Screenshare';
const fullscreenContext = (element === fullscreenElementId);
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const currentUser = users[Auth.meetingID][Auth.userID];
+ const isPresenter = currentUser.presenter;
+
if (isVideoBroadcasting()) {
return (
{
...screenShare,
fullscreenContext,
fullscreenElementId,
+ isPresenter,
}
}
/>
@@ -43,14 +49,10 @@ const ScreenshareContainer = (props) => {
const LAYOUT_CONFIG = Meteor.settings.public.layout;
-export default withTracker(() => {
- const user = Users.findOne({ userId: Auth.userID }, { fields: { presenter: 1 } });
- return {
- isGloballyBroadcasting: isGloballyBroadcasting(),
- isPresenter: user.presenter,
- getSwapLayout,
- shouldEnableSwapLayout,
- toggleSwapLayout: MediaService.toggleSwapLayout,
- hidePresentation: getFromUserSettings('bbb_hide_presentation', LAYOUT_CONFIG.hidePresentation),
- };
-})(ScreenshareContainer);
+export default withTracker(() => ({
+ isGloballyBroadcasting: isGloballyBroadcasting(),
+ getSwapLayout,
+ shouldEnableSwapLayout,
+ toggleSwapLayout: MediaService.toggleSwapLayout,
+ hidePresentation: getFromUserSettings('bbb_hide_presentation', LAYOUT_CONFIG.hidePresentation),
+}))(ScreenshareContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/screenshare/service.js b/bigbluebutton-html5/imports/ui/components/screenshare/service.js
index 27fe34d76f..2a43842644 100644
--- a/bigbluebutton-html5/imports/ui/components/screenshare/service.js
+++ b/bigbluebutton-html5/imports/ui/components/screenshare/service.js
@@ -4,9 +4,8 @@ import BridgeService from '/imports/api/screenshare/client/bridge/service';
import Settings from '/imports/ui/services/settings';
import logger from '/imports/startup/client/logger';
import { stopWatching } from '/imports/ui/components/external-video-player/service';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
-import UserListService from '/imports/ui/components/user-list/service';
import AudioService from '/imports/ui/components/audio/service';
import { Meteor } from "meteor/meteor";
import MediaStreamUtils from '/imports/utils/media-stream-utils';
@@ -97,14 +96,14 @@ const attachLocalPreviewStream = (mediaElement) => {
}
}
-const screenshareHasStarted = () => {
+const screenshareHasStarted = (isPresenter) => {
// Presenter's screen preview is local, so skip
- if (!UserListService.amIPresenter()) {
+ if (!isPresenter) {
viewScreenshare();
}
};
-const shareScreen = async (onFail) => {
+const shareScreen = async (isPresenter, onFail) => {
// stop external video share if running
const meeting = Meetings.findOne({ meetingId: Auth.meetingID });
@@ -114,7 +113,7 @@ const shareScreen = async (onFail) => {
try {
const stream = await BridgeService.getScreenStream();
- if(!UserListService.isUserPresenter(Auth.userID)) return MediaStreamUtils.stopMediaStreamTracks(stream);
+ if(!isPresenter) return MediaStreamUtils.stopMediaStreamTracks(stream);
await KurentoBridge.share(stream, onFail);
setSharingScreen(true);
} catch (error) {
diff --git a/bigbluebutton-html5/imports/ui/components/settings/component.jsx b/bigbluebutton-html5/imports/ui/components/settings/component.jsx
index 657e3d254b..a5303aa018 100644
--- a/bigbluebutton-html5/imports/ui/components/settings/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/settings/component.jsx
@@ -106,7 +106,7 @@ class Settings extends Component {
super(props);
const {
- dataSaving, application,
+ dataSaving, application, selectedTab
} = props;
this.state = {
@@ -118,7 +118,9 @@ class Settings extends Component {
dataSaving: _.clone(dataSaving),
application: _.clone(application),
},
- selectedTab: 0,
+ selectedTab: _.isFinite(selectedTab) && selectedTab >=0 && selectedTab <= 2
+ ? selectedTab
+ : 0,
};
this.updateSettings = props.updateSettings;
diff --git a/bigbluebutton-html5/imports/ui/components/settings/container.jsx b/bigbluebutton-html5/imports/ui/components/settings/container.jsx
index 7e0668e9b6..4b569d46e3 100644
--- a/bigbluebutton-html5/imports/ui/components/settings/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/settings/container.jsx
@@ -17,7 +17,8 @@ const SettingsContainer = (props) => {
return ;
};
-export default withTracker(() => ({
+export default withTracker((props) => ({
+ ...props,
audio: SettingsService.audio,
dataSaving: SettingsService.dataSaving,
application: SettingsService.application,
diff --git a/bigbluebutton-html5/imports/ui/components/settings/service.js b/bigbluebutton-html5/imports/ui/components/settings/service.js
index 355fc2f4e3..19c434dda8 100644
--- a/bigbluebutton-html5/imports/ui/components/settings/service.js
+++ b/bigbluebutton-html5/imports/ui/components/settings/service.js
@@ -1,4 +1,4 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import Settings from '/imports/ui/services/settings';
import { notify } from '/imports/ui/services/notification';
diff --git a/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx b/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx
index fa92ef1a3f..dd88d4d770 100644
--- a/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/sidebar-content/component.jsx
@@ -82,6 +82,7 @@ const SidebarContent = (props) => {
};
const smallSidebar = width < (maxWidth / 2);
+ const pollDisplay = sidebarContentPanel === PANELS.POLL ? 'inherit' : 'none';
return (
{
{sidebarContentPanel === PANELS.CHAT && }
{sidebarContentPanel === PANELS.SHARED_NOTES && }
{sidebarContentPanel === PANELS.CAPTIONS && }
- {sidebarContentPanel === PANELS.POLL
- && (
-
-
-
- )}
{sidebarContentPanel === PANELS.BREAKOUT && }
{sidebarContentPanel === PANELS.WAITING_USERS && }
+
+
+
);
};
diff --git a/bigbluebutton-html5/imports/ui/components/status-notifier/container.jsx b/bigbluebutton-html5/imports/ui/components/status-notifier/container.jsx
index e438f7242c..96a3aee273 100644
--- a/bigbluebutton-html5/imports/ui/components/status-notifier/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/status-notifier/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Settings from '/imports/ui/services/settings';
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
import { makeCall } from '/imports/ui/services/api';
diff --git a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx
index ad3894487e..1f72ec79a9 100755
--- a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx
@@ -4,12 +4,15 @@ import Auth from '/imports/ui/services/auth';
import logger from '/imports/startup/client/logger';
import GroupChat from '/imports/api/group-chat';
import Annotations from '/imports/api/annotations';
-import Users, { localUsersSync } from '/imports/ui/local-collections/users-collection/users';
-import { localBreakoutsSync } from '/imports/ui/local-collections/breakouts-collection/breakouts';
-import { localGuestUsersSync } from '/imports/ui/local-collections/guest-users-collection/guest-users';
-import { localMeetingsSync } from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
import AnnotationsTextService from '/imports/ui/components/whiteboard/annotations/text/service';
import { Annotations as AnnotationsLocal } from '/imports/ui/components/whiteboard/service';
+import {
+ localBreakoutsSync,
+ localGuestUsersSync,
+ localMeetingsSync,
+ localUsersSync,
+} from '/client/collection-mirror-initializer';
import SubscriptionRegistry, { subscriptionReactivity } from '../../services/subscription-registry/subscriptionRegistry';
const CHAT_CONFIG = Meteor.settings.public.chat;
@@ -37,6 +40,7 @@ class Subscriptions extends Component {
Session.set('subscriptionsReady', true);
const event = new Event(EVENT_NAME_SUBSCRIPTION_READY);
window.dispatchEvent(event);
+ Session.set('globalIgnoreDeletes', false);
}
}
@@ -49,6 +53,12 @@ class Subscriptions extends Component {
export default withTracker(() => {
const { credentials } = Auth;
const { meetingId, requesterUserId } = credentials;
+ const userWillAuth = Session.get('userWillAuth');
+ // This if exist because when a unauth user try to subscribe to a publisher
+ // it returns a empty collection to the subscription
+ // and not rerun when the user is authenticated
+ if (userWillAuth) return {};
+
if (Session.get('codeError')) {
return {
subscriptionsReady: true,
@@ -90,11 +100,11 @@ export default withTracker(() => {
if (oldRole === 'VIEWER' && currentUser?.role === 'MODERATOR') {
// let this withTracker re-execute when a subscription is stopped
subscriptionReactivity.depend();
- // Prevent data being removed by subscription stop
localBreakoutsSync.setIgnoreDeletes(true);
localGuestUsersSync.setIgnoreDeletes(true);
localMeetingsSync.setIgnoreDeletes(true);
localUsersSync.setIgnoreDeletes(true);
+ // Prevent data being removed by subscription stop
// stop role dependent subscriptions
[
SubscriptionRegistry.getSubscription('meetings'),
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js
index 20882610e8..bf313653b4 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/service.js
+++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js
@@ -1,8 +1,8 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import VoiceUsers from '/imports/api/voice-users';
import GroupChat from '/imports/api/group-chat';
-import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Breakouts from '/imports/api/breakouts';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import Storage from '/imports/ui/services/storage/session';
import { EMOJI_STATUSES } from '/imports/utils/statuses';
@@ -595,8 +595,6 @@ const isUserPresenter = (userId) => {
return user ? user.presenter : false;
};
-const amIPresenter = () => isUserPresenter(Auth.userID);
-
export const getUserNamesLink = (docTitle, fnSortedLabel, lnSortedLabel) => {
const mimeType = 'text/plain';
const userNamesObj = getUsers()
@@ -666,7 +664,6 @@ export default {
requestUserInformation,
focusFirstDropDownItem,
isUserPresenter,
- amIPresenter,
getUsersProp,
getUserCount,
sortUsersByCurrent,
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/container.jsx
index 057adcee80..394bc20634 100644
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/container.jsx
@@ -4,7 +4,7 @@ import { Session } from 'meteor/session';
import Auth from '/imports/ui/services/auth';
import Storage from '/imports/ui/services/storage/session';
import UserContent from './component';
-import GuestUsers from '/imports/ui/local-collections/guest-users-collection/guest-users';
+import GuestUsers from '/imports/api/guest-users';
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
import WaitingUsersService from '/imports/ui/components/waiting-users/service';
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
index 6903609851..77bbc191f2 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-list-item/container.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import BreakoutService from '/imports/ui/components/breakout-room/service';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import UserListItem from './component';
import UserListService from '/imports/ui/components/user-list/service';
diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
index 26ca5d0dea..26df63fea7 100755
--- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-participants/user-options/container.jsx
@@ -1,7 +1,7 @@
import { withTracker } from 'meteor/react-meteor-data';
import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import ActionsBarService from '/imports/ui/components/actions-bar/service';
import LearningDashboardService from '/imports/ui/components/learning-dashboard/service';
import UserListService from '/imports/ui/components/user-list/service';
diff --git a/bigbluebutton-html5/imports/ui/components/video-preview/container.jsx b/bigbluebutton-html5/imports/ui/components/video-preview/container.jsx
index 820ddbd0ba..f248859d5d 100755
--- a/bigbluebutton-html5/imports/ui/components/video-preview/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-preview/container.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import { withModalMounter } from '/imports/ui/components/modal/service';
import { withTracker } from 'meteor/react-meteor-data';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Users from '/imports/api/users';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import Service from './service';
import VideoPreview from './component';
diff --git a/bigbluebutton-html5/imports/ui/components/video-preview/styles.js b/bigbluebutton-html5/imports/ui/components/video-preview/styles.js
index 2ee187e6ab..3b1282adf3 100644
--- a/bigbluebutton-html5/imports/ui/components/video-preview/styles.js
+++ b/bigbluebutton-html5/imports/ui/components/video-preview/styles.js
@@ -1,4 +1,4 @@
-import styled, { css, keyframes } from 'styled-components'
+import styled, { css, keyframes } from 'styled-components';
import {
borderSize,
borderSizeLarge,
@@ -7,7 +7,7 @@ import {
colorGrayLabel,
colorWhite,
colorGrayLighter,
- colorBackground,
+ colorGrayDark,
colorPrimary,
colorText,
} from '/imports/ui/stylesheets/styled-components/palette';
@@ -117,7 +117,7 @@ const BrowserWarning = styled.p`
const Title = styled.div`
display: block;
- color: ${colorBackground};
+ color: ${colorGrayDark};
font-size: 1.4rem;
text-align: center;
`;
@@ -175,7 +175,7 @@ const ellipsis = keyframes`
to {
width: 1.5em;
}
-`
+`;
const FetchingAnimation = styled.span`
margin: auto;
@@ -226,4 +226,4 @@ export default {
VideoPreviewModal,
FetchingAnimation,
VideoPreview,
-};
\ No newline at end of file
+};
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/many-users-notify/container.jsx b/bigbluebutton-html5/imports/ui/components/video-provider/many-users-notify/container.jsx
index 1320a74dcb..05f3de23bc 100644
--- a/bigbluebutton-html5/imports/ui/components/video-provider/many-users-notify/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/many-users-notify/container.jsx
@@ -1,5 +1,5 @@
import { withTracker } from 'meteor/react-meteor-data';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import Users from '/imports/api/users/';
import VideoStreams from '/imports/api/video-streams';
diff --git a/bigbluebutton-html5/imports/ui/components/video-provider/service.js b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
index e380b05ce1..46677d33e3 100755
--- a/bigbluebutton-html5/imports/ui/components/video-provider/service.js
+++ b/bigbluebutton-html5/imports/ui/components/video-provider/service.js
@@ -2,8 +2,8 @@ import { Tracker } from 'meteor/tracker';
import { Session } from 'meteor/session';
import Settings from '/imports/ui/services/settings';
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Meetings from '/imports/api/meetings';
+import Users from '/imports/api/users';
import VideoStreams from '/imports/api/video-streams';
import UserListService from '/imports/ui/components/user-list/service';
import { makeCall } from '/imports/ui/services/api';
diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/alert/container.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/alert/container.jsx
index 7a768386ee..83cb59dafb 100644
--- a/bigbluebutton-html5/imports/ui/components/waiting-users/alert/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/waiting-users/alert/container.jsx
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Auth from '/imports/ui/services/auth';
-import GuestUsers from '/imports/ui/local-collections/guest-users-collection/guest-users';
+import GuestUsers from '/imports/api/guest-users';
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
import WaitingComponent from './component';
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx b/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx
index 4b5eff4610..8d8bef9efd 100644
--- a/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/waiting-users/container.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import Auth from '/imports/ui/services/auth';
-import GuestUsers from '/imports/ui/local-collections/guest-users-collection/guest-users';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import GuestUsers from '/imports/api/guest-users';
+import Meetings from '/imports/api/meetings';
import Service from './service';
import WaitingComponent from './component';
import { layoutDispatch } from '../layout/context';
diff --git a/bigbluebutton-html5/imports/ui/components/waiting-users/service.js b/bigbluebutton-html5/imports/ui/components/waiting-users/service.js
index 1c90d027dd..2afd33db27 100644
--- a/bigbluebutton-html5/imports/ui/components/waiting-users/service.js
+++ b/bigbluebutton-html5/imports/ui/components/waiting-users/service.js
@@ -1,4 +1,4 @@
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import { makeCall } from '/imports/ui/services/api';
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/container.jsx
index 22b03c0c0d..259fd9f2f7 100755
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotation-factory/reactive-annotation/container.jsx
@@ -5,7 +5,7 @@ import MediaService, { getSwapLayout, shouldEnableSwapLayout } from '/imports/ui
import ReactiveAnnotationService from './service';
import ReactiveAnnotation from './component';
import Auth from '/imports/ui/services/auth';
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import getFromUserSettings from '/imports/ui/services/users-settings';
const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx
index 11ab1c6385..052002816a 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/ellipse/component.jsx
@@ -12,8 +12,6 @@ export default class EllipseDrawComponent extends Component {
const { slideWidth, slideHeight, annotation } = this.props;
const { points } = annotation;
- // x1 and y1 - coordinates of the ellipse's top left corner
- // x2 and y2 - coordinates of the ellipse's bottom right corner
const x1 = points[0];
const y1 = points[1];
const x2 = points[2];
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx
index 7c87acc2b2..005f3cf5ca 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/component.jsx
@@ -149,18 +149,7 @@ export default class TextDrawComponent extends Component {
return (
-
-
-
-
-
(
-
-);
+const TextDrawContainer = (props) => {
+ const { isMultiUser, activeTextShapeId, annotation } = props;
+
+ const usingUsersContext = useContext(UsersContext);
+ const { users } = usingUsersContext;
+ const currentUser = users[Auth.meetingID][Auth.userID];
+ const userIsPresenter = currentUser.presenter;
+
+ let isActive = false;
+
+ if ((userIsPresenter || isMultiUser) && activeTextShapeId === annotation.id) {
+ isActive = true;
+ }
+
+ return ;
+};
export default withTracker((params) => {
const { whiteboardId } = params;
- const isPresenter = TextShapeService.isPresenter();
const isMultiUser = WhiteboardService.isMultiUserActive(whiteboardId);
const activeTextShapeId = TextShapeService.activeTextShapeId();
- let isActive = false;
- if ((isPresenter || isMultiUser) && activeTextShapeId === params.annotation.id) {
- isActive = true;
- }
return {
- isActive,
setTextShapeValue: TextShapeService.setTextShapeValue,
resetTextShapeActiveId: TextShapeService.resetTextShapeActiveId,
+ isMultiUser,
+ activeTextShapeId,
};
})(TextDrawContainer);
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/service.js
index 43cdbfd450..2e23bd22cb 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/service.js
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/text/service.js
@@ -1,6 +1,4 @@
import Storage from '/imports/ui/services/storage/session';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Auth from '/imports/ui/services/auth';
const DRAW_SETTINGS = 'drawSettings';
@@ -20,11 +18,6 @@ const resetTextShapeActiveId = () => {
}
};
-const isPresenter = () => {
- const currentUser = Users.findOne({ userId: Auth.userID }, { fields: { presenter: 1 } });
- return currentUser ? currentUser.presenter : false;
-};
-
const activeTextShapeId = () => {
const drawSettings = Storage.getItem(DRAW_SETTINGS);
return drawSettings ? drawSettings.textShape.textShapeActiveId : '';
@@ -33,6 +26,5 @@ const activeTextShapeId = () => {
export default {
setTextShapeValue,
activeTextShapeId,
- isPresenter,
resetTextShapeActiveId,
};
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx
index 1bf421da11..92b8130245 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/annotations/triangle/component.jsx
@@ -12,21 +12,15 @@ export default class TriangleDrawComponent extends Component {
const { slideWidth, slideHeight, annotation } = this.props;
const { points } = annotation;
- // points[0] and points[1] are x and y coordinates of the top left corner of the annotation
- // points[2] and points[3] are x and y coordinates of the bottom right corner of the annotation
- const xBottomLeft = points[0];
- const yBottomLeft = points[3];
- const xBottomRight = points[2];
- const yBottomRight = points[3];
- const xTop = ((xBottomRight - xBottomLeft) / 2) + xBottomLeft;
- const yTop = points[1];
+ const xApex = ((points[2] - points[0]) / 2) + points[0];
+ const yApex = points[1];
- const path = `M${denormalizeCoord(xTop, slideWidth)
- },${denormalizeCoord(yTop, slideHeight)
- },${denormalizeCoord(xBottomLeft, slideWidth)
- },${denormalizeCoord(yBottomLeft, slideHeight)
- },${denormalizeCoord(xBottomRight, slideWidth)
- },${denormalizeCoord(yBottomRight, slideHeight)
+ const path = `M${denormalizeCoord(xApex, slideWidth)
+ },${denormalizeCoord(yApex, slideHeight)
+ },${denormalizeCoord(points[0], slideWidth)
+ },${denormalizeCoord(points[3], slideHeight)
+ },${denormalizeCoord(points[2], slideWidth)
+ },${denormalizeCoord(points[3], slideHeight)
}Z`;
return path;
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/service.js
index 61d2d9663a..1c214b7b11 100755
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/service.js
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/service.js
@@ -1,4 +1,4 @@
-import Users from '/imports/ui/local-collections/users-collection/users';
+import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import WhiteboardMultiUser from '/imports/api/whiteboard-multi-user';
import addAnnotationQuery from '/imports/api/annotations/addAnnotation';
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/container.jsx
index efb0e98f98..10e8f3c3f7 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/container.jsx
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/container.jsx
@@ -9,7 +9,7 @@ const WhiteboardToolbarContainer = props => (
);
export default withTracker((params) => {
- const { whiteboardId } = params;
+ const { whiteboardId, isPresenter } = params;
const data = {
actions: {
@@ -31,8 +31,8 @@ export default withTracker((params) => {
},
textShapeActiveId: WhiteboardToolbarService.getTextShapeActiveId(),
multiUser: WhiteboardService.isMultiUserActive(whiteboardId),
- isPresenter: WhiteboardToolbarService.isPresenter(),
- annotations: WhiteboardToolbarService.filterAnnotationList(),
+ isPresenter,
+ annotations: WhiteboardToolbarService.filterAnnotationList(isPresenter),
isMeteorConnected: Meteor.status().connected,
multiUserSize: WhiteboardService.getMultiUserSize(whiteboardId),
};
diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/service.js b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/service.js
index a99d3f2a86..61e0c9e10d 100644
--- a/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/service.js
+++ b/bigbluebutton-html5/imports/ui/components/whiteboard/whiteboard-toolbar/service.js
@@ -1,7 +1,5 @@
import { makeCall } from '/imports/ui/services/api';
import Storage from '/imports/ui/services/storage/session';
-import Users from '/imports/ui/local-collections/users-collection/users';
-import Auth from '/imports/ui/services/auth';
import getFromUserSettings from '/imports/ui/services/users-settings';
const DRAW_SETTINGS = 'drawSettings';
@@ -72,14 +70,8 @@ const getTextShapeActiveId = () => {
return drawSettings ? drawSettings.textShape.textShapeActiveId : '';
};
-const isPresenter = () => {
- const currentUser = Users.findOne({ userId: Auth.userID }, { fields: { presenter: 1 } });
- return currentUser ? currentUser.presenter : false;
-};
-
-const filterAnnotationList = () => {
+const filterAnnotationList = (amIPresenter) => {
const multiUserPenOnly = getFromUserSettings('bbb_multi_user_pen_only', WHITEBOARD_TOOLBAR.multiUserPenOnly);
- const amIPresenter = isPresenter();
let filteredAnnotationList = WHITEBOARD_TOOLBAR.tools;
@@ -120,6 +112,5 @@ export default {
setColor,
setTextShapeObject,
getTextShapeActiveId,
- isPresenter,
filterAnnotationList,
};
diff --git a/bigbluebutton-html5/imports/ui/local-collections/breakouts-collection/breakouts.js b/bigbluebutton-html5/imports/ui/local-collections/breakouts-collection/breakouts.js
deleted file mode 100644
index f848b59ff3..0000000000
--- a/bigbluebutton-html5/imports/ui/local-collections/breakouts-collection/breakouts.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import Breakouts from '/imports/api/breakouts';
-import AbstractCollection from '/imports/ui/local-collections/abstract-collection/abstract-collection';
-
-const localBreakouts = new Mongo.Collection('local-breakouts', { connection: null });
-
-export const localBreakoutsSync = new AbstractCollection(Breakouts, localBreakouts);
-
-export default localBreakouts;
diff --git a/bigbluebutton-html5/imports/ui/local-collections/guest-users-collection/guest-users.js b/bigbluebutton-html5/imports/ui/local-collections/guest-users-collection/guest-users.js
deleted file mode 100644
index 48b5a34649..0000000000
--- a/bigbluebutton-html5/imports/ui/local-collections/guest-users-collection/guest-users.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import guestUsers from '/imports/api/guest-users';
-import AbstractCollection from '/imports/ui/local-collections/abstract-collection/abstract-collection';
-
-const localGuestUsers = new Mongo.Collection('local-guest-users', { connection: null });
-
-export const localGuestUsersSync = new AbstractCollection(guestUsers, localGuestUsers);
-
-export default localGuestUsers;
diff --git a/bigbluebutton-html5/imports/ui/local-collections/meetings-collection/meetings.js b/bigbluebutton-html5/imports/ui/local-collections/meetings-collection/meetings.js
deleted file mode 100644
index c27039d6cf..0000000000
--- a/bigbluebutton-html5/imports/ui/local-collections/meetings-collection/meetings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import Meetings from '/imports/api/meetings';
-import AbstractCollection from '/imports/ui/local-collections/abstract-collection/abstract-collection';
-
-const localMeetings = new Mongo.Collection('local-meetings', { connection: null });
-
-export const localMeetingsSync = new AbstractCollection(Meetings, localMeetings);
-
-export default localMeetings;
diff --git a/bigbluebutton-html5/imports/ui/local-collections/users-collection/users.js b/bigbluebutton-html5/imports/ui/local-collections/users-collection/users.js
deleted file mode 100644
index a735aad0e8..0000000000
--- a/bigbluebutton-html5/imports/ui/local-collections/users-collection/users.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import Users from '/imports/api/users';
-import AbstractCollection from '/imports/ui/local-collections/abstract-collection/abstract-collection';
-
-const localUsers = new Mongo.Collection('local-users', { connection: null });
-
-export const localUsersSync = new AbstractCollection(Users, localUsers);
-
-export default localUsers;
diff --git a/bigbluebutton-html5/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker.js b/bigbluebutton-html5/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker.js
new file mode 100644
index 0000000000..b5e6b2b83b
--- /dev/null
+++ b/bigbluebutton-html5/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker.js
@@ -0,0 +1,92 @@
+/*
+This class allows other parts of the code to get called when an event (insert/update/delete) occurs in a server-side published cursor.
+
+In implementation time it was created for the publisher ( meteor live data hooks ) notify the listener ( LocalCollectionSynchronizer ) about the events.
+
+*/
+class CollectionEventsBroker {
+ static getKey(msg, updates) {
+ // the msg.msg has the collection event,
+ // see the collection hooks event object for more information
+ return `/${msg.collection}/${msg.msg}/`;
+ // TODO: also process the updates object
+ }
+
+ constructor() {
+ this.callbacks = {};
+ }
+
+ addListener(collection, event, callback) {
+ try {
+ const index = CollectionEventsBroker.getKey({ collection, msg: event });
+ const TheresCallback = this.callbacks[index];
+ if (TheresCallback) {
+ throw new Error('There is already an associated callback for this event');
+ }
+ this.callbacks[index] = callback;
+ return true;
+ } catch (err) {
+ console.error(err);
+ return false;
+ }
+ }
+
+ dispatchEvent(msg, updates) {
+ try {
+ const msgIndex = CollectionEventsBroker.getKey(msg, updates);
+ const { fields } = msg;
+ const callback = this.callbacks[msgIndex];
+ if (callback) {
+ callback({ ...fields, referenceId: msg.id });
+ }
+ } catch (error) {
+ console.error('Error:', error);
+ }
+ // TODO: also process the updates object
+ }
+}
+
+const collectionEventsBroker = new CollectionEventsBroker();
+
+export const liveDataEventBrokerInitializer = () => {
+ Meteor.connection._processOneDataMessage = function (msg, updates) {
+ try {
+ const messageType = msg.msg;
+ let col = null;
+ if (msg.collection && msg.collection.indexOf('stream-cursor') === -1) {
+ col = Meteor.connection._stores[msg.collection]?._getCollection();
+ }
+ collectionEventsBroker.dispatchEvent(msg, updates);
+ // msg is one of ['added', 'changed', 'removed', 'ready', 'updated']
+ if (messageType === 'added') {
+ if (!col || !col.onAdded || col.onAdded(msg, updates)) {
+ this._process_added(msg, updates);
+ }
+ } else if (messageType === 'changed') {
+ if (!col || !col.onChanged || col.onChanged(msg, updates)) {
+ this._process_changed(msg, updates);
+ }
+ } else if (messageType === 'removed') {
+ if (!col || !col.onRemoved || col.onRemoved(msg, updates)) {
+ this._process_removed(msg, updates);
+ }
+ } else if (messageType === 'ready') {
+ if (!col || !col.onReady || col.onReady(msg, updates)) {
+ this._process_ready(msg, updates);
+ }
+ } else if (messageType === 'updated') {
+ if (!col || !col.onUpdated || col.onUpdated(msg, updates)) {
+ this._process_updated(msg, updates);
+ }
+ } else if (messageType === 'nosub') {
+ // ignore this
+ } else {
+ Meteor._debug('discarding unknown livedata data message type', msg);
+ }
+ } catch (err) {
+ console.error('Error when calling hooks', err);
+ }
+ };
+};
+
+export default collectionEventsBroker;
diff --git a/bigbluebutton-html5/imports/ui/local-collections/abstract-collection/abstract-collection.js b/bigbluebutton-html5/imports/ui/services/LocalCollectionSynchronizer/LocalCollectionSynchronizer.js
similarity index 72%
rename from bigbluebutton-html5/imports/ui/local-collections/abstract-collection/abstract-collection.js
rename to bigbluebutton-html5/imports/ui/services/LocalCollectionSynchronizer/LocalCollectionSynchronizer.js
index 940743f570..94763d9680 100644
--- a/bigbluebutton-html5/imports/ui/local-collections/abstract-collection/abstract-collection.js
+++ b/bigbluebutton-html5/imports/ui/services/LocalCollectionSynchronizer/LocalCollectionSynchronizer.js
@@ -1,7 +1,13 @@
import SubscriptionRegistry from '/imports/ui/services/subscription-registry/subscriptionRegistry';
-import CollectionEventsBroker from '/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks';
+import CollectionEventsBroker from '/imports/ui/services/LiveDataEventBroker/LiveDataEventBroker';
-class AbstractCollection {
+/*
+This class connects a local collection with the LiveDataEventBroker, propagating the changes of a server-side published cursor to a local collection.
+
+It also guarantee that in case of a reconnection or a re-subscription, the data is only removed after subscription is ready, avoiding the situation of missing data during re-synchronization.
+*/
+
+class LocalCollectionSynchronizer {
constructor(serverCollection, localCollection, options = {}) {
this.serverCollection = serverCollection;
this.localCollection = localCollection;
@@ -9,21 +15,24 @@ class AbstractCollection {
this.lastSubscriptionId = '';
this.options = options;
this.ignoreDeletes = false;
- this.createObserver();
}
+ /*
+ This method allow to enable/disable the ignoreDeletes feature.
+ When enabled, system will skip the received deletes ( not apply on local collection ).
+ Don't panic: these deletes will be processed when the subscription gets ready - see removeOldSubscriptionData method.
+ */
setIgnoreDeletes(value) {
this.ignoreDeletes = value;
}
// Replicates remote collection to local collection ( avoiding cleanup during the forced resync )
- createObserver() {
+ setupListeners() {
const self = this;
const addedCallback = function (item) {
const subscription = SubscriptionRegistry
.getSubscription(self.serverCollection._name);
-
if (item.id === 'publication-stop-marker' && item.stopped) {
self.ignoreDeletes = true;
return;
@@ -34,8 +43,9 @@ class AbstractCollection {
const wasEmpty = self.lastSubscriptionId === '';
self.lastSubscriptionId = subscription.subscriptionId;
if (!wasEmpty) {
- self.PollForReadyStatus(() => {
+ self.callWhenSubscriptionReady(() => {
self.ignoreDeletes = false;
+ Session.set('globalIgnoreDeletes', false);
self.removeOldSubscriptionData();
});
}
@@ -73,7 +83,8 @@ class AbstractCollection {
};
const removedCallback = function (item) {
- if (self.ignoreDeletes) {
+ const globalIgnoreDeletes = Session.get('globalIgnoreDeletes');
+ if (self.ignoreDeletes || globalIgnoreDeletes) {
return;
}
const selector = { referenceId: item.referenceId };
@@ -86,7 +97,10 @@ class AbstractCollection {
CollectionEventsBroker.addListener(this.serverCollection._name, 'removed', removedCallback);
}
- PollForReadyStatus(func) {
+ /*
+ This method calls the function received as parameter when the subscription gets ready.
+*/
+ callWhenSubscriptionReady(func) {
const temp = (res) => {
setTimeout(() => {
const subscription = SubscriptionRegistry.getSubscription(this.serverCollection._name);
@@ -103,6 +117,9 @@ class AbstractCollection {
return tempPromise;
}
+ /*
+ This method removes data from previous subscriptions after the current one is ready.
+ */
removeOldSubscriptionData() {
const subscription = SubscriptionRegistry.getSubscription(this.serverCollection._name);
@@ -122,4 +139,4 @@ class AbstractCollection {
}
}
-export default AbstractCollection;
+export default LocalCollectionSynchronizer;
diff --git a/bigbluebutton-html5/imports/ui/services/auth/index.js b/bigbluebutton-html5/imports/ui/services/auth/index.js
index 5aee3a3688..ee8a5fa549 100755
--- a/bigbluebutton-html5/imports/ui/services/auth/index.js
+++ b/bigbluebutton-html5/imports/ui/services/auth/index.js
@@ -244,6 +244,7 @@ class Auth {
initAnnotationsStreamListener();
clearTimeout(validationTimeout);
this.connectionID = authenticationTokenValidation.connectionId;
+ Session.set('userWillAuth', false);
setTimeout(() => resolve(true), 100);
break;
default:
diff --git a/bigbluebutton-html5/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks.js b/bigbluebutton-html5/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks.js
deleted file mode 100644
index ff24247c8e..0000000000
--- a/bigbluebutton-html5/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks.js
+++ /dev/null
@@ -1,43 +0,0 @@
-class CollectionEventsBroker {
- static getKey(msg, updates) {
- // the msg.msg has the collection event,
- // see the collection hooks event object for more information
- return `/${msg.collection}/${msg.msg}/`;
- // TODO: also process the updates object
- }
-
- constructor() {
- this.callbacks = {};
- }
-
- addListener(collection, event, callback) {
- try {
- const index = CollectionEventsBroker.getKey({ collection, msg: event });
- const TheresCallback = this.callbacks[index];
- if (TheresCallback) {
- throw new Error('There is already an associated callback for this event');
- }
- this.callbacks[index] = callback;
- return true;
- } catch (err) {
- console.error(err);
- return false;
- }
- }
-
- dispatchEvent(msg, updates) {
- try {
- const msgIndex = CollectionEventsBroker.getKey(msg, updates);
- const { fields } = msg;
- const callback = this.callbacks[msgIndex];
- if (callback) {
- callback({ ...fields, referenceId: msg.id });
- }
- } catch (error) {
- console.error('Error:', error);
- }
- // TODO: also process the updates object
- }
-}
-
-export default new CollectionEventsBroker();
diff --git a/bigbluebutton-html5/imports/ui/services/collection-hooks/collection-hooks.js b/bigbluebutton-html5/imports/ui/services/collection-hooks/collection-hooks.js
deleted file mode 100644
index f0681510bc..0000000000
--- a/bigbluebutton-html5/imports/ui/services/collection-hooks/collection-hooks.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import CollectionEventsBroker from '/imports/ui/services/collection-hooks-callbacks/collection-hooks-callbacks';
-
-Meteor.connection._processOneDataMessage = function (msg, updates) {
- try {
- const messageType = msg.msg;
-
- let col = null;
- if (msg.collection && msg.collection.indexOf('stream-cursor') === -1) {
- col = Meteor.connection._stores[msg.collection]?._getCollection();
- }
- CollectionEventsBroker.dispatchEvent(msg, updates);
- // msg is one of ['added', 'changed', 'removed', 'ready', 'updated']
- if (messageType === 'added') {
- if (!col || !col.onAdded || col.onAdded(msg, updates)) {
- this._process_added(msg, updates);
- }
- } else if (messageType === 'changed') {
- if (!col || !col.onChanged || col.onChanged(msg, updates)) {
- this._process_changed(msg, updates);
- }
- } else if (messageType === 'removed') {
- if (!col || !col.onRemoved || col.onRemoved(msg, updates)) {
- this._process_removed(msg, updates);
- }
- } else if (messageType === 'ready') {
- if (!col || !col.onReady || col.onReady(msg, updates)) {
- this._process_ready(msg, updates);
- }
- } else if (messageType === 'updated') {
- if (!col || !col.onUpdated || col.onUpdated(msg, updates)) {
- this._process_updated(msg, updates);
- }
- } else if (messageType === 'nosub') {
- // ignore this
- } else {
- Meteor._debug('discarding unknown livedata data message type', msg);
- }
- } catch (err) {
- console.error('Error when calling hooks', err);
- }
-};
diff --git a/bigbluebutton-html5/imports/ui/services/meeting-settings/index.js b/bigbluebutton-html5/imports/ui/services/meeting-settings/index.js
index 8af6646c29..0830209b9d 100644
--- a/bigbluebutton-html5/imports/ui/services/meeting-settings/index.js
+++ b/bigbluebutton-html5/imports/ui/services/meeting-settings/index.js
@@ -1,5 +1,5 @@
import Auth from '/imports/ui/services/auth';
-import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
+import Meetings from '/imports/api/meetings';
export default function getFromMeetingSettings(setting, defaultValue) {
const prop = Meetings.findOne(
diff --git a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/breakpoints.js b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/breakpoints.js
index 50b2e65883..82aa979d42 100644
--- a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/breakpoints.js
+++ b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/breakpoints.js
@@ -1,10 +1,12 @@
const smallOnly = 'only screen and (max-width: 40em)';
const mediumOnly = 'only screen and (min-width: 40.063em) and (max-width: 64em)';
const mediumUp = 'only screen and (min-width: 40.063em)';
+const mediumDown = 'only screen and (max-width: 40.0629em)';
const landscape = "only screen and (orientation: landscape)";
const phoneLandscape = 'only screen and (max-width: 480px) and (orientation: landscape)';
const largeUp = 'only screen and (min-width: 64.063em)';
const hasPhoneDimentions = 'only screen and (max-height: 479px), only screen and (max-width: 479px)';
+const hasPhoneWidth = 'only screen and (max-width: 479px)';
export {
smallOnly,
@@ -14,4 +16,6 @@ export {
phoneLandscape,
largeUp,
hasPhoneDimentions,
+ mediumDown,
+ hasPhoneWidth,
};
diff --git a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js
index 6d6d82c5f7..cf0cf25811 100644
--- a/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js
+++ b/bigbluebutton-html5/imports/ui/stylesheets/styled-components/palette.js
@@ -1,110 +1,110 @@
-const colorWhite = '#FFF';
-const colorOffWhite = '#F3F6F9';
+const colorWhite = 'var(--color-white, #FFF)';
+const colorOffWhite = 'var(--color-off-white, #F3F6F9)';
-const colorBlack = '#000000';
+const colorBlack = 'var(--color-black, #000000)';
-const colorGray = '#4E5A66';
-const colorGrayDark = '#06172A';
-const colorGrayLight = '#8B9AA8';
-const colorGrayLighter = '#A7B3BD';
-const colorGrayLightest = '#D4D9DF';
+const colorGray = 'var(--color-gray, #4E5A66)';
+const colorGrayDark = 'var(--color-gray-dark, #06172A)';
+const colorGrayLight = 'var(--color-gray-light, #8B9AA8)';
+const colorGrayLighter = 'var(--color-gray-lighter, #A7B3BD)';
+const colorGrayLightest = 'var(--color-gray-lightest, #D4D9DF)';
-const colorBlueLight = '#54a1f3';
-const colorBlueLighter = '#92BCEA';
-const colorBlueLightest = '#E4ECF2';
+const colorBlueLight = 'var(--color-blue-light, #54a1f3)';
+const colorBlueLighter = 'var(--color-blue-lighter, #92BCEA)';
+const colorBlueLightest = 'var(--color-blue-lightest, #E4ECF2)';
-const colorTransparent = '#ff000000';
+const colorTransparent = 'var(--color-transparent, #ff000000)';
-const colorPrimary = '#0F70D7';
-const colorDanger = '#DF2721';
-const colorSuccess = '#008081';
-const colorWarning = 'purple';
-const colorOffline = colorGrayLight;
-const colorMuted = '#586571';
-const colorMutedBackground = '#F3F6F9';
+const colorPrimary = 'var(--color-primary, #0F70D7)';
+const colorDanger = 'var(--color-danger, #DF2721)';
+const colorSuccess = 'var(--color-success, #008081)';
+const colorWarning = 'var(--color-warning, purple)';
+const colorOffline = `var(--color-offline, ${colorGrayLight})`;
+const colorMuted = 'var(--color-muted, #586571)';
+const colorMutedBackground = 'var(--color-muted-background, #F3F6F9)';
-const colorBackground = colorGrayDark;
-const userListBg = colorOffWhite;
-const userListText = colorGray;
-const unreadMessagesBg = colorDanger;
-const colorGrayLabel = colorGray;
-const colorText = colorGray;
-const colorLink = colorPrimary;
+const colorBackground = `var(--color-background, ${colorGrayDark})`;
+const userListBg = `var(--user-list-bg, ${colorOffWhite})`;
+const userListText = `var(--user-list-text, ${colorGray})`;
+const unreadMessagesBg = `var(--unread-messages-bg, ${colorDanger})`;
+const colorGrayLabel = `var(--color-gray-label, ${colorGray})`;
+const colorText = `var(--color-text, ${colorGray})`;
+const colorLink = `var(--color-link, ${colorPrimary})`;
-const listItemBgHover = '#DCE4ED';
-const colorTipBg = '#333333';
-const itemFocusBorder = colorBlueLighter;
+const listItemBgHover = 'var(--list-item-bg-hover, #DCE4ED)';
+const colorTipBg = 'var(--color-tip-bg, #333333)';
+const itemFocusBorder = `var(--item-focus-border, ${colorBlueLighter})`;
-const btnDefaultColor = colorGray;
-const btnDefaultBg = colorWhite;
-const btnDefaultBorder = colorWhite;
+const btnDefaultColor = `var(--btn-default-color, ${colorGray})`;
+const btnDefaultBg = `var(--btn-default-bg, ${colorWhite})`;
+const btnDefaultBorder = `var(--btn-default-border, ${colorWhite})`;
-const btnPrimaryBorder = colorPrimary;
-const btnPrimaryColor = colorWhite;
-const btnPrimaryBg = colorPrimary;
+const btnPrimaryBorder = `var(--btn-primary-border, ${colorPrimary})`;
+const btnPrimaryColor = `var(--btn-primary-color, ${colorWhite})`;
+const btnPrimaryBg = `var(--btn-primary-bg, ${colorPrimary})`;
-const btnSuccessBorder = colorSuccess;
-const btnSuccessColor = colorWhite;
-const btnSuccessBg = colorSuccess;
+const btnSuccessBorder = `var(--btn-success-border, ${colorSuccess})`;
+const btnSuccessColor = `var(--btn-success-color, ${colorWhite})`;
+const btnSuccessBg = `var(--btn-success-bg, ${colorSuccess})`;
-const btnWarningBorder = colorWarning;
-const btnWarningColor = colorWhite;
-const btnWarningBg = colorWarning;
+const btnWarningBorder = `var(--btn-warning-border, ${colorWarning})`;
+const btnWarningColor = `var(--btn-warning-color, ${colorWhite})`;
+const btnWarningBg = `var(--btn-warning-bg, ${colorWarning})`;
-const btnDangerBorder = colorDanger;
-const btnDangerColor = colorWhite;
-const btnDangerBg = colorDanger;
+const btnDangerBorder = `var(--btn-danger-border, ${colorDanger})`;
+const btnDangerColor = `var(--btn-danger-color, ${colorWhite})`;
+const btnDangerBg = `var(--btn-danger-bg, ${colorDanger})`;
-const btnDarkBorder = colorDanger;
-const btnDarkColor = colorWhite;
-const btnDarkBg = colorGrayDark;
+const btnDarkBorder = `var(--btn-dark-border, ${colorDanger})`;
+const btnDarkColor = `var(--btn-dark-color, ${colorWhite})`;
+const btnDarkBg = `var(--btn-dark-bg, ${colorGrayDark})`;
-const btnOfflineBorder = colorOffline;
-const btnOfflineColor = colorWhite;
-const btnOfflineBg = colorOffline;
+const btnOfflineBorder = `var(--btn-offline-border, ${colorOffline})`;
+const btnOfflineColor = `var(--btn-offline-color, ${colorWhite})`;
+const btnOfflineBg = `var(--btn-offline-bg, ${colorOffline})`;
-const btnMutedBorder = colorMutedBackground;
-const btnMutedColor = colorMuted;
-const btnMutedBg = colorMutedBackground;
+const btnMutedBorder = `var(--btn-muted-border, ${colorMutedBackground})`;
+const btnMutedColor = `var(--btn-muted-color, ${colorMuted})`;
+const btnMutedBg = `var(--btn-muted-bg, ${colorMutedBackground})`;
-const toolbarButtonColor = btnDefaultColor;
-const userThumbnailBorder = colorGrayLight;
-const loaderBg = colorGrayDark;
-const loaderBullet = colorWhite;
+const toolbarButtonColor = `var(--toolbar-button-color, ${btnDefaultColor})`;
+const userThumbnailBorder = `var(--user-thumbnail-border, ${colorGrayLight})`;
+const loaderBg = `var(--loader-bg, ${colorGrayDark})`;
+const loaderBullet = `var(--loader-bullet, ${colorWhite})`;
-const systemMessageBackgroundColor = '#F9FBFC';
-const systemMessageBorderColor = '#C5CDD4';
-const systemMessageFontColor = colorGrayDark;
-const colorHeading = colorGrayDark;
-const palettePlaceholderText = '#787675';
-const pollAnnotationGray = '#333333';
+const systemMessageBackgroundColor = 'var(--system-message-background-color, #F9FBFC)';
+const systemMessageBorderColor = 'var(--system-message-border-color, #C5CDD4)';
+const systemMessageFontColor = `var(--system-message-font-color, ${colorGrayDark})`;
+const colorHeading = `var(--color-heading, ${colorGrayDark})`;
+const palettePlaceholderText = 'var(--palette-placeholder-text, #787675)';
+const pollAnnotationGray = 'var(--poll-annotation-gray, #333333)';
-const toolbarButtonBorderColor = colorGrayLighter;
-const toolbarListColor = colorGray;
-const toolbarButtonBg = btnDefaultBg;
-const toolbarListBg = '#DDD';
-const toolbarListBgFocus = '#C6C6C6';
-const colorContentBackground = '#1B2A3A';
+const toolbarButtonBorderColor = `var(--toolbar-button-border-color, ${colorGrayLighter})`;
+const toolbarListColor = `var(--toolbar-list-color, ${colorGray})`;
+const toolbarButtonBg = `var(--toolbar-button-bg, ${btnDefaultBg})`;
+const toolbarListBg = 'var(--toolbar-list-bg, #DDD)';
+const toolbarListBgFocus = 'var(--toolbar-list-bg-focus, #C6C6C6)';
+const colorContentBackground = 'var(--color-content-background, #1B2A3A)';
-const dropdownBg = colorWhite;
+const dropdownBg = `var(--dropdown-bg, ${colorWhite})`;
-const pollStatsBorderColor = '#D4D9DF';
-const pollBlue = '#1A73D4';
+const pollStatsBorderColor = 'var(--poll-stats-border-color, #D4D9DF)';
+const pollBlue = 'var(--poll-blue, #1A73D4)';
-const toastDefaultColor = colorWhite;
-const toastDefaultBg = colorGray;
+const toastDefaultColor = `var(--toast-default-color, ${colorWhite})`;
+const toastDefaultBg = `var(--toast-default-bg, ${colorGray})`;
-const toastInfoColor = colorWhite;
-const toastInfoBg = colorPrimary;
+const toastInfoColor = `var(--toast-info-color, ${colorWhite})`;
+const toastInfoBg = `var(--toast-info-bg, ${colorPrimary})`;
-const toastSuccessColor = colorWhite;
-const toastSuccessBg = colorSuccess;
+const toastSuccessColor = `var(--toast-success-color, ${colorWhite})`;
+const toastSuccessBg = `var(--toast-success-bg, ${colorSuccess})`;
-const toastErrorColor = colorWhite;
-const toastErrorBg = colorDanger;
+const toastErrorColor = `var(--toast-error-color, ${colorWhite})`;
+const toastErrorBg = `var(--toast-error-bg, ${colorDanger})`;
-const toastWarningColor = colorWhite;
-const toastWarningBg = colorWarning;
+const toastWarningColor = `var(--toast-warning-color, ${colorWhite})`;
+const toastWarningBg = `var(--toast-warning-bg, ${colorWarning})`;
export {
colorWhite,
diff --git a/bigbluebutton-html5/package-lock.json b/bigbluebutton-html5/package-lock.json
index cbe1916288..7dd9d75e56 100644
--- a/bigbluebutton-html5/package-lock.json
+++ b/bigbluebutton-html5/package-lock.json
@@ -3574,6 +3574,64 @@
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
},
+ "meow": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
+ "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
+ "requires": {
+ "@types/minimist": "^1.2.0",
+ "camelcase-keys": "^6.2.2",
+ "decamelize": "^1.2.0",
+ "decamelize-keys": "^1.1.0",
+ "hard-rejection": "^2.1.0",
+ "minimist-options": "4.1.0",
+ "normalize-package-data": "^3.0.0",
+ "read-pkg-up": "^7.0.1",
+ "redent": "^3.0.0",
+ "trim-newlines": "^3.0.0",
+ "type-fest": "^0.18.0",
+ "yargs-parser": "^20.2.3"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz",
+ "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "normalize-package-data": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz",
+ "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==",
+ "requires": {
+ "hosted-git-info": "^4.0.1",
+ "resolve": "^1.20.0",
+ "semver": "^7.3.4",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "trim-newlines": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.0.2.tgz",
+ "integrity": "sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew=="
+ },
+ "type-fest": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
+ "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="
+ }
+ }
+ },
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
diff --git a/bigbluebutton-html5/public/locales/ar.json b/bigbluebutton-html5/public/locales/ar.json
index 308a34af3a..5540f75517 100644
--- a/bigbluebutton-html5/public/locales/ar.json
+++ b/bigbluebutton-html5/public/locales/ar.json
@@ -676,16 +676,24 @@
"app.connection-status.description": "عرض حالة اتصال المستخدم",
"app.connection-status.empty": "لا توجد حاليا أي مشاكل في الاتصال قد تم التبليغ عنها",
"app.connection-status.more": "أكثر",
- "app.connection-status.copy": "نسخ بيانات الشبكة",
+ "app.connection-status.copy": "نسخ الإحصائيات",
"app.connection-status.copied": "تم النسخ!",
"app.connection-status.jitter": "تقطع",
"app.connection-status.label": "حالة الاتصال",
+ "app.connection-status.settings": "ضبط الإعدادات الخاصة بك",
"app.connection-status.no": "لا",
"app.connection-status.notification": "تم الكشف عن فقدان اتصالك",
"app.connection-status.offline": "غير متصل",
+ "app.connection-status.audioUploadRate": "معدل تحميل الصوت",
+ "app.connection-status.audioDownloadRate": "معدل تحميل الصوت",
+ "app.connection-status.videoUploadRate": "معدل تحميل الفيديو",
+ "app.connection-status.videoDownloadRate": "معدل تنزيل الفيديو",
"app.connection-status.lostPackets": "الحزم المفقودة",
"app.connection-status.usingTurn": "باستخدام TURN",
"app.connection-status.yes": "نعم",
+ "app.connection-status.connectionStats": "إحصائيات الاتصال",
+ "app.connection-status.myLogs": "سجلاتي",
+ "app.connection-status.sessionLogs": "سجلات الجلسة",
"app.learning-dashboard.label": "لوحة التعلم",
"app.learning-dashboard.description": "افتح لوحة التعلم مع أنشطة المستخدمين",
"app.learning-dashboard.clickHereToOpen": "افتح لوحة التعلم",
diff --git a/bigbluebutton-html5/public/locales/en.json b/bigbluebutton-html5/public/locales/en.json
index 6ece198628..2976a98326 100755
--- a/bigbluebutton-html5/public/locales/en.json
+++ b/bigbluebutton-html5/public/locales/en.json
@@ -679,16 +679,26 @@
"app.connection-status.description": "View users' connection status",
"app.connection-status.empty": "There are currently no reported connection issues",
"app.connection-status.more": "more",
- "app.connection-status.copy": "Copy network data",
+ "app.connection-status.copy": "Copy Stats",
"app.connection-status.copied": "Copied!",
"app.connection-status.jitter": "Jitter",
"app.connection-status.label": "Connection status",
+ "app.connection-status.settings": "Adjusting Your Settings",
"app.connection-status.no": "No",
"app.connection-status.notification": "Loss in your connection was detected",
"app.connection-status.offline": "offline",
+ "app.connection-status.audioUploadRate": "Audio Upload Rate",
+ "app.connection-status.audioDownloadRate": "Audio Download Rate",
+ "app.connection-status.videoUploadRate": "Video Upload Rate",
+ "app.connection-status.videoDownloadRate": "Video Download Rate",
"app.connection-status.lostPackets": "Lost packets",
"app.connection-status.usingTurn": "Using TURN",
"app.connection-status.yes": "Yes",
+ "app.connection-status.connectionStats": "Connection Stats",
+ "app.connection-status.myLogs": "My Logs",
+ "app.connection-status.sessionLogs": "Session Logs",
+ "app.connection-status.next": "Next page",
+ "app.connection-status.prev": "Previous page",
"app.learning-dashboard.label": "Learning Dashboard",
"app.learning-dashboard.description": "Open dashboard with users activities",
"app.learning-dashboard.clickHereToOpen": "Open Learning Dashboard",
@@ -916,15 +926,18 @@
"playback.player.thumbnails.wrapper.aria": "Thumbnails area",
"playback.player.video.wrapper.aria": "Video area",
"app.learningDashboard.dashboardTitle": "Learning Dashboard",
- "app.learningDashboard.user": "User",
+ "app.learningDashboard.downloadSessionDataLabel": "Download Session Data",
+ "app.learningDashboard.lastUpdatedLabel": "Last updated at",
+ "app.learningDashboard.sessionDataDownloadedLabel": "Downloaded!",
"app.learningDashboard.shareButton": "Share with others",
"app.learningDashboard.shareLinkCopied": "Link successfully copied!",
+ "app.learningDashboard.user": "Users",
"app.learningDashboard.indicators.meetingStatusEnded": "Ended",
"app.learningDashboard.indicators.meetingStatusActive": "Active",
"app.learningDashboard.indicators.usersOnline": "Active Users",
"app.learningDashboard.indicators.usersTotal": "Total Number Of Users",
"app.learningDashboard.indicators.polls": "Polls",
- "app.learningDashboard.indicators.raiseHand": "Raise Hand",
+ "app.learningDashboard.indicators.timeline": "Timeline",
"app.learningDashboard.indicators.activityScore": "Activity Score",
"app.learningDashboard.indicators.duration": "Duration",
"app.learningDashboard.usersTable.title": "Overview",
@@ -939,10 +952,17 @@
"app.learningDashboard.usersTable.userStatusOnline": "Online",
"app.learningDashboard.usersTable.userStatusOffline": "Offline",
"app.learningDashboard.usersTable.noUsers": "No users yet",
+ "app.learningDashboard.usersTable.name": "Name",
+ "app.learningDashboard.usersTable.moderator": "Moderator",
+ "app.learningDashboard.usersTable.pollVotes": "Poll Votes",
+ "app.learningDashboard.usersTable.join": "Join",
+ "app.learningDashboard.usersTable.left": "Left",
"app.learningDashboard.pollsTable.title": "Polling",
"app.learningDashboard.pollsTable.anonymousAnswer": "Anonymous Poll (answers in the last row)",
"app.learningDashboard.pollsTable.anonymousRowName": "Anonymous",
- "app.learningDashboard.statusTimelineTable.title": "Status Timeline",
+ "app.learningDashboard.pollsTable.noPollsCreatedHeading": "No polls have been created",
+ "app.learningDashboard.pollsTable.noPollsCreatedMessage": "Once a poll has been sent to users, their results will appear in this list.",
+ "app.learningDashboard.statusTimelineTable.title": "Timeline",
"app.learningDashboard.errors.invalidToken": "Invalid session token",
"app.learningDashboard.errors.dataUnavailable": "Data is no longer available"
}
diff --git a/bigbluebutton-html5/public/locales/pt_BR.json b/bigbluebutton-html5/public/locales/pt_BR.json
index 9ff874499d..7bac11132c 100644
--- a/bigbluebutton-html5/public/locales/pt_BR.json
+++ b/bigbluebutton-html5/public/locales/pt_BR.json
@@ -658,6 +658,10 @@
"app.connection-status.label": "Status da conexão",
"app.connection-status.notification": "Sua conexão foi perdida",
"app.connection-status.offline": "desconectado",
+ "app.connection-status.audioUploadRate": "Taxa de Upload de Áudio",
+ "app.connection-status.audioDownloadRate": "Taxa de Download de Áudio",
+ "app.connection-status.videoUploadRate": "Taxa de Upload de Video",
+ "app.connection-status.videoDownloadRate": "Taxa de Download de Video",
"app.recording.startTitle": "Iniciar gravação",
"app.recording.stopTitle": "Pausar gravação",
"app.recording.resumeTitle": "Continuar gravação",
diff --git a/build/packages-template/bigbluebutton/build.sh b/build/packages-template/bigbluebutton/build.sh
index eb4a6c5aea..62f27b2e73 100755
--- a/build/packages-template/bigbluebutton/build.sh
+++ b/build/packages-template/bigbluebutton/build.sh
@@ -52,7 +52,7 @@ Standards-Version: 3.9.2
Package: bigbluebutton
Version: $VERSION
-Maintainer: Senfcall IT
+Maintainer: ffdixon@bigbluebutton.org
Depends: $DEPENDENCIES
Architecture: amd64
Copyright: license.txt
|