Merge pull request #13574 from Tainan404/smooth-reconnection-25

Improve data structure to not break on reconnections
This commit is contained in:
Anton Georgiev 2021-11-17 15:55:21 -05:00 committed by GitHub
commit 542db847ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 446 additions and 252 deletions

View File

@ -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);

View File

@ -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) => {

View File

@ -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):

View File

@ -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 });

View File

@ -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:

View File

@ -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 });

View File

@ -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 });

View File

@ -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 });

View File

@ -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({

View File

@ -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;

View File

@ -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:

View File

@ -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 });

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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 });

View File

@ -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 });

View File

@ -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({

View File

@ -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 });
}

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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');

View File

@ -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;

View File

@ -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');

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
import React 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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -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';

View File

@ -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 = {};

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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({

View File

@ -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() {

View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -2,7 +2,7 @@ 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 Users from '/imports/api/users';
import Settings from '/imports/ui/services/settings';
import getFromUserSettings from '/imports/ui/services/users-settings';
import { ACTIONS } from '../layout/enums';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -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';

View File

@ -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';

View File

@ -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 { CurrentPoll } from '/imports/api/polls';
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';

View File

@ -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';

View File

@ -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,

View File

@ -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 });

View File

@ -4,7 +4,7 @@ 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';

View File

@ -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';

View File

@ -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';

View File

@ -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'),

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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;

View File

@ -1,5 +1,5 @@
import Storage from '/imports/ui/services/storage/session';
import Users from '/imports/ui/local-collections/users-collection/users';
import Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
const DRAW_SETTINGS = 'drawSettings';

View File

@ -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';

View File

@ -1,6 +1,6 @@
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 Users from '/imports/api/users';
import Auth from '/imports/ui/services/auth';
import getFromUserSettings from '/imports/ui/services/users-settings';

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -244,6 +244,7 @@ class Auth {
initAnnotationsStreamListener();
clearTimeout(validationTimeout);
this.connectionID = authenticationTokenValidation.connectionId;
Session.set('userWillAuth', false);
setTimeout(() => resolve(true), 100);
break;
default:

View File

@ -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();

View File

@ -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);
}
};

View File

@ -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(