diff --git a/bigbluebutton-html5/client/main.jsx b/bigbluebutton-html5/client/main.jsx index 6b95921e6e..d5fb86231b 100755 --- a/bigbluebutton-html5/client/main.jsx +++ b/bigbluebutton-html5/client/main.jsx @@ -6,6 +6,7 @@ import logger from '/imports/startup/client/logger'; import Base from '/imports/startup/client/base'; import JoinHandler from '/imports/ui/components/join-handler/component'; import AuthenticatedHandler from '/imports/ui/components/authenticated-handler/component'; +import Subscriptions from '/imports/ui/components/subscriptions/component'; Meteor.startup(() => { // Logs all uncaught exceptions to the client logger @@ -24,9 +25,11 @@ Meteor.startup(() => { // TODO make this a Promise render( - + - + + + , document.getElementById('app'), diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 1f56ae90a9..ce40367f08 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -10,23 +10,15 @@ import Settings from '/imports/ui/services/settings'; import AudioManager from '/imports/ui/services/audio-manager'; import logger from '/imports/startup/client/logger'; import Users from '/imports/api/users'; -import Annotations from '/imports/api/annotations'; -import AnnotationsLocal from '/imports/ui/components/whiteboard/service'; -import GroupChat from '/imports/api/group-chat'; -import mapUser from '/imports/ui/services/user/mapUser'; import { Session } from 'meteor/session'; import IntlStartup from './intl'; import Meetings from '../../api/meetings'; import AppService from '/imports/ui/components/app/service'; -import AnnotationsTextService from '/imports/ui/components/whiteboard/annotations/text/service'; import Breakouts from '/imports/api/breakouts'; import AudioService from '/imports/ui/components/audio/service'; import { FormattedMessage } from 'react-intl'; import { notify } from '/imports/ui/services/notification'; -const CHAT_CONFIG = Meteor.settings.public.chat; -const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id; -const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public; const HTML = document.getElementsByTagName('html')[0]; let breakoutNotified = false; @@ -35,7 +27,7 @@ const propTypes = { subscriptionsReady: PropTypes.bool, locale: PropTypes.string, approved: PropTypes.bool, - meetingHasEnded: PropTypes.bool, + meetingHasEnded: PropTypes.bool.isRequired, meetingExist: PropTypes.bool, }; @@ -44,7 +36,6 @@ const defaultProps = { approved: undefined, meetingExist: false, subscriptionsReady: false, - meetingHasEnded: false, }; const fullscreenChangedEvents = [ @@ -73,7 +64,6 @@ class Base extends Component { loading: false, meetingExisted: false, }; - this.updateLoadingState = this.updateLoadingState.bind(this); } @@ -120,7 +110,7 @@ class Base extends Component { // In case the meeting delayed to load if (!subscriptionsReady || !meetingExist) return; - if (approved && loading) this.updateLoadingState(false); + if (approved && loading && subscriptionsReady) this.updateLoadingState(false); if (prevProps.ejected || ejected) { Session.set('codeError', '403'); @@ -167,12 +157,12 @@ class Base extends Component { const codeError = Session.get('codeError'); const { ejected, - subscriptionsReady, meetingExist, meetingHasEnded, + meetingIsBreakout, + subscriptionsReady, } = this.props; - const meetingIsBreakout = AppService.meetingIsBreakout(); if ((loading || !subscriptionsReady) && !meetingHasEnded && meetingExist) { return ({loading}); } @@ -219,16 +209,10 @@ class Base extends Component { Base.propTypes = propTypes; Base.defaultProps = defaultProps; -const SUBSCRIPTIONS_NAME = [ - 'users', 'meetings', 'polls', 'presentations', - 'slides', 'captions', 'voiceUsers', 'whiteboard-multi-user', 'screenshare', - 'group-chat', 'presentation-pods', 'users-settings', 'guestUser', 'users-infos', -]; - const BaseContainer = withTracker(() => { const { locale, animations } = Settings.application; const { credentials, loggedIn } = Auth; - const { meetingId, requesterUserId } = credentials; + const { meetingId } = credentials; let breakoutRoomSubscriptionHandler; let meetingModeratorSubscriptionHandler; @@ -253,66 +237,7 @@ const BaseContainer = withTracker(() => { let userSubscriptionHandler; - const subscriptionErrorHandler = { - onError: (error) => { - logger.error({ logCode: 'startup_client_subscription_error' }, error); - Session.set('codeError', error.error); - }, - }; - - const subscriptionsHandlers = SUBSCRIPTIONS_NAME - .map(name => Meteor.subscribe(name, credentials, subscriptionErrorHandler)); - - const subscriptionsReady = subscriptionsHandlers.every(handler => handler.ready()) - && loggedIn; - - const chats = GroupChat.find({ - $or: [ - { - meetingId, - access: PUBLIC_CHAT_TYPE, - chatId: { $ne: PUBLIC_GROUP_CHAT_ID }, - }, - { meetingId, users: { $all: [requesterUserId] } }, - ], - }).fetch(); - - const chatIds = chats.map(chat => chat.chatId); - - const groupChatMessageHandler = Meteor.subscribe('group-chat-msg', credentials, chatIds, subscriptionErrorHandler); const User = Users.findOne({ intId: credentials.requesterUserId }); - let responseDelay; - let inactivityCheck; - - if (User) { - const { - responseDelay: userResponseDelay, - inactivityCheck: userInactivityCheck, - } = User; - responseDelay = userResponseDelay; - inactivityCheck = userInactivityCheck; - const mappedUser = mapUser(User); - // override meteor subscription to verify if is moderator - userSubscriptionHandler = Meteor.subscribe('users', credentials, mappedUser.isModerator, subscriptionErrorHandler); - breakoutRoomSubscriptionHandler = Meteor.subscribe('breakouts', credentials, mappedUser.isModerator, subscriptionErrorHandler); - meetingModeratorSubscriptionHandler = Meteor.subscribe('meetings', credentials, mappedUser.isModerator, subscriptionErrorHandler); - } - - const annotationsHandler = Meteor.subscribe('annotations', credentials, { - onReady: () => { - const activeTextShapeId = AnnotationsTextService.activeTextShapeId(); - AnnotationsLocal.remove({ id: { $ne: `${activeTextShapeId}-fake` } }); - Annotations.find({ id: { $ne: activeTextShapeId } }, { reactive: false }).forEach((a) => { - try { - AnnotationsLocal.insert(a); - } catch (e) { - // TODO - } - }); - annotationsHandler.stop(); - }, - ...subscriptionErrorHandler, - }); Breakouts.find().observeChanges({ added() { @@ -367,19 +292,17 @@ const BaseContainer = withTracker(() => { approved, ejected, locale, - subscriptionsReady, - annotationsHandler, - groupChatMessageHandler, userSubscriptionHandler, breakoutRoomSubscriptionHandler, meetingModeratorSubscriptionHandler, animations, - responseDelay, - inactivityCheck, User, meteorIsConnected: Meteor.status().connected, meetingExist: !!meeting, meetingHasEnded: !!meeting && meeting.meetingEnded, + meetingIsBreakout: AppService.meetingIsBreakout(), + subscriptionsReady: Session.get('subscriptionsReady'), + loggedIn, }; })(Base); diff --git a/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx new file mode 100644 index 0000000000..16177391ef --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/subscriptions/component.jsx @@ -0,0 +1,96 @@ +import React, { Component } from 'react'; +import { withTracker } from 'meteor/react-meteor-data'; +import Auth from '/imports/ui/services/auth'; +import logger from '/imports/startup/client/logger'; +import GroupChat from '/imports/api/group-chat'; +import Users from '/imports/api/users'; +import Annotations from '/imports/api/annotations'; +import AnnotationsTextService from '/imports/ui/components/whiteboard/annotations/text/service'; +import AnnotationsLocal from '/imports/ui/components/whiteboard/service'; +import mapUser from '/imports/ui/services/user/mapUser'; + +const CHAT_CONFIG = Meteor.settings.public.chat; +const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id; +const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public; +const SUBSCRIPTIONS = [ + 'users', 'meetings', 'polls', 'presentations', 'slides', 'captions', + 'voiceUsers', 'whiteboard-multi-user', 'screenshare', 'group-chat', + 'presentation-pods', 'users-settings', 'guestUser', 'users-infos', +]; + +class Subscriptions extends Component { + componentDidUpdate() { + const { subscriptionsReady } = this.props; + if (subscriptionsReady) { + Session.set('subscriptionsReady', true); + } + } + + render() { + const { children } = this.props; + return children; + } +} + +export default withTracker(() => { + const { credentials } = Auth; + const { meetingId, requesterUserId } = credentials; + + const subscriptionErrorHandler = { + onError: (error) => { + logger.error({ logCode: 'startup_client_subscription_error' }, error); + Session.set('codeError', error.error); + }, + }; + + const subscriptionsHandlers = SUBSCRIPTIONS.map(name => Meteor.subscribe(name, credentials, subscriptionErrorHandler)); + + let groupChatMessageHandler = {}; + let annotationsHandler = {}; + + const chats = GroupChat.find({ + $or: [ + { + meetingId, + access: PUBLIC_CHAT_TYPE, + chatId: { $ne: PUBLIC_GROUP_CHAT_ID }, + }, + { meetingId, users: { $all: [requesterUserId] } }, + ], + }).fetch(); + + const chatIds = chats.map(chat => chat.chatId); + groupChatMessageHandler = Meteor.subscribe('group-chat-msg', credentials, chatIds, subscriptionErrorHandler); + subscriptionsHandlers.push(groupChatMessageHandler); + const User = Users.findOne({ intId: requesterUserId }); + + if (User) { + const mappedUser = mapUser(User); + Meteor.subscribe('users', credentials, mappedUser.isModerator, subscriptionErrorHandler); + Meteor.subscribe('breakouts', credentials, mappedUser.isModerator, subscriptionErrorHandler); + Meteor.subscribe('meetings', credentials, mappedUser.isModerator, subscriptionErrorHandler); + } + + annotationsHandler = Meteor.subscribe('annotations', credentials, { + onReady: () => { + const activeTextShapeId = AnnotationsTextService.activeTextShapeId(); + AnnotationsLocal.remove({ id: { $ne: `${activeTextShapeId}-fake` } }); + }, + ...subscriptionErrorHandler, + }); + + Annotations.find({ meetingId: Auth.meetingID }).observe({ + added(doc) { + AnnotationsLocal.insert(doc); + }, + }); + + subscriptionsHandlers.push(annotationsHandler); + + const ready = subscriptionsHandlers.every(handler => handler.ready()); + + return { + subscriptionsReady: ready, + subscriptionsHandlers, + }; +})(Subscriptions);