Merge branch 'develop' of github.com:bigbluebutton/bigbluebutton into 2.4-into-develop
This commit is contained in:
commit
1622425cbc
@ -12,7 +12,16 @@ trait ChangeUserEmojiCmdMsgHdlr extends RightsManagementTrait {
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleChangeUserEmojiCmdMsg(msg: ChangeUserEmojiCmdMsg) {
|
||||
if (msg.header.userId != msg.body.userId && permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
|
||||
// Usually only moderators are allowed to change someone else's emoji status
|
||||
// Exceptional case: Viewers who are presenter are allowed to lower someone else's raised hand:
|
||||
val isViewerProhibitedFromLoweringOthersHand =
|
||||
!(Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId).get.emoji.equals("raiseHand") &&
|
||||
msg.body.emoji.equals("none")) ||
|
||||
permissionFailed(PermissionCheck.VIEWER_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)
|
||||
|
||||
if (msg.header.userId != msg.body.userId &&
|
||||
permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId) &&
|
||||
isViewerProhibitedFromLoweringOthersHand) {
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
val reason = "No permission to clear change user emoji status."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
|
@ -4,6 +4,7 @@
|
||||
<condition field="${sip_via_protocol}" expression="^wss?$" break="on-false">
|
||||
<action application="set" data="bbb_authorized=true"/>
|
||||
<action application="set" data="jb_use_timestamps=true"/>
|
||||
<action application="set" data="include_external_ip=true"/>
|
||||
<action application="transfer" data="${destination_number} XML default"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
@ -18,6 +18,7 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
|
||||
import './wdyr';
|
||||
import '../imports/ui/services/collection-hooks/collection-hooks';
|
||||
|
||||
import React from 'react';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
@ -33,6 +34,15 @@ import ChatAdapter from '/imports/ui/components/components-data/chat-context/ada
|
||||
import UsersAdapter from '/imports/ui/components/components-data/users-context/adapter';
|
||||
import GroupChatAdapter from '/imports/ui/components/components-data/group-chat-context/adapter';
|
||||
|
||||
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('/imports/api/audio/client/bridge/bridge-whitelist').catch(() => {
|
||||
// bridge loading
|
||||
});
|
||||
|
||||
Meteor.startup(() => {
|
||||
// Logs all uncaught exceptions to the client logger
|
||||
window.addEventListener('error', (e) => {
|
||||
|
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Bridge whitelist, needed for dynamically importing bridges (as modules).
|
||||
*
|
||||
* The code is intentionally unreachable, but its trigger Meteor's static
|
||||
* analysis, which makes bridge module available to build process.
|
||||
*
|
||||
* For new bridges, we must append an import statement here.
|
||||
*
|
||||
* More information here:
|
||||
*https://docs.meteor.com/packages/dynamic-import.html
|
||||
*/
|
||||
|
||||
throw new Error();
|
||||
|
||||
/* eslint-disable no-unreachable */
|
||||
// BRIDGES LIST
|
@ -305,3 +305,5 @@ export default class KurentoAudioBridge extends BaseAudioBridge {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = KurentoAudioBridge;
|
||||
|
@ -1532,3 +1532,5 @@ export default class SIPBridge extends BaseAudioBridge {
|
||||
return this.activeSession.updateAudioConstraints(constraints);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SIPBridge;
|
||||
|
@ -3,10 +3,11 @@ import Breakouts from '/imports/api/breakouts';
|
||||
import Users from '/imports/api/users';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||
import { publicationSafeGuard } from '/imports/api/common/server/helpers';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
function breakouts(role) {
|
||||
function breakouts() {
|
||||
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
|
||||
|
||||
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
|
||||
@ -25,7 +26,19 @@ function breakouts(role) {
|
||||
{ breakoutId: meetingId },
|
||||
],
|
||||
};
|
||||
// Monitor this publication and stop it when user is not a moderator anymore
|
||||
const comparisonFunc = () => {
|
||||
const user = Users.findOne({ userId, meetingId }, { fields: { role: 1, userId: 1 } });
|
||||
const condition = user.role === ROLE_MODERATOR;
|
||||
|
||||
if (!condition) {
|
||||
Logger.info(`conditions aren't filled anymore in publication ${this._name}:
|
||||
user.role === ROLE_MODERATOR :${condition}, user.role: ${user.role} ROLE_MODERATOR: ${ROLE_MODERATOR}`);
|
||||
}
|
||||
|
||||
return condition;
|
||||
};
|
||||
publicationSafeGuard(comparisonFunc, this);
|
||||
return Breakouts.find(presenterSelector);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
|
||||
const MSG_DIRECT_TYPE = 'DIRECT';
|
||||
const NODE_USER = 'nodeJSapp';
|
||||
@ -21,7 +22,7 @@ export const indexOf = [].indexOf || function (item) {
|
||||
return -1;
|
||||
};
|
||||
|
||||
export const processForHTML5ServerOnly = fn => (message, ...args) => {
|
||||
export const processForHTML5ServerOnly = (fn) => (message, ...args) => {
|
||||
const { envelope } = message;
|
||||
const { routing } = envelope;
|
||||
const { msgType, meetingId, userId } = routing;
|
||||
@ -45,3 +46,23 @@ export const extractCredentials = (credentials) => {
|
||||
const requesterUserId = credentialsArray[1];
|
||||
return { meetingId, requesterUserId };
|
||||
};
|
||||
|
||||
// Creates a background job to periodically check the result of the provided function.
|
||||
// The provided function is publication-specific and must check the "survival condition" of the publication.
|
||||
export const publicationSafeGuard = function (fn, self) {
|
||||
let stopped = false;
|
||||
const periodicCheck = function () {
|
||||
if (stopped) return;
|
||||
if (!fn()) {
|
||||
self.added(self._name, 'publication-stop-marker', { id: 'publication-stop-marker', stopped: true });
|
||||
self.stop();
|
||||
} else Meteor.setTimeout(periodicCheck, 1000);
|
||||
};
|
||||
|
||||
self.onStop(() => {
|
||||
stopped = true;
|
||||
Logger.info(`Publication ${self._name} has stopped in server side`);
|
||||
});
|
||||
|
||||
periodicCheck();
|
||||
};
|
||||
|
@ -8,4 +8,9 @@ if (Meteor.isServer) {
|
||||
UsersTyping._ensureIndex({ meetingId: 1, isTypingTo: 1 });
|
||||
}
|
||||
|
||||
// As we store chat in context, skip adding to mini mongo
|
||||
if (Meteor.isClient) {
|
||||
GroupChatMsg.onAdded = () => false;
|
||||
}
|
||||
|
||||
export { GroupChatMsg, UsersTyping };
|
||||
|
@ -40,9 +40,7 @@ export default function sendGroupChatMsg(chatId, message) {
|
||||
check(requesterUserId, String);
|
||||
check(chatId, String);
|
||||
check(message, Object);
|
||||
|
||||
const parsedMessage = parseMessage(message.message);
|
||||
|
||||
message.message = parsedMessage;
|
||||
|
||||
const payload = {
|
||||
|
@ -3,10 +3,11 @@ import Users from '/imports/api/users';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||
import { publicationSafeGuard } from '/imports/api/common/server/helpers';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
function guestUsers(role) {
|
||||
function guestUsers() {
|
||||
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
|
||||
|
||||
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
|
||||
@ -22,9 +23,22 @@ function guestUsers(role) {
|
||||
'Publishing GuestUser was requested by non-moderator connection',
|
||||
{ meetingId, userId, connectionId: this.connection.id },
|
||||
);
|
||||
|
||||
return GuestUsers.find({ meetingId: '' });
|
||||
}
|
||||
// Monitor this publication and stop it when user is not a moderator anymore
|
||||
const comparisonFunc = () => {
|
||||
const user = Users.findOne({ userId, meetingId }, { fields: { role: 1, userId: 1 } });
|
||||
const condition = user.role === ROLE_MODERATOR;
|
||||
|
||||
if (!condition) {
|
||||
Logger.info(`conditions aren't filled anymore in publication ${this._name}:
|
||||
user.role === ROLE_MODERATOR :${condition}, user.role: ${user.role} ROLE_MODERATOR: ${ROLE_MODERATOR}`);
|
||||
}
|
||||
|
||||
return condition;
|
||||
};
|
||||
publicationSafeGuard(comparisonFunc, this);
|
||||
Logger.debug(`Publishing GuestUsers for ${meetingId} ${userId}`);
|
||||
|
||||
return GuestUsers.find({ meetingId });
|
||||
|
@ -6,11 +6,12 @@ import Meetings, {
|
||||
} from '/imports/api/meetings';
|
||||
import Users from '/imports/api/users';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import { publicationSafeGuard } from '/imports/api/common/server/helpers';
|
||||
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
function meetings(role) {
|
||||
function meetings() {
|
||||
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
|
||||
|
||||
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
|
||||
@ -28,14 +29,26 @@ function meetings(role) {
|
||||
],
|
||||
};
|
||||
|
||||
const User = Users.findOne({ userId, meetingId }, { fields: { role: 1 } });
|
||||
const User = Users.findOne({ userId, meetingId }, { fields: { userId: 1, role: 1 } });
|
||||
if (!!User && User.role === ROLE_MODERATOR) {
|
||||
selector.$or.push({
|
||||
'meetingProp.isBreakout': true,
|
||||
'breakoutProps.parentId': meetingId,
|
||||
});
|
||||
}
|
||||
// Monitor this publication and stop it when user is not a moderator anymore
|
||||
const comparisonFunc = () => {
|
||||
const user = Users.findOne({ userId, meetingId }, { fields: { role: 1, userId: 1 } });
|
||||
const condition = user.role === ROLE_MODERATOR;
|
||||
|
||||
if (!condition) {
|
||||
Logger.info(`conditions aren't filled anymore in publication ${this._name}:
|
||||
user.role === ROLE_MODERATOR :${condition}, user.role: ${user.role} ROLE_MODERATOR: ${ROLE_MODERATOR}`);
|
||||
}
|
||||
|
||||
return condition;
|
||||
};
|
||||
publicationSafeGuard(comparisonFunc, this);
|
||||
}
|
||||
const options = {
|
||||
fields: {
|
||||
password: false,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
const Polls = new Mongo.Collection('polls');
|
||||
export const CurrentPoll = new Mongo.Collection('current-poll');
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// We can have just one active poll per meeting
|
||||
|
@ -14,11 +14,11 @@ function currentPoll(secretPoll) {
|
||||
});
|
||||
|
||||
if (
|
||||
!tokenValidation ||
|
||||
tokenValidation.validationStatus !== ValidationStates.VALIDATED
|
||||
!tokenValidation
|
||||
|| tokenValidation.validationStatus !== ValidationStates.VALIDATED
|
||||
) {
|
||||
Logger.warn(
|
||||
`Publishing Polls was requested by unauth connection ${this.connection.id}`
|
||||
`Publishing Polls was requested by unauth connection ${this.connection.id}`,
|
||||
);
|
||||
return Polls.find({ meetingId: '' });
|
||||
}
|
||||
@ -41,15 +41,16 @@ function currentPoll(secretPoll) {
|
||||
if ((hasPoll && hasPoll.secretPoll) || secretPoll) {
|
||||
options.fields.responses = 0;
|
||||
}
|
||||
|
||||
return Polls.find(selector, options);
|
||||
Mongo.Collection._publishCursor(Polls.find(selector, options), this, 'current-poll');
|
||||
return this.ready();
|
||||
}
|
||||
|
||||
Logger.warn(
|
||||
'Publishing current-poll was requested by non-moderator connection',
|
||||
{ meetingId, userId, connectionId: this.connection.id },
|
||||
);
|
||||
return Polls.find({ meetingId: '' });
|
||||
Mongo.Collection._publishCursor(Polls.find({ meetingId: '' }), this, 'current-poll');
|
||||
return this.ready();
|
||||
}
|
||||
|
||||
function publishCurrentPoll(...args) {
|
||||
@ -65,11 +66,11 @@ function polls() {
|
||||
});
|
||||
|
||||
if (
|
||||
!tokenValidation ||
|
||||
tokenValidation.validationStatus !== ValidationStates.VALIDATED
|
||||
!tokenValidation
|
||||
|| tokenValidation.validationStatus !== ValidationStates.VALIDATED
|
||||
) {
|
||||
Logger.warn(
|
||||
`Publishing Polls was requested by unauth connection ${this.connection.id}`
|
||||
`Publishing Polls was requested by unauth connection ${this.connection.id}`,
|
||||
);
|
||||
return Polls.find({ meetingId: '' });
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
const Users = new Mongo.Collection('users');
|
||||
export const CurrentUser = new Mongo.Collection('current-user');
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// types of queries for the users:
|
||||
|
@ -2,14 +2,15 @@ import Users from '/imports/api/users';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import Logger from '/imports/startup/server/logger';
|
||||
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||
import { extractCredentials } from '/imports/api/common/server/helpers';
|
||||
import { extractCredentials, publicationSafeGuard } from '/imports/api/common/server/helpers';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
function currentUser() {
|
||||
if (!this.userId) {
|
||||
return Users.find({ meetingId: '' });
|
||||
Mongo.Collection._publishCursor(Users.find({ meetingId: '' }), this, 'current-user');
|
||||
return this.ready();
|
||||
}
|
||||
const { meetingId, requesterUserId } = extractCredentials(this.userId);
|
||||
|
||||
@ -28,8 +29,8 @@ function currentUser() {
|
||||
authToken: false, // Not asking for authToken from client side but also not exposing it
|
||||
},
|
||||
};
|
||||
|
||||
return Users.find(selector, options);
|
||||
Mongo.Collection._publishCursor(Users.find(selector, options), this, 'current-user');
|
||||
return this.ready();
|
||||
}
|
||||
|
||||
function publishCurrentUser(...args) {
|
||||
@ -39,7 +40,7 @@ function publishCurrentUser(...args) {
|
||||
|
||||
Meteor.publish('current-user', publishCurrentUser);
|
||||
|
||||
function users(role) {
|
||||
function users() {
|
||||
const tokenValidation = AuthTokenValidation.findOne({ connectionId: this.connection.id });
|
||||
|
||||
if (!tokenValidation || tokenValidation.validationStatus !== ValidationStates.VALIDATED) {
|
||||
@ -67,6 +68,18 @@ function users(role) {
|
||||
'breakoutProps.isBreakoutUser': true,
|
||||
'breakoutProps.parentId': meetingId,
|
||||
});
|
||||
// Monitor this publication and stop it when user is not a moderator anymore
|
||||
const comparisonFunc = () => {
|
||||
const user = Users.findOne({ userId, meetingId }, { fields: { role: 1, userId: 1 } });
|
||||
const condition = user.role === ROLE_MODERATOR;
|
||||
if (!condition) {
|
||||
Logger.info(`conditions aren't filled anymore in publication ${this._name}:
|
||||
user.role === ROLE_MODERATOR :${condition}, user.role: ${user.role} ROLE_MODERATOR: ${ROLE_MODERATOR}`);
|
||||
}
|
||||
|
||||
return condition;
|
||||
};
|
||||
publicationSafeGuard(comparisonFunc, this);
|
||||
}
|
||||
|
||||
const options = {
|
||||
|
@ -8,18 +8,19 @@ 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/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import { Session } from 'meteor/session';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import Meetings, { RecordMeetings } from '../../api/meetings';
|
||||
import { RecordMeetings } from '../../api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import AppService from '/imports/ui/components/app/service';
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import AudioService from '/imports/ui/components/audio/service';
|
||||
import { notify } from '/imports/ui/services/notification';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import { LayoutContextFunc } from '../../ui/components/layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../ui/components/layout/context';
|
||||
import VideoService from '/imports/ui/components/video-provider/service';
|
||||
import DebugWindow from '/imports/ui/components/debug-window/component';
|
||||
import { ACTIONS, PANELS } from '../../ui/components/layout/enums';
|
||||
@ -183,7 +184,7 @@ class Base extends Component {
|
||||
isMeteorConnected,
|
||||
subscriptionsReady,
|
||||
layoutContextDispatch,
|
||||
layoutContextState,
|
||||
sidebarContentPanel,
|
||||
usersVideo,
|
||||
} = this.props;
|
||||
const {
|
||||
@ -191,10 +192,6 @@ class Base extends Component {
|
||||
meetingExisted,
|
||||
} = this.state;
|
||||
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
|
||||
if (usersVideo !== prevProps.usersVideo) {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_NUM_CAMERAS,
|
||||
@ -377,7 +374,15 @@ class Base extends Component {
|
||||
Base.propTypes = propTypes;
|
||||
Base.defaultProps = defaultProps;
|
||||
|
||||
const BaseContainer = withTracker(() => {
|
||||
const BaseContainer = (props) => {
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <Base {...{ sidebarContentPanel, layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
||||
export default withTracker(() => {
|
||||
const {
|
||||
animations,
|
||||
} = Settings.application;
|
||||
@ -510,6 +515,4 @@ const BaseContainer = withTracker(() => {
|
||||
codeError,
|
||||
usersVideo,
|
||||
};
|
||||
})(LayoutContextFunc.withContext(Base));
|
||||
|
||||
export default BaseContainer;
|
||||
})(BaseContainer);
|
||||
|
@ -1,17 +1,16 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Presentations from '/imports/api/presentations';
|
||||
import PresentationUploaderService from '/imports/ui/components/presentation/presentation-uploader/service';
|
||||
import PresentationPodService from '/imports/ui/components/presentation-pod/service';
|
||||
import ActionsDropdown from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
|
||||
const ActionsDropdownContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent, sidebarNavigation } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const sidebarNavigation = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return (
|
||||
<ActionsDropdown {...{
|
||||
|
@ -12,7 +12,7 @@ import Service from './service';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import ExternalVideoService from '/imports/ui/components/external-video-player/service';
|
||||
import CaptionsService from '/imports/ui/components/captions/service';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import { isVideoBroadcasting } from '/imports/ui/components/screenshare/service';
|
||||
|
||||
import MediaService, {
|
||||
@ -21,12 +21,11 @@ import MediaService, {
|
||||
} from '../media/service';
|
||||
|
||||
const ActionsBarContainer = (props) => {
|
||||
const actionsBarStyle = layoutSelectOutput((i) => i.actionBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { output } = layoutContextState;
|
||||
const { actionBar: actionsBarStyle } = output;
|
||||
|
||||
const currentUser = { userId: Auth.userID, emoji: users[Auth.meetingID][Auth.userID].emoji };
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import QuickPollDropdown from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutDispatch } from '../../layout/context';
|
||||
import PollService from '/imports/ui/components/poll/service';
|
||||
|
||||
const QuickPollDropdownContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <QuickPollDropdown {...{ layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import { getVideoUrl } from '/imports/ui/components/external-video-player/service';
|
||||
|
||||
const USER_CONFIG = Meteor.settings.public.user;
|
||||
|
@ -29,16 +29,11 @@ import PresentationAreaContainer from '../presentation/presentation-area/contain
|
||||
import ScreenshareContainer from '../screenshare/container';
|
||||
import ExternalVideoContainer from '../external-video-player/container';
|
||||
import { styles } from './styles';
|
||||
import {
|
||||
LAYOUT_TYPE, DEVICE_TYPE, ACTIONS,
|
||||
} from '../layout/enums';
|
||||
import { DEVICE_TYPE, ACTIONS } from '../layout/enums';
|
||||
import {
|
||||
isMobile, isTablet, isTabletPortrait, isTabletLandscape, isDesktop,
|
||||
} from '../layout/utils';
|
||||
import CustomLayout from '../layout/layout-manager/customLayout';
|
||||
import SmartLayout from '../layout/layout-manager/smartLayout';
|
||||
import PresentationFocusLayout from '../layout/layout-manager/presentationFocusLayout';
|
||||
import VideoFocusLayout from '../layout/layout-manager/videoFocusLayout';
|
||||
import LayoutEngine from '../layout/layout-manager/layoutEngine';
|
||||
import NavBarContainer from '../nav-bar/container';
|
||||
import SidebarNavigationContainer from '../sidebar-navigation/container';
|
||||
import SidebarContentContainer from '../sidebar-content/container';
|
||||
@ -426,22 +421,6 @@ class App extends Component {
|
||||
) : null);
|
||||
}
|
||||
|
||||
renderLayoutManager() {
|
||||
const { layoutType } = this.props;
|
||||
switch (layoutType) {
|
||||
case LAYOUT_TYPE.CUSTOM_LAYOUT:
|
||||
return <CustomLayout />;
|
||||
case LAYOUT_TYPE.SMART_LAYOUT:
|
||||
return <SmartLayout />;
|
||||
case LAYOUT_TYPE.PRESENTATION_FOCUS:
|
||||
return <PresentationFocusLayout />;
|
||||
case LAYOUT_TYPE.VIDEO_FOCUS:
|
||||
return <VideoFocusLayout />;
|
||||
default:
|
||||
return <CustomLayout />;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
customStyle,
|
||||
@ -452,11 +431,12 @@ class App extends Component {
|
||||
shouldShowScreenshare,
|
||||
shouldShowExternalVideo,
|
||||
isPresenter,
|
||||
layoutType,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.renderLayoutManager()}
|
||||
<LayoutEngine layoutType={layoutType} />
|
||||
<div
|
||||
id="layout"
|
||||
className={styles.layout}
|
||||
|
@ -1,20 +1,25 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
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 AuthTokenValidation from '/imports/api/auth-token-validation';
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import { notify } from '/imports/ui/services/notification';
|
||||
import CaptionsContainer from '/imports/ui/components/captions/container';
|
||||
import CaptionsService from '/imports/ui/components/captions/service';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import UserInfos from '/imports/api/users-infos';
|
||||
import LayoutContext from '../layout/context';
|
||||
import Settings from '/imports/ui/services/settings';
|
||||
import MediaService from '/imports/ui/components/media/service';
|
||||
import {
|
||||
layoutSelect,
|
||||
layoutSelectInput,
|
||||
layoutSelectOutput,
|
||||
layoutDispatch
|
||||
} from '../layout/context';
|
||||
|
||||
import {
|
||||
getFontSize,
|
||||
@ -51,9 +56,6 @@ const endMeeting = (code) => {
|
||||
};
|
||||
|
||||
const AppContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
|
||||
const {
|
||||
actionsbar,
|
||||
meetingLayout,
|
||||
@ -64,19 +66,19 @@ const AppContainer = (props) => {
|
||||
presentationRestoreOnUpdate,
|
||||
...otherProps
|
||||
} = props;
|
||||
const {
|
||||
input,
|
||||
output,
|
||||
layoutType,
|
||||
deviceType,
|
||||
} = layoutContextState;
|
||||
const { sidebarContent, sidebarNavigation, presentation } = input;
|
||||
const { actionBar: actionsBarStyle, captions: captionsStyle } = output;
|
||||
const { sidebarNavPanel } = sidebarNavigation;
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const sidebarNavigationIsOpen = sidebarNavigation.isOpen;
|
||||
const sidebarContentIsOpen = sidebarContent.isOpen;
|
||||
const presentationIsOpen = presentation.isOpen;
|
||||
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const sidebarNavigation = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const actionsBarStyle = layoutSelectOutput((i) => i.actionBar);
|
||||
const captionsStyle = layoutSelectOutput((i) => i.captions);
|
||||
const presentation = layoutSelectInput((i) => i.presentation);
|
||||
const layoutType = layoutSelect((i) => i.layoutType);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { sidebarContentPanel, isOpen: sidebarContentIsOpen } = sidebarContent;
|
||||
const { sidebarNavPanel, isOpen: sidebarNavigationIsOpen } = sidebarNavigation;
|
||||
const { isOpen: presentationIsOpen } = presentation;
|
||||
const shouldShowPresentation = propsShouldShowPresentation
|
||||
&& (presentationIsOpen || presentationRestoreOnUpdate);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Settings from '/imports/ui/services/settings';
|
||||
import Auth from '/imports/ui/services/auth/index';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
|
@ -5,7 +5,7 @@ import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import browserInfo from '/imports/utils/browserInfo';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import AudioModal from './component';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/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';
|
||||
|
@ -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/api/breakouts';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import AppService from '/imports/ui/components/app/service';
|
||||
import { notify } from '/imports/ui/services/notification';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import VoiceUsers from '/imports/api/voice-users';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
|
@ -1,15 +1,13 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { Session } from 'meteor/session';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import BannerComponent from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../layout/context';
|
||||
|
||||
const BannerContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { bannerBar } = input;
|
||||
const bannerBar = layoutSelectInput((i) => i.bannerBar);
|
||||
const { hasBanner } = bannerBar;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <BannerComponent {...{ hasBanner, layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import breakoutService from '/imports/ui/components/breakout-room/service';
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React 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 LayoutContext from '../layout/context';
|
||||
import { layoutDispatch } from '../layout/context';
|
||||
|
||||
const BreakoutContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <BreakoutComponent {...{ layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Breakouts from '/imports/api/breakouts';
|
||||
import Meetings, { MeetingTimeRemaining } from '/imports/api/meetings';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import { MeetingTimeRemaining } from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import { Session } from 'meteor/session';
|
||||
import CaptionsService from '/imports/ui/components/captions/service';
|
||||
import Pad from './component';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
import { ACTIONS, PANELS } from '../../layout/enums';
|
||||
|
||||
const PadContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch, layoutContextState } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { cameraDock } = input;
|
||||
const cameraDock = layoutSelectInput((i) => i.cameraDock);
|
||||
const { isResizing } = cameraDock;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const {
|
||||
amIModerator,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import Captions from '/imports/api/captions';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
@ -3,15 +3,15 @@ import { withTracker } from 'meteor/react-meteor-data';
|
||||
import { withModalMounter } from '/imports/ui/components/modal/service';
|
||||
import CaptionsService from '/imports/ui/components/captions/service';
|
||||
import WriterMenu from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutDispatch } from '../../layout/context';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||
|
||||
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
const WriterMenuContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const currentUser = users[Auth.meetingID][Auth.userID];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ChatAlert from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelect, layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
import { PANELS } from '../../layout/enums';
|
||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||
import { ChatContext } from '/imports/ui/components/components-data/chat-context/context';
|
||||
@ -17,11 +17,11 @@ const propTypes = {
|
||||
};
|
||||
|
||||
const ChatAlertContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { idChatOpen, input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const idChatOpen = layoutSelect((i) => i.idChatOpen);
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { audioAlertEnabled, pushAlertEnabled } = props;
|
||||
|
||||
let idChat = idChatOpen;
|
||||
|
@ -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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||
import ChatDropdown from './component';
|
||||
|
||||
|
@ -12,7 +12,7 @@ import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
|
||||
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
|
||||
import Chat from '/imports/ui/components/chat/component';
|
||||
import ChatService from './service';
|
||||
import { LayoutContextFunc } from '../layout/context';
|
||||
import { layoutSelect, layoutDispatch } from '../layout/context';
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const PUBLIC_CHAT_KEY = CHAT_CONFIG.public_id;
|
||||
@ -71,11 +71,12 @@ const ChatContainer = (props) => {
|
||||
isChatLockedPublic,
|
||||
isChatLockedPrivate,
|
||||
users: propUsers,
|
||||
layoutContextState,
|
||||
layoutContextDispatch,
|
||||
...restProps
|
||||
} = props;
|
||||
const { idChatOpen } = layoutContextState;
|
||||
|
||||
const idChatOpen = layoutSelect((i) => i.idChatOpen);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const isPublicChat = idChatOpen === PUBLIC_CHAT_KEY;
|
||||
|
||||
const chatID = idChatOpen;
|
||||
@ -264,4 +265,4 @@ export default lockContextContainer(injectIntl(withTracker(({ intl, userLocks })
|
||||
handleClosePrivateChat: ChatService.closePrivateChat,
|
||||
},
|
||||
};
|
||||
})(LayoutContextFunc.withConsumer(ChatContainer))));
|
||||
})(ChatContainer)));
|
||||
|
@ -1,17 +1,16 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import MessageForm from './component';
|
||||
import ChatService from '/imports/ui/components/chat/service';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelect } from '../../layout/context';
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const START_TYPING_THROTTLE_INTERVAL = 2000;
|
||||
|
||||
const MessageFormContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState } = layoutContext;
|
||||
const { idChatOpen } = layoutContextState;
|
||||
const idChatOpen = layoutSelect((i) => i.idChatOpen);
|
||||
|
||||
const handleSendMessage = (message) => {
|
||||
ChatService.setUserSentMessage(true);
|
||||
return ChatService.sendGroupMessage(message, idChatOpen);
|
||||
|
@ -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/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import TypingIndicator from './component';
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import GroupChat from '/imports/api/group-chat';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import UnreadMessages from '/imports/ui/services/unread-messages';
|
||||
|
@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
||||
import TimeWindowChatItem from './component';
|
||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||
import ChatService from '../../service';
|
||||
import LayoutContext from '../../../layout/context';
|
||||
import { layoutSelect } from '../../../layout/context';
|
||||
import PollService from '/imports/ui/components/poll/service';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
|
||||
@ -12,9 +12,9 @@ const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
||||
|
||||
const TimeWindowChatItemContainer = (props) => {
|
||||
const { message, messageId } = props;
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState } = layoutContext;
|
||||
const { idChatOpen } = layoutContextState;
|
||||
|
||||
const idChatOpen = layoutSelect((i) => i.idChatOpen);
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const {
|
||||
|
@ -5,6 +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';
|
||||
|
||||
let prevUserData = {};
|
||||
let currentUserData = {};
|
||||
@ -119,23 +120,19 @@ const Adapter = () => {
|
||||
});
|
||||
}, 1000, { trailing: true, leading: true });
|
||||
|
||||
Meteor.connection._stream.socket.addEventListener('message', (msg) => {
|
||||
if (msg.data.indexOf('{"msg":"added","collection":"group-chat-msg"') !== -1) {
|
||||
const parsedMsg = JSON.parse(msg.data);
|
||||
if (parsedMsg.msg === 'added') {
|
||||
const { fields } = parsedMsg;
|
||||
if (fields.id === `${SYSTEM_CHAT_TYPE}-${CHAT_CLEAR_MESSAGE}`) {
|
||||
messageQueue = [];
|
||||
dispatch({
|
||||
type: ACTIONS.REMOVED,
|
||||
});
|
||||
}
|
||||
|
||||
messageQueue.push(fields);
|
||||
throttledDispatch();
|
||||
}
|
||||
const insertToContext = (fields) => {
|
||||
if (fields.id === `${SYSTEM_CHAT_TYPE}-${CHAT_CLEAR_MESSAGE}`) {
|
||||
messageQueue = [];
|
||||
dispatch({
|
||||
type: ACTIONS.REMOVED,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
messageQueue.push(fields);
|
||||
throttledDispatch();
|
||||
};
|
||||
|
||||
CollectionEventsBroker.addListener('group-chat-msg', 'added', insertToContext);
|
||||
}, [Meteor.status().connected, Meteor.connection._lastSessionId]);
|
||||
|
||||
return null;
|
||||
|
@ -3,7 +3,7 @@ import React, {
|
||||
useReducer,
|
||||
} from 'react';
|
||||
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/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';
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import Users from '/imports/api/users';
|
||||
import { CurrentUser } from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import UsersPersistentData from '/imports/api/users-persistent-data';
|
||||
import { UsersContext, ACTIONS } from './context';
|
||||
import ChatLogger from '/imports/ui/components/chat/chat-logger/ChatLogger';
|
||||
@ -8,11 +9,11 @@ const Adapter = () => {
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { dispatch } = usingUsersContext;
|
||||
|
||||
useEffect(()=> {
|
||||
useEffect(() => {
|
||||
const usersPersistentDataCursor = UsersPersistentData.find({}, { sort: { timestamp: 1 } });
|
||||
usersPersistentDataCursor.observe({
|
||||
added: (obj) => {
|
||||
ChatLogger.debug("usersAdapter::observe::added_persistent_user", obj);
|
||||
ChatLogger.debug('usersAdapter::observe::added_persistent_user', obj);
|
||||
dispatch({
|
||||
type: ACTIONS.ADDED_USER_PERSISTENT_DATA,
|
||||
value: {
|
||||
@ -21,7 +22,7 @@ const Adapter = () => {
|
||||
});
|
||||
},
|
||||
changed: (obj) => {
|
||||
ChatLogger.debug("usersAdapter::observe::changed_persistent_user", obj);
|
||||
ChatLogger.debug('usersAdapter::observe::changed_persistent_user', obj);
|
||||
dispatch({
|
||||
type: ACTIONS.CHANGED_USER_PERSISTENT_DATA,
|
||||
value: {
|
||||
@ -29,15 +30,16 @@ const Adapter = () => {
|
||||
},
|
||||
});
|
||||
},
|
||||
removed: (obj) => {},
|
||||
removed: () => {},
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const usersCursor = Users.find({}, { sort: { timestamp: 1 } });
|
||||
const CurrentUserCursor = CurrentUser.find({});
|
||||
usersCursor.observe({
|
||||
added: (obj) => {
|
||||
ChatLogger.debug("usersAdapter::observe::added", obj);
|
||||
ChatLogger.debug('usersAdapter::observe::added', obj);
|
||||
dispatch({
|
||||
type: ACTIONS.ADDED,
|
||||
value: {
|
||||
@ -54,6 +56,18 @@ const Adapter = () => {
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
CurrentUserCursor.observe({
|
||||
added: (obj) => {
|
||||
ChatLogger.debug('usersAdapter::observe::current-user::added', obj);
|
||||
dispatch({
|
||||
type: ACTIONS.ADDED,
|
||||
value: {
|
||||
user: obj,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defineMessages } from 'react-intl';
|
||||
import ConnectionStatus from '/imports/api/connection-status';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import UsersPersistentData from '/imports/api/users-persistent-data';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Settings from '/imports/ui/services/settings';
|
||||
|
@ -2,12 +2,13 @@ import React from 'react';
|
||||
import { ChatContextProvider } from '/imports/ui/components/components-data/chat-context/context';
|
||||
import { UsersContextProvider } from '/imports/ui/components/components-data/users-context/context';
|
||||
import { GroupChatContextProvider } from '/imports/ui/components/components-data/group-chat-context/context';
|
||||
|
||||
import { LayoutContextProvider } from '/imports/ui/components/layout/context';
|
||||
|
||||
const providersList = [
|
||||
ChatContextProvider,
|
||||
GroupChatContextProvider,
|
||||
UsersContextProvider,
|
||||
LayoutContextProvider,
|
||||
];
|
||||
|
||||
const ContextProvidersComponent = props => providersList.reduce((acc, Component) => (
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
|
||||
const getMeetingTitle = () => {
|
||||
const meeting = Meetings.findOne({
|
||||
|
@ -1,19 +1,18 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import { Session } from 'meteor/session';
|
||||
import { getVideoUrl } from './service';
|
||||
import ExternalVideoComponent from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectInput, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import MediaService, { getSwapLayout } from '/imports/ui/components/media/service';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
|
||||
const ExternalVideoContainer = (props) => {
|
||||
const layoutManager = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutManager;
|
||||
const { output, input } = layoutContextState;
|
||||
const { externalVideo } = output;
|
||||
const { cameraDock } = input;
|
||||
const externalVideo = layoutSelectOutput((i) => i.externalVideo);
|
||||
const cameraDock = layoutSelectInput((i) => i.cameraDock);
|
||||
const { isResizing } = cameraDock;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return (
|
||||
<ExternalVideoComponent
|
||||
{
|
||||
|
@ -1,17 +1,16 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import FullscreenButtonComponent from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelect, layoutDispatch } from '../layout/context';
|
||||
|
||||
const FullscreenButtonContainer = (props) => <FullscreenButtonComponent {...props} />;
|
||||
|
||||
export default (props) => {
|
||||
const isIphone = !!(navigator.userAgent.match(/iPhone/i));
|
||||
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { fullscreen } = layoutContextState;
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const { element: currentElement, group: currentGroup } = fullscreen;
|
||||
const isFullscreen = !!currentElement;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return (
|
||||
<FullscreenButtonContainer
|
||||
|
@ -7,7 +7,7 @@ import { setCustomLogoUrl, setModeratorOnlyMessage } from '/imports/ui/component
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import LoadingScreen from '/imports/ui/components/loading-screen/component';
|
||||
import Users from '/imports/api/users';
|
||||
import { CurrentUser } from '/imports/api/users';
|
||||
|
||||
const propTypes = {
|
||||
children: PropTypes.element.isRequired,
|
||||
@ -162,7 +162,7 @@ class JoinHandler extends Component {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
if (customdata.length) {
|
||||
makeCall('addUserSettings', customdata).then(r => resolve(r));
|
||||
makeCall('addUserSettings', customdata).then((r) => resolve(r));
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
@ -190,8 +190,8 @@ class JoinHandler extends Component {
|
||||
setModOnlyMessage(response);
|
||||
|
||||
Tracker.autorun(async (cd) => {
|
||||
const user = Users.findOne({ userId: Auth.userID, approved: true }, { fields: { _id: 1 } });
|
||||
|
||||
const user = CurrentUser
|
||||
.findOne({ userId: Auth.userID, approved: true }, { fields: { _id: 1 } });
|
||||
if (user) {
|
||||
await setCustomData(response);
|
||||
cd.stop();
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { createContext, useReducer } from 'react';
|
||||
import React, { useReducer } from 'react';
|
||||
import { createContext, useContextSelector } from 'use-context-selector';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ACTIONS } from '/imports/ui/components/layout/enums';
|
||||
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
||||
@ -24,7 +25,7 @@ const providerPropTypes = {
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
const LayoutContext = createContext();
|
||||
const LayoutContextSelector = createContext();
|
||||
|
||||
const initState = {
|
||||
deviceType: null,
|
||||
@ -333,12 +334,12 @@ const reducer = (state, action) => {
|
||||
} = action.value;
|
||||
const { sidebarNavigation } = state.output;
|
||||
if (sidebarNavigation.display === display
|
||||
&& sidebarNavigation.minWidth === width
|
||||
&& sidebarNavigation.maxWidth === width
|
||||
&& sidebarNavigation.minWidth === minWidth
|
||||
&& sidebarNavigation.maxWidth === maxWidth
|
||||
&& sidebarNavigation.width === width
|
||||
&& sidebarNavigation.minHeight === height
|
||||
&& sidebarNavigation.minHeight === minHeight
|
||||
&& sidebarNavigation.height === height
|
||||
&& sidebarNavigation.maxHeight === height
|
||||
&& sidebarNavigation.maxHeight === maxHeight
|
||||
&& sidebarNavigation.top === top
|
||||
&& sidebarNavigation.left === left
|
||||
&& sidebarNavigation.right === right
|
||||
@ -1129,37 +1130,40 @@ const reducer = (state, action) => {
|
||||
}
|
||||
};
|
||||
|
||||
const ContextProvider = (props) => {
|
||||
const LayoutContextProvider = (props) => {
|
||||
const [layoutContextState, layoutContextDispatch] = useReducer(reducer, initState);
|
||||
const { children } = props;
|
||||
return (
|
||||
<LayoutContext.Provider value={{
|
||||
layoutContextState,
|
||||
layoutContextDispatch,
|
||||
}}
|
||||
<LayoutContextSelector.Provider value={
|
||||
[
|
||||
layoutContextState,
|
||||
layoutContextDispatch,
|
||||
]
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</LayoutContext.Provider>
|
||||
</LayoutContextSelector.Provider>
|
||||
);
|
||||
};
|
||||
ContextProvider.propTypes = providerPropTypes;
|
||||
LayoutContextProvider.propTypes = providerPropTypes;
|
||||
|
||||
const withProvider = (Component) => (props) => (
|
||||
<ContextProvider>
|
||||
<Component {...props} />
|
||||
</ContextProvider>
|
||||
);
|
||||
|
||||
const withConsumer = (Component) => (props) => (
|
||||
<LayoutContext.Consumer>
|
||||
{(contexts) => <Component {...props} {...contexts} />}
|
||||
</LayoutContext.Consumer>
|
||||
);
|
||||
|
||||
export default LayoutContext;
|
||||
|
||||
export const LayoutContextFunc = {
|
||||
withProvider,
|
||||
withConsumer,
|
||||
withContext: (Component) => withProvider(withConsumer(Component)),
|
||||
const layoutSelect = (selector) => {
|
||||
return useContextSelector(LayoutContextSelector, layout => selector(layout[0]));
|
||||
};
|
||||
const layoutSelectInput = (selector) => {
|
||||
return useContextSelector(LayoutContextSelector, layout => selector(layout[0].input));
|
||||
};
|
||||
const layoutSelectOutput = (selector) => {
|
||||
return useContextSelector(LayoutContextSelector, layout => selector(layout[0].output));
|
||||
};
|
||||
const layoutDispatch = () => {
|
||||
return useContextSelector(LayoutContextSelector, layout => layout[1]);
|
||||
};
|
||||
|
||||
export {
|
||||
LayoutContextProvider,
|
||||
layoutSelect,
|
||||
layoutSelectInput,
|
||||
layoutSelectOutput,
|
||||
layoutDispatch,
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,302 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { layoutSelect, layoutSelectInput } from '/imports/ui/components/layout/context';
|
||||
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
||||
import { LAYOUT_TYPE, DEVICE_TYPE } from '/imports/ui/components/layout/enums';
|
||||
|
||||
import CustomLayout from '/imports/ui/components/layout/layout-manager/customLayout';
|
||||
import SmartLayout from '/imports/ui/components/layout/layout-manager/smartLayout';
|
||||
import PresentationFocusLayout from '/imports/ui/components/layout/layout-manager/presentationFocusLayout';
|
||||
import VideoFocusLayout from '/imports/ui/components/layout/layout-manager/videoFocusLayout';
|
||||
|
||||
const propTypes = {
|
||||
layoutType: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const LayoutEngine = ({ layoutType }) => {
|
||||
const bannerBarInput = layoutSelectInput((i) => i.bannerBar);
|
||||
const notificationsBarInput = layoutSelectInput((i) => i.notificationsBar);
|
||||
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
|
||||
const presentationInput = layoutSelectInput((i) => i.presentation);
|
||||
const actionbarInput = layoutSelectInput((i) => i.actionBar);
|
||||
const sidebarNavigationInput = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const fontSize = layoutSelect((i) => i.fontSize);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
|
||||
const isMobile = deviceType === DEVICE_TYPE.MOBILE;
|
||||
const isTablet = deviceType === DEVICE_TYPE.TABLET;
|
||||
const windowWidth = () => window.document.documentElement.clientWidth;
|
||||
const windowHeight = () => window.document.documentElement.clientHeight;
|
||||
const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
|
||||
const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
|
||||
|
||||
const bannerAreaHeight = () => {
|
||||
const { hasNotification } = notificationsBarInput;
|
||||
const { hasBanner } = bannerBarInput;
|
||||
const bannerHeight = hasBanner ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
const notificationHeight = hasNotification ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
|
||||
return bannerHeight + notificationHeight;
|
||||
};
|
||||
|
||||
const baseCameraDockBounds = (mediaAreaBounds, sidebarSize) => {
|
||||
const { isOpen } = presentationInput;
|
||||
|
||||
const cameraDockBounds = {};
|
||||
|
||||
if (cameraDockInput.numCameras === 0) {
|
||||
cameraDockBounds.width = 0;
|
||||
cameraDockBounds.height = 0;
|
||||
|
||||
return cameraDockBounds;
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
|
||||
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
||||
}
|
||||
|
||||
if (fullscreen.group === 'webcams') {
|
||||
cameraDockBounds.width = windowWidth();
|
||||
cameraDockBounds.minWidth = windowWidth();
|
||||
cameraDockBounds.maxWidth = windowWidth();
|
||||
cameraDockBounds.height = windowHeight();
|
||||
cameraDockBounds.minHeight = windowHeight();
|
||||
cameraDockBounds.maxHeight = windowHeight();
|
||||
cameraDockBounds.top = 0;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.zIndex = 99;
|
||||
|
||||
return cameraDockBounds;
|
||||
}
|
||||
|
||||
return cameraDockBounds;
|
||||
};
|
||||
|
||||
const calculatesNavbarBounds = (mediaAreaBounds) => {
|
||||
const { navBarHeight, navBarTop } = DEFAULT_VALUES;
|
||||
|
||||
return {
|
||||
width: mediaAreaBounds.width,
|
||||
height: navBarHeight,
|
||||
top: navBarTop + bannerAreaHeight(),
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesActionbarHeight = () => {
|
||||
const { actionBarHeight, actionBarPadding } = DEFAULT_VALUES;
|
||||
|
||||
const BASE_FONT_SIZE = 14; // 90% font size
|
||||
const height = ((actionBarHeight / BASE_FONT_SIZE) * fontSize);
|
||||
|
||||
return {
|
||||
height: height + (actionBarPadding * 2),
|
||||
innerHeight: height,
|
||||
padding: actionBarPadding,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesActionbarBounds = (mediaAreaBounds) => {
|
||||
const actionBarHeight = calculatesActionbarHeight();
|
||||
|
||||
return {
|
||||
display: actionbarInput.hasActionBar,
|
||||
width: mediaAreaBounds.width,
|
||||
height: actionBarHeight.height,
|
||||
innerHeight: actionBarHeight.innerHeight,
|
||||
padding: actionBarHeight.padding,
|
||||
top: windowHeight() - actionBarHeight.height,
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesSidebarNavWidth = () => {
|
||||
const {
|
||||
sidebarNavMinWidth,
|
||||
sidebarNavMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
|
||||
const { isOpen, width: sidebarNavWidth } = sidebarNavigationInput;
|
||||
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (isOpen) {
|
||||
if (isMobile) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (sidebarNavWidth === 0) {
|
||||
width = min(max((windowWidth() * 0.2), sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
} else {
|
||||
width = min(max(sidebarNavWidth, sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
}
|
||||
minWidth = sidebarNavMinWidth;
|
||||
maxWidth = sidebarNavMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesSidebarNavHeight = () => {
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
const { isOpen } = sidebarNavigationInput;
|
||||
|
||||
let sidebarNavHeight = 0;
|
||||
if (isOpen) {
|
||||
if (isMobile) {
|
||||
sidebarNavHeight = windowHeight() - navBarHeight - bannerAreaHeight();
|
||||
} else {
|
||||
sidebarNavHeight = windowHeight() - bannerAreaHeight();
|
||||
}
|
||||
}
|
||||
return sidebarNavHeight;
|
||||
};
|
||||
|
||||
const calculatesSidebarNavBounds = () => {
|
||||
const { sidebarNavTop, navBarHeight, sidebarNavLeft } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + bannerAreaHeight();
|
||||
|
||||
if (isMobile) {
|
||||
top = navBarHeight + bannerAreaHeight();
|
||||
}
|
||||
|
||||
return {
|
||||
top,
|
||||
left: !isRTL ? sidebarNavLeft : null,
|
||||
right: isRTL ? sidebarNavLeft : null,
|
||||
zIndex: isMobile ? 11 : 2,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesSidebarContentWidth = () => {
|
||||
const {
|
||||
sidebarContentMinWidth,
|
||||
sidebarContentMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
|
||||
const { isOpen, width: sidebarContentWidth } = sidebarContentInput;
|
||||
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
|
||||
if (isOpen) {
|
||||
if (isMobile) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (sidebarContentWidth === 0) {
|
||||
width = min(
|
||||
max((windowWidth() * 0.2), sidebarContentMinWidth), sidebarContentMaxWidth,
|
||||
);
|
||||
} else {
|
||||
width = min(max(sidebarContentWidth, sidebarContentMinWidth),
|
||||
sidebarContentMaxWidth);
|
||||
}
|
||||
minWidth = sidebarContentMinWidth;
|
||||
maxWidth = sidebarContentMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesSidebarContentBounds = (sidebarNavWidth) => {
|
||||
const { navBarHeight, sidebarNavTop } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + bannerAreaHeight();
|
||||
|
||||
if (isMobile) top = navBarHeight + bannerAreaHeight();
|
||||
|
||||
let left = isMobile ? 0 : sidebarNavWidth;
|
||||
let right = isMobile ? 0 : sidebarNavWidth;
|
||||
left = !isRTL ? left : null;
|
||||
right = isRTL ? right : null;
|
||||
|
||||
const zIndex = isMobile ? 11 : 1;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
right,
|
||||
zIndex,
|
||||
};
|
||||
};
|
||||
|
||||
const calculatesMediaAreaBounds = (sidebarNavWidth, sidebarContentWidth) => {
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
const { height: actionBarHeight } = calculatesActionbarHeight();
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (isMobile) {
|
||||
width = windowWidth();
|
||||
} else {
|
||||
left = !isRTL ? sidebarNavWidth + sidebarContentWidth : 0;
|
||||
width = windowWidth() - sidebarNavWidth - sidebarContentWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height: windowHeight() - (navBarHeight + actionBarHeight + bannerAreaHeight()),
|
||||
top: navBarHeight + bannerAreaHeight(),
|
||||
left,
|
||||
};
|
||||
};
|
||||
|
||||
const common = {
|
||||
bannerAreaHeight,
|
||||
baseCameraDockBounds,
|
||||
calculatesNavbarBounds,
|
||||
calculatesActionbarHeight,
|
||||
calculatesActionbarBounds,
|
||||
calculatesSidebarNavWidth,
|
||||
calculatesSidebarNavHeight,
|
||||
calculatesSidebarNavBounds,
|
||||
calculatesSidebarContentWidth,
|
||||
calculatesSidebarContentBounds,
|
||||
calculatesMediaAreaBounds,
|
||||
isMobile,
|
||||
isTablet,
|
||||
};
|
||||
|
||||
switch (layoutType) {
|
||||
case LAYOUT_TYPE.CUSTOM_LAYOUT:
|
||||
return <CustomLayout {...common} />;
|
||||
case LAYOUT_TYPE.SMART_LAYOUT:
|
||||
return <SmartLayout {...common} />;
|
||||
case LAYOUT_TYPE.PRESENTATION_FOCUS:
|
||||
return <PresentationFocusLayout {...common} />;
|
||||
case LAYOUT_TYPE.VIDEO_FOCUS:
|
||||
return <VideoFocusLayout {...common} />;
|
||||
default:
|
||||
return <CustomLayout {...common} />;
|
||||
}
|
||||
};
|
||||
|
||||
LayoutEngine.propTypes = propTypes;
|
||||
|
||||
export default LayoutEngine;
|
@ -1,26 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import { throttle, defaultsDeep } from 'lodash';
|
||||
import { LayoutContextFunc } from '/imports/ui/components/layout/context';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { layoutDispatch, layoutSelect, layoutSelectInput } from '/imports/ui/components/layout/context';
|
||||
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
||||
import { INITIAL_INPUT_STATE } from '/imports/ui/components/layout/initState';
|
||||
import { DEVICE_TYPE, ACTIONS, PANELS } from '/imports/ui/components/layout/enums';
|
||||
import { ACTIONS, PANELS } from '/imports/ui/components/layout/enums';
|
||||
|
||||
const windowWidth = () => window.document.documentElement.clientWidth;
|
||||
const windowHeight = () => window.document.documentElement.clientHeight;
|
||||
const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
|
||||
const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
|
||||
|
||||
class PresentationFocusLayout extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const PresentationFocusLayout = (props) => {
|
||||
const { bannerAreaHeight, isMobile } = props;
|
||||
|
||||
this.throttledCalculatesLayout = throttle(() => this.calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.init();
|
||||
const { layoutContextDispatch } = this.props;
|
||||
const input = layoutSelect((i) => i.input);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const fontSize = layoutSelect((i) => i.fontSize);
|
||||
const currentPanelType = layoutSelect((i) => i.currentPanelType);
|
||||
|
||||
const presentationInput = layoutSelectInput((i) => i.presentation);
|
||||
const sidebarNavigationInput = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
|
||||
const actionbarInput = layoutSelectInput((i) => i.actionBar);
|
||||
const navbarInput = layoutSelectInput((i) => i.navBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const prevDeviceType = usePrevious(deviceType);
|
||||
|
||||
const throttledCalculatesLayout = _.throttle(() => calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_BROWSER_SIZE,
|
||||
@ -30,74 +51,54 @@ class PresentationFocusLayout extends Component {
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
return layoutContextState.input !== nextProps.layoutContextState.input
|
||||
|| layoutContextState.deviceType !== nextProps.layoutContextState.deviceType
|
||||
|| layoutContextState.isRTL !== nextProps.layoutContextState.isRTL
|
||||
|| layoutContextState.fontSize !== nextProps.layoutContextState.fontSize
|
||||
|| layoutContextState.fullscreen !== nextProps.layoutContextState.fullscreen;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (deviceType === null) return;
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType } = layoutContextState;
|
||||
if (prevProps.layoutContextState.deviceType !== deviceType) {
|
||||
this.init();
|
||||
if (deviceType !== prevDeviceType) {
|
||||
// reset layout if deviceType changed
|
||||
// not all options is supported in all devices
|
||||
init();
|
||||
} else {
|
||||
this.throttledCalculatesLayout();
|
||||
throttledCalculatesLayout();
|
||||
}
|
||||
}
|
||||
}, [input, deviceType, isRTL, fontSize, fullscreen]);
|
||||
|
||||
bannerAreaHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input } = layoutContextState;
|
||||
const { bannerBar, notificationsBar } = input;
|
||||
|
||||
const bannerHeight = bannerBar.hasBanner ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
const notificationHeight = notificationsBar.hasNotification ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
|
||||
return bannerHeight + notificationHeight;
|
||||
}
|
||||
|
||||
init() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
const init = () => {
|
||||
if (isMobile) {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
value: defaultsDeep({
|
||||
value: _.defaultsDeep({
|
||||
sidebarNavigation: {
|
||||
isOpen: false,
|
||||
sidebarNavPanel: input.sidebarNavigation.sidebarNavPanel,
|
||||
sidebarNavPanel: sidebarNavigationInput.sidebarNavPanel,
|
||||
},
|
||||
sidebarContent: {
|
||||
isOpen: false,
|
||||
sidebarContentPanel: input.sidebarContent.sidebarContentPanel,
|
||||
sidebarContentPanel: sidebarContentInput.sidebarContentPanel,
|
||||
},
|
||||
SidebarContentHorizontalResizer: {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
}, INITIAL_INPUT_STATE),
|
||||
});
|
||||
} else {
|
||||
const { sidebarContentPanel } = input.sidebarContent;
|
||||
const { sidebarContentPanel } = sidebarContentInput;
|
||||
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
value: defaultsDeep({
|
||||
value: _.defaultsDeep({
|
||||
sidebarNavigation: {
|
||||
isOpen: input.sidebarNavigation.isOpen || false,
|
||||
},
|
||||
@ -109,177 +110,23 @@ class PresentationFocusLayout extends Component {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
}, INITIAL_INPUT_STATE),
|
||||
});
|
||||
}
|
||||
this.throttledCalculatesLayout();
|
||||
}
|
||||
throttledCalculatesLayout();
|
||||
};
|
||||
|
||||
reset() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
calculatesNavbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { isRTL } = layoutContextState;
|
||||
|
||||
return {
|
||||
width: mediaAreaBounds.width,
|
||||
height: DEFAULT_VALUES.navBarHeight,
|
||||
top: DEFAULT_VALUES.navBarTop + this.bannerAreaHeight(),
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { fontSize } = layoutContextState;
|
||||
|
||||
const BASE_FONT_SIZE = 14; // 90% font size
|
||||
const BASE_HEIGHT = DEFAULT_VALUES.actionBarHeight;
|
||||
const PADDING = DEFAULT_VALUES.actionBarPadding;
|
||||
|
||||
const actionBarHeight = ((BASE_HEIGHT / BASE_FONT_SIZE) * fontSize);
|
||||
|
||||
return {
|
||||
height: actionBarHeight + (PADDING * 2),
|
||||
innerHeight: actionBarHeight,
|
||||
padding: PADDING,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input, isRTL } = layoutContextState;
|
||||
|
||||
const actionBarHeight = this.calculatesActionbarHeight();
|
||||
|
||||
return {
|
||||
display: input.actionBar.hasActionBar,
|
||||
width: mediaAreaBounds.width,
|
||||
height: actionBarHeight.height,
|
||||
innerHeight: actionBarHeight.innerHeight,
|
||||
padding: actionBarHeight.padding,
|
||||
top: windowHeight() - actionBarHeight.height,
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarNavMinWidth,
|
||||
sidebarNavMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarNavigation.width === 0) {
|
||||
width = min(max((windowWidth() * 0.2), sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
} else {
|
||||
width = min(max(input.sidebarNavigation.width, sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
}
|
||||
minWidth = sidebarNavMinWidth;
|
||||
maxWidth = sidebarNavMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
let sidebarNavHeight = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
sidebarNavHeight = windowHeight() - navBarHeight - this.bannerAreaHeight();
|
||||
} else {
|
||||
sidebarNavHeight = windowHeight() - this.bannerAreaHeight();
|
||||
}
|
||||
}
|
||||
return sidebarNavHeight;
|
||||
}
|
||||
|
||||
calculatesSidebarNavBounds() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { sidebarNavTop, navBarHeight, sidebarNavLeft } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
return {
|
||||
top,
|
||||
left: !isRTL ? sidebarNavLeft : null,
|
||||
right: isRTL ? sidebarNavLeft : null,
|
||||
zIndex: deviceType === DEVICE_TYPE.MOBILE ? 11 : 2,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarContentMinWidth,
|
||||
sidebarContentMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarContent.width === 0) {
|
||||
width = min(
|
||||
max((windowWidth() * 0.2), sidebarContentMinWidth), sidebarContentMaxWidth,
|
||||
);
|
||||
} else {
|
||||
width = min(max(input.sidebarContent.width, sidebarContentMinWidth),
|
||||
sidebarContentMaxWidth);
|
||||
}
|
||||
minWidth = sidebarContentMinWidth;
|
||||
maxWidth = sidebarContentMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const { presentation } = input;
|
||||
const { isOpen } = presentation;
|
||||
const calculatesSidebarContentHeight = () => {
|
||||
const { isOpen } = presentationInput;
|
||||
const {
|
||||
navBarHeight,
|
||||
sidebarContentMinHeight,
|
||||
@ -287,22 +134,22 @@ class PresentationFocusLayout extends Component {
|
||||
let height = 0;
|
||||
let minHeight = 0;
|
||||
let maxHeight = 0;
|
||||
if (input.sidebarContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
height = windowHeight() - navBarHeight - this.bannerAreaHeight();
|
||||
if (sidebarContentInput.isOpen) {
|
||||
if (isMobile) {
|
||||
height = windowHeight() - navBarHeight - bannerAreaHeight();
|
||||
minHeight = height;
|
||||
maxHeight = height;
|
||||
} else if (input.cameraDock.numCameras > 0 && isOpen) {
|
||||
if (input.sidebarContent.height === 0) {
|
||||
height = (windowHeight() * 0.75) - this.bannerAreaHeight();
|
||||
} else if (cameraDockInput.numCameras > 0 && isOpen) {
|
||||
if (sidebarContentInput.height === 0) {
|
||||
height = (windowHeight() * 0.75) - bannerAreaHeight();
|
||||
} else {
|
||||
height = min(max(input.sidebarContent.height, sidebarContentMinHeight),
|
||||
height = min(max(sidebarContentInput.height, sidebarContentMinHeight),
|
||||
windowHeight());
|
||||
}
|
||||
minHeight = windowHeight() * 0.25 - this.bannerAreaHeight();
|
||||
maxHeight = windowHeight() * 0.75 - this.bannerAreaHeight();
|
||||
minHeight = windowHeight() * 0.25 - bannerAreaHeight();
|
||||
maxHeight = windowHeight() * 0.75 - bannerAreaHeight();
|
||||
} else {
|
||||
height = windowHeight() - this.bannerAreaHeight();
|
||||
height = windowHeight() - bannerAreaHeight();
|
||||
minHeight = height;
|
||||
maxHeight = height;
|
||||
}
|
||||
@ -312,143 +159,68 @@ class PresentationFocusLayout extends Component {
|
||||
minHeight,
|
||||
maxHeight,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
calculatesSidebarContentBounds(sidebarNavWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { navBarHeight, sidebarNavTop } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
let left = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
let right = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
left = !isRTL ? left : null;
|
||||
right = isRTL ? right : null;
|
||||
|
||||
const zIndex = deviceType === DEVICE_TYPE.MOBILE ? 11 : 1;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
right,
|
||||
zIndex,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesMediaAreaBounds(sidebarNavWidth, sidebarContentWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
const { height: actionBarHeight } = this.calculatesActionbarHeight();
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
left = 0;
|
||||
width = windowWidth();
|
||||
} else {
|
||||
left = !isRTL ? sidebarNavWidth + sidebarContentWidth : 0;
|
||||
width = windowWidth() - sidebarNavWidth - sidebarContentWidth;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height: windowHeight() - (navBarHeight + actionBarHeight + this.bannerAreaHeight()),
|
||||
top: navBarHeight + this.bannerAreaHeight(),
|
||||
left,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesCameraDockBounds(
|
||||
const calculatesCameraDockBounds = (
|
||||
mediaBounds,
|
||||
mediaAreaBounds,
|
||||
sidebarNavWidth,
|
||||
sidebarContentWidth,
|
||||
sidebarContentHeight,
|
||||
) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
deviceType, input, fullscreen, isRTL,
|
||||
} = layoutContextState;
|
||||
const { presentation } = input;
|
||||
const { isOpen } = presentation;
|
||||
const cameraDockBounds = {};
|
||||
) => {
|
||||
const { baseCameraDockBounds } = props;
|
||||
const sidebarSize = sidebarNavWidth + sidebarContentWidth;
|
||||
|
||||
if (input.cameraDock.numCameras > 0) {
|
||||
if (fullscreen.group === 'webcams') {
|
||||
cameraDockBounds.width = windowWidth();
|
||||
cameraDockBounds.minWidth = windowWidth();
|
||||
cameraDockBounds.maxWidth = windowWidth();
|
||||
cameraDockBounds.height = windowHeight();
|
||||
cameraDockBounds.minHeight = windowHeight();
|
||||
cameraDockBounds.maxHeight = windowHeight();
|
||||
cameraDockBounds.top = 0;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.zIndex = 99;
|
||||
return cameraDockBounds;
|
||||
}
|
||||
const baseBounds = baseCameraDockBounds(mediaAreaBounds, sidebarSize);
|
||||
|
||||
if (!isOpen) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
|
||||
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
||||
} else {
|
||||
let cameraDockHeight = 0;
|
||||
// do not proceed if using values from LayoutEngine
|
||||
if (Object.keys(baseBounds).length > 0) {
|
||||
return baseBounds;
|
||||
}
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
cameraDockBounds.top = mediaAreaBounds.top + mediaBounds.height;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.minWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight;
|
||||
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height - mediaBounds.height;
|
||||
} else {
|
||||
if (input.cameraDock.height === 0) {
|
||||
cameraDockHeight = min(
|
||||
max((windowHeight() - sidebarContentHeight), DEFAULT_VALUES.cameraDockMinHeight),
|
||||
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight),
|
||||
);
|
||||
} else {
|
||||
cameraDockHeight = min(
|
||||
max(input.cameraDock.height, DEFAULT_VALUES.cameraDockMinHeight),
|
||||
(windowHeight() - DEFAULT_VALUES.cameraDockMinHeight),
|
||||
);
|
||||
}
|
||||
cameraDockBounds.top = windowHeight() - cameraDockHeight;
|
||||
cameraDockBounds.left = !isRTL ? sidebarNavWidth : 0;
|
||||
cameraDockBounds.right = isRTL ? sidebarNavWidth : 0;
|
||||
cameraDockBounds.minWidth = sidebarContentWidth;
|
||||
cameraDockBounds.width = sidebarContentWidth;
|
||||
cameraDockBounds.maxWidth = sidebarContentWidth;
|
||||
cameraDockBounds.minHeight = DEFAULT_VALUES.cameraDockMinHeight;
|
||||
cameraDockBounds.height = cameraDockHeight;
|
||||
cameraDockBounds.maxHeight = windowHeight() - sidebarContentHeight;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
}
|
||||
}
|
||||
const { cameraDockMinHeight } = DEFAULT_VALUES;
|
||||
|
||||
const cameraDockBounds = {};
|
||||
|
||||
let cameraDockHeight = 0;
|
||||
|
||||
if (isMobile) {
|
||||
cameraDockBounds.top = mediaAreaBounds.top + mediaBounds.height;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.minWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.minHeight = cameraDockMinHeight;
|
||||
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height - mediaBounds.height;
|
||||
} else {
|
||||
cameraDockBounds.width = 0;
|
||||
cameraDockBounds.height = 0;
|
||||
if (cameraDockInput.height === 0) {
|
||||
cameraDockHeight = min(
|
||||
max((windowHeight() - sidebarContentHeight), cameraDockMinHeight),
|
||||
(windowHeight() - cameraDockMinHeight),
|
||||
);
|
||||
} else {
|
||||
cameraDockHeight = min(
|
||||
max(cameraDockInput.height, cameraDockMinHeight),
|
||||
(windowHeight() - cameraDockMinHeight),
|
||||
);
|
||||
}
|
||||
cameraDockBounds.top = windowHeight() - cameraDockHeight;
|
||||
cameraDockBounds.left = !isRTL ? sidebarNavWidth : 0;
|
||||
cameraDockBounds.right = isRTL ? sidebarNavWidth : 0;
|
||||
cameraDockBounds.minWidth = sidebarContentWidth;
|
||||
cameraDockBounds.width = sidebarContentWidth;
|
||||
cameraDockBounds.maxWidth = sidebarContentWidth;
|
||||
cameraDockBounds.minHeight = cameraDockMinHeight;
|
||||
cameraDockBounds.height = cameraDockHeight;
|
||||
cameraDockBounds.maxHeight = windowHeight() - sidebarContentHeight;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
}
|
||||
return cameraDockBounds;
|
||||
}
|
||||
};
|
||||
|
||||
calculatesMediaBounds(mediaAreaBounds, sidebarSize) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
deviceType, input, fullscreen, isRTL,
|
||||
} = layoutContextState;
|
||||
const calculatesMediaBounds = (mediaAreaBounds, sidebarSize) => {
|
||||
const mediaBounds = {};
|
||||
const { element: fullscreenElement } = fullscreen;
|
||||
|
||||
@ -462,43 +234,48 @@ class PresentationFocusLayout extends Component {
|
||||
return mediaBounds;
|
||||
}
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE && input.cameraDock.numCameras > 0) {
|
||||
if (isMobile && cameraDockInput.numCameras > 0) {
|
||||
mediaBounds.height = mediaAreaBounds.height * 0.7;
|
||||
} else {
|
||||
mediaBounds.height = mediaAreaBounds.height;
|
||||
}
|
||||
mediaBounds.width = mediaAreaBounds.width;
|
||||
mediaBounds.top = DEFAULT_VALUES.navBarHeight + this.bannerAreaHeight();
|
||||
mediaBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight();
|
||||
mediaBounds.left = !isRTL ? mediaAreaBounds.left : null;
|
||||
mediaBounds.right = isRTL ? sidebarSize : null;
|
||||
mediaBounds.zIndex = 1;
|
||||
|
||||
return mediaBounds;
|
||||
}
|
||||
};
|
||||
|
||||
calculatesLayout() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { deviceType, input, isRTL } = layoutContextState;
|
||||
const calculatesLayout = () => {
|
||||
const {
|
||||
calculatesNavbarBounds,
|
||||
calculatesActionbarBounds,
|
||||
calculatesSidebarNavWidth,
|
||||
calculatesSidebarNavHeight,
|
||||
calculatesSidebarNavBounds,
|
||||
calculatesSidebarContentWidth,
|
||||
calculatesSidebarContentBounds,
|
||||
calculatesMediaAreaBounds,
|
||||
isTablet,
|
||||
} = props;
|
||||
const { captionsMargin } = DEFAULT_VALUES;
|
||||
|
||||
const sidebarNavWidth = this.calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = this.calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = this.calculatesSidebarContentWidth();
|
||||
const sidebarNavBounds = this.calculatesSidebarNavBounds(
|
||||
const sidebarNavWidth = calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = calculatesSidebarContentWidth();
|
||||
const sidebarNavBounds = calculatesSidebarNavBounds();
|
||||
const sidebarContentBounds = calculatesSidebarContentBounds(sidebarNavWidth.width);
|
||||
const mediaAreaBounds = calculatesMediaAreaBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const sidebarContentBounds = this.calculatesSidebarContentBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const mediaAreaBounds = this.calculatesMediaAreaBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const navbarBounds = this.calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = this.calculatesActionbarBounds(mediaAreaBounds);
|
||||
const navbarBounds = calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = calculatesActionbarBounds(mediaAreaBounds);
|
||||
const sidebarSize = sidebarContentWidth.width + sidebarNavWidth.width;
|
||||
const mediaBounds = this.calculatesMediaBounds(mediaAreaBounds, sidebarSize);
|
||||
const sidebarContentHeight = this.calculatesSidebarContentHeight();
|
||||
const cameraDockBounds = this.calculatesCameraDockBounds(
|
||||
const mediaBounds = calculatesMediaBounds(mediaAreaBounds, sidebarSize);
|
||||
const sidebarContentHeight = calculatesSidebarContentHeight();
|
||||
const cameraDockBounds = calculatesCameraDockBounds(
|
||||
mediaBounds,
|
||||
mediaAreaBounds,
|
||||
sidebarNavWidth.width,
|
||||
@ -509,7 +286,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_NAVBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.navBar.hasNavBar,
|
||||
display: navbarInput.hasNavBar,
|
||||
width: navbarBounds.width,
|
||||
height: navbarBounds.height,
|
||||
top: navbarBounds.top,
|
||||
@ -522,7 +299,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_ACTIONBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.actionBar.hasActionBar,
|
||||
display: actionbarInput.hasActionBar,
|
||||
width: actionbarBounds.width,
|
||||
height: actionbarBounds.height,
|
||||
innerHeight: actionbarBounds.innerHeight,
|
||||
@ -546,7 +323,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_NAVIGATION_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarNavigation.isOpen,
|
||||
display: sidebarNavigationInput.isOpen,
|
||||
minWidth: sidebarNavWidth.minWidth,
|
||||
width: sidebarNavWidth.width,
|
||||
maxWidth: sidebarNavWidth.maxWidth,
|
||||
@ -555,8 +332,7 @@ class PresentationFocusLayout extends Component {
|
||||
left: sidebarNavBounds.left,
|
||||
right: sidebarNavBounds.right,
|
||||
tabOrder: DEFAULT_VALUES.sidebarNavTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarNavBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -574,7 +350,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarContent.isOpen,
|
||||
display: sidebarContentInput.isOpen,
|
||||
minWidth: sidebarContentWidth.minWidth,
|
||||
width: sidebarContentWidth.width,
|
||||
maxWidth: sidebarContentWidth.maxWidth,
|
||||
@ -584,10 +360,9 @@ class PresentationFocusLayout extends Component {
|
||||
top: sidebarContentBounds.top,
|
||||
left: sidebarContentBounds.left,
|
||||
right: sidebarContentBounds.right,
|
||||
currentPanelType: input.currentPanelType,
|
||||
currentPanelType,
|
||||
tabOrder: DEFAULT_VALUES.sidebarContentTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarContentBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -597,7 +372,7 @@ class PresentationFocusLayout extends Component {
|
||||
value: {
|
||||
top: false,
|
||||
right: !isRTL,
|
||||
bottom: input.cameraDock.numCameras > 0,
|
||||
bottom: cameraDockInput.numCameras > 0,
|
||||
left: isRTL,
|
||||
},
|
||||
});
|
||||
@ -613,7 +388,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_CAMERA_DOCK_OUTPUT,
|
||||
value: {
|
||||
display: input.cameraDock.numCameras > 0,
|
||||
display: cameraDockInput.numCameras > 0,
|
||||
minWidth: cameraDockBounds.minWidth,
|
||||
width: cameraDockBounds.width,
|
||||
maxWidth: cameraDockBounds.maxWidth,
|
||||
@ -638,7 +413,7 @@ class PresentationFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_PRESENTATION_OUTPUT,
|
||||
value: {
|
||||
display: input.presentation.isOpen,
|
||||
display: presentationInput.isOpen,
|
||||
width: mediaBounds.width,
|
||||
height: mediaBounds.height,
|
||||
top: mediaBounds.top,
|
||||
@ -672,13 +447,9 @@ class PresentationFocusLayout extends Component {
|
||||
right: mediaBounds.right,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default LayoutContextFunc.withConsumer(PresentationFocusLayout);
|
||||
export default PresentationFocusLayout;
|
||||
|
@ -1,28 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { LayoutContextFunc } from '/imports/ui/components/layout/context';
|
||||
import { layoutDispatch, layoutSelect, layoutSelectInput } from '/imports/ui/components/layout/context';
|
||||
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
||||
import { INITIAL_INPUT_STATE } from '/imports/ui/components/layout/initState';
|
||||
import {
|
||||
DEVICE_TYPE, ACTIONS, PANELS, CAMERADOCK_POSITION,
|
||||
} from '/imports/ui/components/layout/enums';
|
||||
import { ACTIONS, PANELS, CAMERADOCK_POSITION } from '/imports/ui/components/layout/enums';
|
||||
|
||||
const windowWidth = () => window.document.documentElement.clientWidth;
|
||||
const windowHeight = () => window.document.documentElement.clientHeight;
|
||||
const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
|
||||
const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
|
||||
|
||||
class SmartLayout extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const SmartLayout = (props) => {
|
||||
const { bannerAreaHeight, isMobile } = props;
|
||||
|
||||
this.throttledCalculatesLayout = _.throttle(() => this.calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.init();
|
||||
const { layoutContextDispatch } = this.props;
|
||||
const input = layoutSelect((i) => i.input);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const fontSize = layoutSelect((i) => i.fontSize);
|
||||
const currentPanelType = layoutSelect((i) => i.currentPanelType);
|
||||
|
||||
const presentationInput = layoutSelectInput((i) => i.presentation);
|
||||
const sidebarNavigationInput = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
|
||||
const actionbarInput = layoutSelectInput((i) => i.actionBar);
|
||||
const navbarInput = layoutSelectInput((i) => i.navBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const prevDeviceType = usePrevious(deviceType);
|
||||
|
||||
const throttledCalculatesLayout = _.throttle(() => calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_BROWSER_SIZE,
|
||||
@ -32,70 +49,50 @@ class SmartLayout extends Component {
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
return layoutContextState.input !== nextProps.layoutContextState.input
|
||||
|| layoutContextState.deviceType !== nextProps.layoutContextState.deviceType
|
||||
|| layoutContextState.isRTL !== nextProps.layoutContextState.isRTL
|
||||
|| layoutContextState.fontSize !== nextProps.layoutContextState.fontSize
|
||||
|| layoutContextState.fullscreen !== nextProps.layoutContextState.fullscreen;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (deviceType === null) return;
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType } = layoutContextState;
|
||||
if (prevProps.layoutContextState.deviceType !== deviceType) {
|
||||
this.init();
|
||||
if (deviceType !== prevDeviceType) {
|
||||
// reset layout if deviceType changed
|
||||
// not all options is supported in all devices
|
||||
init();
|
||||
} else {
|
||||
this.throttledCalculatesLayout();
|
||||
throttledCalculatesLayout();
|
||||
}
|
||||
}
|
||||
}, [input, deviceType, isRTL, fontSize, fullscreen]);
|
||||
|
||||
bannerAreaHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input } = layoutContextState;
|
||||
const { bannerBar, notificationsBar } = input;
|
||||
|
||||
const bannerHeight = bannerBar.hasBanner ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
const notificationHeight = notificationsBar.hasNotification ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
|
||||
return bannerHeight + notificationHeight;
|
||||
}
|
||||
|
||||
init() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
const init = () => {
|
||||
if (isMobile) {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
value: _.defaultsDeep({
|
||||
sidebarNavigation: {
|
||||
isOpen: false,
|
||||
sidebarNavPanel: input.sidebarNavigation.sidebarNavPanel,
|
||||
sidebarNavPanel: sidebarNavigationInput.sidebarNavPanel,
|
||||
},
|
||||
sidebarContent: {
|
||||
isOpen: false,
|
||||
sidebarContentPanel: input.sidebarContent.sidebarContentPanel,
|
||||
sidebarContentPanel: sidebarContentInput.sidebarContentPanel,
|
||||
},
|
||||
SidebarContentHorizontalResizer: {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
}, INITIAL_INPUT_STATE),
|
||||
});
|
||||
} else {
|
||||
const { sidebarContentPanel } = input.sidebarContent;
|
||||
const { sidebarContentPanel } = sidebarContentInput;
|
||||
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
@ -111,310 +108,89 @@ class SmartLayout extends Component {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
}, INITIAL_INPUT_STATE),
|
||||
});
|
||||
}
|
||||
this.throttledCalculatesLayout();
|
||||
}
|
||||
throttledCalculatesLayout();
|
||||
};
|
||||
|
||||
reset() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
calculatesNavbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { isRTL } = layoutContextState;
|
||||
|
||||
return {
|
||||
width: mediaAreaBounds.width,
|
||||
height: DEFAULT_VALUES.navBarHeight,
|
||||
top: DEFAULT_VALUES.navBarTop + this.bannerAreaHeight(),
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { fontSize } = layoutContextState;
|
||||
|
||||
const BASE_FONT_SIZE = 14; // 90% font size
|
||||
const BASE_HEIGHT = DEFAULT_VALUES.actionBarHeight;
|
||||
const PADDING = DEFAULT_VALUES.actionBarPadding;
|
||||
|
||||
const actionBarHeight = ((BASE_HEIGHT / BASE_FONT_SIZE) * fontSize);
|
||||
|
||||
return {
|
||||
height: actionBarHeight + (PADDING * 2),
|
||||
innerHeight: actionBarHeight,
|
||||
padding: PADDING,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input, isRTL } = layoutContextState;
|
||||
|
||||
const actionBarHeight = this.calculatesActionbarHeight();
|
||||
|
||||
return {
|
||||
display: input.actionBar.hasActionBar,
|
||||
width: mediaAreaBounds.width,
|
||||
height: actionBarHeight.height,
|
||||
innerHeight: actionBarHeight.innerHeight,
|
||||
padding: actionBarHeight.padding,
|
||||
top: windowHeight() - actionBarHeight.height,
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarNavMinWidth,
|
||||
sidebarNavMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarNavigation.width === 0) {
|
||||
width = min(max((windowWidth() * 0.2), sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
} else {
|
||||
width = min(max(input.sidebarNavigation.width, sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
}
|
||||
minWidth = sidebarNavMinWidth;
|
||||
maxWidth = sidebarNavMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
let sidebarNavHeight = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
sidebarNavHeight = windowHeight() - DEFAULT_VALUES.navBarHeight;
|
||||
} else {
|
||||
sidebarNavHeight = windowHeight();
|
||||
}
|
||||
sidebarNavHeight -= this.bannerAreaHeight();
|
||||
}
|
||||
return sidebarNavHeight;
|
||||
}
|
||||
|
||||
calculatesSidebarNavBounds() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { sidebarNavTop, navBarHeight, sidebarNavLeft } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
return {
|
||||
top,
|
||||
left: !isRTL ? sidebarNavLeft : null,
|
||||
right: isRTL ? sidebarNavLeft : null,
|
||||
zIndex: deviceType === DEVICE_TYPE.MOBILE ? 11 : 2,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarContentMinWidth,
|
||||
sidebarContentMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarContent.width === 0) {
|
||||
width = min(
|
||||
max((windowWidth() * 0.2), sidebarContentMinWidth), sidebarContentMaxWidth,
|
||||
);
|
||||
} else {
|
||||
width = min(max(input.sidebarContent.width, sidebarContentMinWidth),
|
||||
sidebarContentMaxWidth);
|
||||
}
|
||||
minWidth = sidebarContentMinWidth;
|
||||
maxWidth = sidebarContentMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const calculatesSidebarContentHeight = () => {
|
||||
let sidebarContentHeight = 0;
|
||||
if (input.sidebarContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
if (sidebarContentInput.isOpen) {
|
||||
if (isMobile) {
|
||||
sidebarContentHeight = windowHeight() - DEFAULT_VALUES.navBarHeight;
|
||||
} else {
|
||||
sidebarContentHeight = windowHeight();
|
||||
}
|
||||
sidebarContentHeight -= this.bannerAreaHeight();
|
||||
sidebarContentHeight -= bannerAreaHeight();
|
||||
}
|
||||
return sidebarContentHeight;
|
||||
}
|
||||
};
|
||||
|
||||
calculatesSidebarContentBounds(sidebarNavWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { sidebarNavTop, navBarHeight } = DEFAULT_VALUES;
|
||||
const calculatesCameraDockBounds = (mediaAreaBounds, mediaBounds, sidebarSize) => {
|
||||
const { baseCameraDockBounds } = props;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
const baseBounds = baseCameraDockBounds(mediaAreaBounds, sidebarSize);
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
let left = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
left = !isRTL ? left : null;
|
||||
|
||||
let right = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
right = isRTL ? right : null;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
right,
|
||||
zIndex: deviceType === DEVICE_TYPE.MOBILE ? 11 : 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesMediaAreaBounds(sidebarNavWidth, sidebarContentWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
const { height: actionBarHeight } = this.calculatesActionbarHeight();
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
left = 0;
|
||||
width = windowWidth();
|
||||
} else {
|
||||
left = !isRTL ? sidebarNavWidth + sidebarContentWidth : 0;
|
||||
width = windowWidth() - sidebarNavWidth - sidebarContentWidth;
|
||||
// do not proceed if using values from LayoutEngine
|
||||
if (Object.keys(baseBounds).length > 0) {
|
||||
baseBounds.isCameraHorizontal = false;
|
||||
return baseBounds;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height: windowHeight() - (navBarHeight + actionBarHeight + this.bannerAreaHeight()),
|
||||
top: navBarHeight + this.bannerAreaHeight(),
|
||||
left,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesCameraDockBounds(mediaAreaBounds, mediaBounds, sidebarSize) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
input, fullscreen, isRTL, deviceType,
|
||||
} = layoutContextState;
|
||||
const { presentation } = input;
|
||||
const { isOpen } = presentation;
|
||||
const { camerasMargin, presentationToolbarMinWidth } = DEFAULT_VALUES;
|
||||
|
||||
const cameraDockBounds = {};
|
||||
|
||||
cameraDockBounds.isCameraHorizontal = false;
|
||||
const mediaBoundsWidth = (mediaBounds.width > presentationToolbarMinWidth
|
||||
&& deviceType !== DEVICE_TYPE.MOBILE)
|
||||
|
||||
const mediaBoundsWidth = (mediaBounds.width > presentationToolbarMinWidth && !isMobile)
|
||||
? mediaBounds.width
|
||||
: presentationToolbarMinWidth;
|
||||
|
||||
if (input.cameraDock.numCameras > 0) {
|
||||
cameraDockBounds.top = mediaAreaBounds.top;
|
||||
cameraDockBounds.left = mediaAreaBounds.left;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize + (camerasMargin * 2) : null;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
cameraDockBounds.top = mediaAreaBounds.top;
|
||||
cameraDockBounds.left = mediaAreaBounds.left;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize + (camerasMargin * 2) : null;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
|
||||
if (!isOpen) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.position = CAMERADOCK_POSITION.CONTENT_TOP;
|
||||
} else if (mediaBounds.width < mediaAreaBounds.width) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width - mediaBoundsWidth;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width * 0.8;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.left += camerasMargin;
|
||||
cameraDockBounds.width -= (camerasMargin * 2);
|
||||
cameraDockBounds.isCameraHorizontal = true;
|
||||
cameraDockBounds.position = CAMERADOCK_POSITION.CONTENT_LEFT;
|
||||
// button size in vertical position
|
||||
cameraDockBounds.height -= 20;
|
||||
} else {
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.8;
|
||||
cameraDockBounds.top += camerasMargin;
|
||||
cameraDockBounds.height -= (camerasMargin * 2);
|
||||
cameraDockBounds.position = CAMERADOCK_POSITION.CONTENT_TOP;
|
||||
}
|
||||
|
||||
cameraDockBounds.minWidth = cameraDockBounds.width;
|
||||
cameraDockBounds.minHeight = cameraDockBounds.height;
|
||||
|
||||
if (fullscreen.group === 'webcams') {
|
||||
cameraDockBounds.width = windowWidth();
|
||||
cameraDockBounds.minWidth = windowWidth();
|
||||
cameraDockBounds.maxWidth = windowWidth();
|
||||
cameraDockBounds.height = windowHeight();
|
||||
cameraDockBounds.minHeight = windowHeight();
|
||||
cameraDockBounds.maxHeight = windowHeight();
|
||||
cameraDockBounds.top = 0;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.zIndex = 99;
|
||||
}
|
||||
if (mediaBounds.width < mediaAreaBounds.width) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width - mediaBoundsWidth;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width * 0.8;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.left += camerasMargin;
|
||||
cameraDockBounds.width -= (camerasMargin * 2);
|
||||
cameraDockBounds.isCameraHorizontal = true;
|
||||
cameraDockBounds.position = CAMERADOCK_POSITION.CONTENT_LEFT;
|
||||
// button size in vertical position
|
||||
cameraDockBounds.height -= 20;
|
||||
} else {
|
||||
cameraDockBounds.width = 0;
|
||||
cameraDockBounds.height = 0;
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height - mediaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.8;
|
||||
cameraDockBounds.top += camerasMargin;
|
||||
cameraDockBounds.height -= (camerasMargin * 2);
|
||||
cameraDockBounds.position = CAMERADOCK_POSITION.CONTENT_TOP;
|
||||
}
|
||||
|
||||
return cameraDockBounds;
|
||||
}
|
||||
cameraDockBounds.minWidth = cameraDockBounds.width;
|
||||
cameraDockBounds.minHeight = cameraDockBounds.height;
|
||||
|
||||
calculatesSlideSize(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input } = layoutContextState;
|
||||
const { presentation } = input;
|
||||
const { currentSlide } = presentation;
|
||||
return cameraDockBounds;
|
||||
};
|
||||
|
||||
const calculatesSlideSize = (mediaAreaBounds) => {
|
||||
const { currentSlide } = presentationInput;
|
||||
|
||||
if (currentSlide.size.width === 0 && currentSlide.size.height === 0) {
|
||||
return {
|
||||
@ -438,15 +214,10 @@ class SmartLayout extends Component {
|
||||
width: slideWidth,
|
||||
height: slideHeight,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
calculatesMediaBounds(mediaAreaBounds, slideSize, sidebarSize) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
input, fullscreen, isRTL, deviceType,
|
||||
} = layoutContextState;
|
||||
const { presentation } = input;
|
||||
const { isOpen } = presentation;
|
||||
const calculatesMediaBounds = (mediaAreaBounds, slideSize, sidebarSize) => {
|
||||
const { isOpen } = presentationInput;
|
||||
const mediaBounds = {};
|
||||
const { element: fullscreenElement } = fullscreen;
|
||||
|
||||
@ -470,9 +241,9 @@ class SmartLayout extends Component {
|
||||
return mediaBounds;
|
||||
}
|
||||
|
||||
if (input.cameraDock.numCameras > 0 && !input.cameraDock.isDragging) {
|
||||
if (cameraDockInput.numCameras > 0 && !cameraDockInput.isDragging) {
|
||||
if (slideSize.width !== 0 && slideSize.height !== 0) {
|
||||
if (slideSize.width < mediaAreaBounds.width && deviceType !== DEVICE_TYPE.MOBILE) {
|
||||
if (slideSize.width < mediaAreaBounds.width && !isMobile) {
|
||||
if (slideSize.width < (mediaAreaBounds.width * 0.8)) {
|
||||
mediaBounds.width = slideSize.width;
|
||||
} else {
|
||||
@ -517,30 +288,35 @@ class SmartLayout extends Component {
|
||||
mediaBounds.zIndex = 1;
|
||||
|
||||
return mediaBounds;
|
||||
}
|
||||
};
|
||||
|
||||
calculatesLayout() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { deviceType, input, isRTL } = layoutContextState;
|
||||
const calculatesLayout = () => {
|
||||
const {
|
||||
calculatesNavbarBounds,
|
||||
calculatesActionbarBounds,
|
||||
calculatesSidebarNavWidth,
|
||||
calculatesSidebarNavHeight,
|
||||
calculatesSidebarNavBounds,
|
||||
calculatesSidebarContentWidth,
|
||||
calculatesSidebarContentBounds,
|
||||
calculatesMediaAreaBounds,
|
||||
isTablet,
|
||||
} = props;
|
||||
const { camerasMargin, captionsMargin } = DEFAULT_VALUES;
|
||||
|
||||
const sidebarNavWidth = this.calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = this.calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = this.calculatesSidebarContentWidth();
|
||||
const sidebarContentHeight = this.calculatesSidebarContentHeight();
|
||||
const sidebarNavBounds = this
|
||||
.calculatesSidebarNavBounds(sidebarNavWidth.width, sidebarContentWidth.width);
|
||||
const sidebarContentBounds = this
|
||||
.calculatesSidebarContentBounds(sidebarNavWidth.width, sidebarContentWidth.width);
|
||||
const mediaAreaBounds = this
|
||||
.calculatesMediaAreaBounds(sidebarNavWidth.width, sidebarContentWidth.width);
|
||||
const navbarBounds = this.calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = this.calculatesActionbarBounds(mediaAreaBounds);
|
||||
const slideSize = this.calculatesSlideSize(mediaAreaBounds);
|
||||
const sidebarNavWidth = calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = calculatesSidebarContentWidth();
|
||||
const sidebarContentHeight = calculatesSidebarContentHeight();
|
||||
const sidebarNavBounds = calculatesSidebarNavBounds();
|
||||
const sidebarContentBounds = calculatesSidebarContentBounds(sidebarNavWidth.width);
|
||||
const mediaAreaBounds = calculatesMediaAreaBounds(sidebarNavWidth.width, sidebarContentWidth.width);
|
||||
const navbarBounds = calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = calculatesActionbarBounds(mediaAreaBounds);
|
||||
const slideSize = calculatesSlideSize(mediaAreaBounds);
|
||||
const sidebarSize = sidebarContentWidth.width + sidebarNavWidth.width;
|
||||
const mediaBounds = this.calculatesMediaBounds(mediaAreaBounds, slideSize, sidebarSize);
|
||||
const cameraDockBounds = this
|
||||
.calculatesCameraDockBounds(mediaAreaBounds, mediaBounds, sidebarSize);
|
||||
const mediaBounds = calculatesMediaBounds(mediaAreaBounds, slideSize, sidebarSize);
|
||||
const cameraDockBounds = calculatesCameraDockBounds(mediaAreaBounds, mediaBounds, sidebarSize);
|
||||
const horizontalCameraDiff = cameraDockBounds.isCameraHorizontal
|
||||
? cameraDockBounds.width + (camerasMargin * 2)
|
||||
: 0;
|
||||
@ -548,7 +324,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_NAVBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.navBar.hasNavBar,
|
||||
display: navbarInput.hasNavBar,
|
||||
width: navbarBounds.width,
|
||||
height: navbarBounds.height,
|
||||
top: navbarBounds.top,
|
||||
@ -561,7 +337,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_ACTIONBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.actionBar.hasActionBar,
|
||||
display: actionbarInput.hasActionBar,
|
||||
width: actionbarBounds.width,
|
||||
height: actionbarBounds.height,
|
||||
innerHeight: actionbarBounds.innerHeight,
|
||||
@ -585,7 +361,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_NAVIGATION_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarNavigation.isOpen,
|
||||
display: sidebarNavigationInput.isOpen,
|
||||
minWidth: sidebarNavWidth.minWidth,
|
||||
width: sidebarNavWidth.width,
|
||||
maxWidth: sidebarNavWidth.maxWidth,
|
||||
@ -594,8 +370,7 @@ class SmartLayout extends Component {
|
||||
left: sidebarNavBounds.left,
|
||||
right: sidebarNavBounds.right,
|
||||
tabOrder: DEFAULT_VALUES.sidebarNavTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarNavBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -613,7 +388,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarContent.isOpen,
|
||||
display: sidebarContentInput.isOpen,
|
||||
minWidth: sidebarContentWidth.minWidth,
|
||||
width: sidebarContentWidth.width,
|
||||
maxWidth: sidebarContentWidth.maxWidth,
|
||||
@ -621,10 +396,9 @@ class SmartLayout extends Component {
|
||||
top: sidebarContentBounds.top,
|
||||
left: sidebarContentBounds.left,
|
||||
right: sidebarContentBounds.right,
|
||||
currentPanelType: input.currentPanelType,
|
||||
currentPanelType,
|
||||
tabOrder: DEFAULT_VALUES.sidebarContentTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarContentBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -650,7 +424,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_CAMERA_DOCK_OUTPUT,
|
||||
value: {
|
||||
display: input.cameraDock.numCameras > 0,
|
||||
display: cameraDockInput.numCameras > 0,
|
||||
position: cameraDockBounds.position,
|
||||
minWidth: cameraDockBounds.minWidth,
|
||||
width: cameraDockBounds.width,
|
||||
@ -676,7 +450,7 @@ class SmartLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_PRESENTATION_OUTPUT,
|
||||
value: {
|
||||
display: input.presentation.isOpen,
|
||||
display: presentationInput.isOpen,
|
||||
width: mediaBounds.width,
|
||||
height: mediaBounds.height,
|
||||
top: mediaBounds.top,
|
||||
@ -710,13 +484,9 @@ class SmartLayout extends Component {
|
||||
right: isRTL ? (mediaBounds.right + horizontalCameraDiff) : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default LayoutContextFunc.withConsumer(SmartLayout);
|
||||
export default SmartLayout;
|
||||
|
@ -1,26 +1,52 @@
|
||||
import React, { Component } from 'react';
|
||||
import { throttle, defaultsDeep } from 'lodash';
|
||||
import { LayoutContextFunc } from '/imports/ui/components/layout/context';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
layoutDispatch,
|
||||
layoutSelect,
|
||||
layoutSelectInput,
|
||||
layoutSelectOutput
|
||||
} from '/imports/ui/components/layout/context';
|
||||
import DEFAULT_VALUES from '/imports/ui/components/layout/defaultValues';
|
||||
import { INITIAL_INPUT_STATE } from '/imports/ui/components/layout/initState';
|
||||
import { DEVICE_TYPE, ACTIONS, PANELS } from '/imports/ui/components/layout/enums';
|
||||
import { ACTIONS, PANELS } from '/imports/ui/components/layout/enums';
|
||||
|
||||
const windowWidth = () => window.document.documentElement.clientWidth;
|
||||
const windowHeight = () => window.document.documentElement.clientHeight;
|
||||
const min = (value1, value2) => (value1 <= value2 ? value1 : value2);
|
||||
const max = (value1, value2) => (value1 >= value2 ? value1 : value2);
|
||||
|
||||
class VideoFocusLayout extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const VideoFocusLayout = (props) => {
|
||||
const { bannerAreaHeight, isMobile } = props;
|
||||
|
||||
this.throttledCalculatesLayout = throttle(() => this.calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.init();
|
||||
const { layoutContextDispatch } = this.props;
|
||||
const input = layoutSelect((i) => i.input);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
const isRTL = layoutSelect((i) => i.isRTL);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const fontSize = layoutSelect((i) => i.fontSize);
|
||||
const currentPanelType = layoutSelect((i) => i.currentPanelType);
|
||||
|
||||
const presentationInput = layoutSelectInput((i) => i.presentation);
|
||||
const sidebarNavigationInput = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
const cameraDockInput = layoutSelectInput((i) => i.cameraDock);
|
||||
const actionbarInput = layoutSelectInput((i) => i.actionBar);
|
||||
const navbarInput = layoutSelectInput((i) => i.navBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const sidebarContentOutput = layoutSelectOutput((i) => i.sidebarContent);
|
||||
|
||||
const prevDeviceType = usePrevious(deviceType);
|
||||
|
||||
const throttledCalculatesLayout = _.throttle(() => calculatesLayout(),
|
||||
50, { trailing: true, leading: true });
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_BROWSER_SIZE,
|
||||
@ -30,78 +56,57 @@ class VideoFocusLayout extends Component {
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
return layoutContextState.input !== nextProps.layoutContextState.input
|
||||
|| layoutContextState.deviceType !== nextProps.layoutContextState.deviceType
|
||||
|| layoutContextState.isRTL !== nextProps.layoutContextState.isRTL
|
||||
|| layoutContextState.fontSize !== nextProps.layoutContextState.fontSize
|
||||
|| layoutContextState.fullscreen !== nextProps.layoutContextState.fullscreen;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (deviceType === null) return;
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType } = layoutContextState;
|
||||
if (prevProps.layoutContextState.deviceType !== deviceType) {
|
||||
this.init();
|
||||
if (deviceType !== prevDeviceType) {
|
||||
// reset layout if deviceType changed
|
||||
// not all options is supported in all devices
|
||||
init();
|
||||
} else {
|
||||
this.throttledCalculatesLayout();
|
||||
throttledCalculatesLayout();
|
||||
}
|
||||
}
|
||||
}, [input, deviceType, isRTL, fontSize, fullscreen]);
|
||||
|
||||
bannerAreaHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input } = layoutContextState;
|
||||
const { bannerBar, notificationsBar } = input;
|
||||
|
||||
const bannerHeight = bannerBar.hasBanner ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
const notificationHeight = notificationsBar.hasNotification ? DEFAULT_VALUES.bannerHeight : 0;
|
||||
|
||||
return bannerHeight + notificationHeight;
|
||||
}
|
||||
|
||||
init() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { input } = layoutContextState;
|
||||
const { deviceType } = layoutContextState;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
const init = () => {
|
||||
if (isMobile) {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
value: defaultsDeep(
|
||||
value: _.defaultsDeep(
|
||||
{
|
||||
sidebarNavigation: {
|
||||
isOpen: false,
|
||||
sidebarNavPanel: input.sidebarNavigation.sidebarNavPanel,
|
||||
sidebarNavPanel: sidebarNavigationInput.sidebarNavPanel,
|
||||
},
|
||||
sidebarContent: {
|
||||
isOpen: false,
|
||||
sidebarContentPanel: input.sidebarContent.sidebarContentPanel,
|
||||
sidebarContentPanel: sidebarContentInput.sidebarContentPanel,
|
||||
},
|
||||
SidebarContentHorizontalResizer: {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
},
|
||||
INITIAL_INPUT_STATE,
|
||||
),
|
||||
});
|
||||
} else {
|
||||
const { sidebarContentPanel } = input.sidebarContent;
|
||||
const { sidebarContentPanel } = sidebarContentInput;
|
||||
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_LAYOUT_INPUT,
|
||||
value: defaultsDeep(
|
||||
value: _.defaultsDeep(
|
||||
{
|
||||
sidebarNavigation: {
|
||||
isOpen: input.sidebarNavigation.isOpen || false,
|
||||
@ -114,207 +119,52 @@ class VideoFocusLayout extends Component {
|
||||
isOpen: false,
|
||||
},
|
||||
presentation: {
|
||||
isOpen: input.presentation.isOpen,
|
||||
slidesLength: input.presentation.slidesLength,
|
||||
isOpen: presentationInput.isOpen,
|
||||
slidesLength: presentationInput.slidesLength,
|
||||
currentSlide: {
|
||||
...input.presentation.currentSlide,
|
||||
...presentationInput.currentSlide,
|
||||
},
|
||||
},
|
||||
cameraDock: {
|
||||
numCameras: input.cameraDock.numCameras,
|
||||
numCameras: cameraDockInput.numCameras,
|
||||
},
|
||||
},
|
||||
INITIAL_INPUT_STATE,
|
||||
),
|
||||
});
|
||||
}
|
||||
this.throttledCalculatesLayout();
|
||||
}
|
||||
throttledCalculatesLayout();
|
||||
};
|
||||
|
||||
reset() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
calculatesNavbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { isRTL } = layoutContextState;
|
||||
|
||||
return {
|
||||
width: mediaAreaBounds.width,
|
||||
height: DEFAULT_VALUES.navBarHeight,
|
||||
top: DEFAULT_VALUES.navBarTop + this.bannerAreaHeight(),
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { fontSize } = layoutContextState;
|
||||
|
||||
const BASE_FONT_SIZE = 14; // 90% font size
|
||||
const BASE_HEIGHT = DEFAULT_VALUES.actionBarHeight;
|
||||
const PADDING = DEFAULT_VALUES.actionBarPadding;
|
||||
|
||||
const actionBarHeight = ((BASE_HEIGHT / BASE_FONT_SIZE) * fontSize);
|
||||
|
||||
return {
|
||||
height: actionBarHeight + (PADDING * 2),
|
||||
innerHeight: actionBarHeight,
|
||||
padding: PADDING,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesActionbarBounds(mediaAreaBounds) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { input, isRTL } = layoutContextState;
|
||||
|
||||
const actionBarHeight = this.calculatesActionbarHeight();
|
||||
|
||||
return {
|
||||
display: input.actionBar.hasActionBar,
|
||||
width: mediaAreaBounds.width,
|
||||
height: actionBarHeight.height,
|
||||
innerHeight: actionBarHeight.innerHeight,
|
||||
padding: actionBarHeight.padding,
|
||||
top: windowHeight() - actionBarHeight.height,
|
||||
left: !isRTL ? mediaAreaBounds.left : 0,
|
||||
zIndex: 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarNavMinWidth,
|
||||
sidebarNavMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarNavigation.width === 0) {
|
||||
width = min(max((windowWidth() * 0.2), sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
} else {
|
||||
width = min(max(input.sidebarNavigation.width, sidebarNavMinWidth), sidebarNavMaxWidth);
|
||||
}
|
||||
minWidth = sidebarNavMinWidth;
|
||||
maxWidth = sidebarNavMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarNavHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
let sidebarNavHeight = 0;
|
||||
if (input.sidebarNavigation.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
sidebarNavHeight = windowHeight() - DEFAULT_VALUES.navBarHeight;
|
||||
} else {
|
||||
sidebarNavHeight = windowHeight();
|
||||
}
|
||||
sidebarNavHeight -= this.bannerAreaHeight();
|
||||
}
|
||||
return sidebarNavHeight;
|
||||
}
|
||||
|
||||
calculatesSidebarNavBounds() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { sidebarNavTop, navBarHeight, sidebarNavLeft } = DEFAULT_VALUES;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
return {
|
||||
top,
|
||||
left: !isRTL ? sidebarNavLeft : null,
|
||||
right: isRTL ? sidebarNavLeft : null,
|
||||
zIndex: deviceType === DEVICE_TYPE.MOBILE ? 10 : 2,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentWidth() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input } = layoutContextState;
|
||||
const {
|
||||
sidebarContentMinWidth,
|
||||
sidebarContentMaxWidth,
|
||||
} = DEFAULT_VALUES;
|
||||
let minWidth = 0;
|
||||
let width = 0;
|
||||
let maxWidth = 0;
|
||||
if (input.sidebarContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
minWidth = windowWidth();
|
||||
width = windowWidth();
|
||||
maxWidth = windowWidth();
|
||||
} else {
|
||||
if (input.sidebarContent.width === 0) {
|
||||
width = min(
|
||||
max((windowWidth() * 0.2), sidebarContentMinWidth), sidebarContentMaxWidth,
|
||||
);
|
||||
} else {
|
||||
width = min(max(input.sidebarContent.width, sidebarContentMinWidth),
|
||||
sidebarContentMaxWidth);
|
||||
}
|
||||
minWidth = sidebarContentMinWidth;
|
||||
maxWidth = sidebarContentMaxWidth;
|
||||
}
|
||||
}
|
||||
return {
|
||||
minWidth,
|
||||
width,
|
||||
maxWidth,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesSidebarContentHeight() {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, input, output } = layoutContextState;
|
||||
const { sidebarContent: inputContent, presentation } = input;
|
||||
const { sidebarContent: outputContent } = output;
|
||||
const calculatesSidebarContentHeight = () => {
|
||||
let minHeight = 0;
|
||||
let height = 0;
|
||||
let maxHeight = 0;
|
||||
if (inputContent.isOpen) {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
height = windowHeight() - DEFAULT_VALUES.navBarHeight - this.bannerAreaHeight();
|
||||
if (sidebarContentInput.isOpen) {
|
||||
if (isMobile) {
|
||||
height = windowHeight() - DEFAULT_VALUES.navBarHeight - bannerAreaHeight();
|
||||
minHeight = height;
|
||||
maxHeight = height;
|
||||
} else if (input.cameraDock.numCameras > 0 && presentation.isOpen) {
|
||||
if (inputContent.height > 0 && inputContent.height < windowHeight()) {
|
||||
height = inputContent.height - this.bannerAreaHeight();
|
||||
} else if (cameraDockInput.numCameras > 0 && presentationInput.isOpen) {
|
||||
if (sidebarContentInput.height > 0 && sidebarContentInput.height < windowHeight()) {
|
||||
height = sidebarContentInput.height - bannerAreaHeight();
|
||||
} else {
|
||||
const { size: slideSize } = input.presentation.currentSlide;
|
||||
let calculatedHeight = (windowHeight() - this.bannerAreaHeight()) * 0.3;
|
||||
const { size: slideSize } = presentationInput.currentSlide;
|
||||
let calculatedHeight = (windowHeight() - bannerAreaHeight()) * 0.3;
|
||||
|
||||
if (slideSize.height > 0 && slideSize.width > 0) {
|
||||
calculatedHeight = (slideSize.height * outputContent.width) / slideSize.width;
|
||||
calculatedHeight = (slideSize.height * sidebarContentOutput.width) / slideSize.width;
|
||||
}
|
||||
height = windowHeight() - calculatedHeight - this.bannerAreaHeight();
|
||||
height = windowHeight() - calculatedHeight - bannerAreaHeight();
|
||||
}
|
||||
maxHeight = windowHeight() * 0.75 - this.bannerAreaHeight();
|
||||
minHeight = windowHeight() * 0.25 - this.bannerAreaHeight();
|
||||
maxHeight = windowHeight() * 0.75 - bannerAreaHeight();
|
||||
minHeight = windowHeight() * 0.25 - bannerAreaHeight();
|
||||
|
||||
if (height > maxHeight) {
|
||||
height = maxHeight;
|
||||
}
|
||||
} else {
|
||||
height = windowHeight() - this.bannerAreaHeight();
|
||||
height = windowHeight() - bannerAreaHeight();
|
||||
maxHeight = height;
|
||||
minHeight = height;
|
||||
}
|
||||
@ -324,130 +174,50 @@ class VideoFocusLayout extends Component {
|
||||
height,
|
||||
maxHeight,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
calculatesSidebarContentBounds(sidebarNavWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { sidebarNavTop, navBarHeight } = DEFAULT_VALUES;
|
||||
const calculatesCameraDockBounds = (mediaAreaBounds, sidebarSize) => {
|
||||
const { baseCameraDockBounds } = props;
|
||||
|
||||
let top = sidebarNavTop + this.bannerAreaHeight();
|
||||
const baseBounds = baseCameraDockBounds(mediaAreaBounds, sidebarSize);
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) top = navBarHeight + this.bannerAreaHeight();
|
||||
|
||||
let left = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
let right = deviceType === DEVICE_TYPE.MOBILE ? 0 : sidebarNavWidth;
|
||||
left = !isRTL ? left : null;
|
||||
right = isRTL ? right : null;
|
||||
|
||||
return {
|
||||
top,
|
||||
left,
|
||||
right,
|
||||
zIndex: deviceType === DEVICE_TYPE.MOBILE ? 11 : 1,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesMediaAreaBounds(sidebarNavWidth, sidebarContentWidth) {
|
||||
const { layoutContextState } = this.props;
|
||||
const { deviceType, isRTL } = layoutContextState;
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
const { height: actionBarHeight } = this.calculatesActionbarHeight();
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
left = 0;
|
||||
width = windowWidth();
|
||||
} else {
|
||||
left = !isRTL ? sidebarNavWidth + sidebarContentWidth : 0;
|
||||
width = windowWidth() - sidebarNavWidth - sidebarContentWidth;
|
||||
// do not proceed if using values from LayoutEngine
|
||||
if (Object.keys(baseBounds).length > 0) {
|
||||
return baseBounds;
|
||||
}
|
||||
|
||||
return {
|
||||
width,
|
||||
height: windowHeight() - (navBarHeight + actionBarHeight + this.bannerAreaHeight()),
|
||||
top: navBarHeight + this.bannerAreaHeight(),
|
||||
left,
|
||||
};
|
||||
}
|
||||
|
||||
calculatesCameraDockBounds(mediaAreaBounds, sidebarSize) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
deviceType, input, fullscreen, isRTL,
|
||||
} = layoutContextState;
|
||||
const { cameraDock, presentation } = input;
|
||||
const { isOpen } = presentation;
|
||||
const { numCameras } = cameraDock;
|
||||
const { navBarHeight } = DEFAULT_VALUES;
|
||||
|
||||
const cameraDockBounds = {};
|
||||
|
||||
if (numCameras > 0) {
|
||||
if (fullscreen.group === 'webcams') {
|
||||
cameraDockBounds.width = windowWidth();
|
||||
cameraDockBounds.minWidth = windowWidth();
|
||||
cameraDockBounds.maxWidth = windowWidth();
|
||||
cameraDockBounds.height = windowHeight();
|
||||
cameraDockBounds.minHeight = windowHeight();
|
||||
cameraDockBounds.maxHeight = windowHeight();
|
||||
cameraDockBounds.top = 0;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.right = 0;
|
||||
cameraDockBounds.zIndex = 99;
|
||||
return cameraDockBounds;
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
|
||||
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : 0;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
||||
} else {
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
cameraDockBounds.minHeight = mediaAreaBounds.height * 0.7;
|
||||
cameraDockBounds.height = mediaAreaBounds.height * 0.7;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.7;
|
||||
} else {
|
||||
cameraDockBounds.minHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
}
|
||||
|
||||
cameraDockBounds.top = DEFAULT_VALUES.navBarHeight;
|
||||
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : null;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
||||
cameraDockBounds.minWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
}
|
||||
return cameraDockBounds;
|
||||
if (isMobile) {
|
||||
cameraDockBounds.minHeight = mediaAreaBounds.height * 0.7;
|
||||
cameraDockBounds.height = mediaAreaBounds.height * 0.7;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height * 0.7;
|
||||
} else {
|
||||
cameraDockBounds.minHeight = mediaAreaBounds.height;
|
||||
cameraDockBounds.height = mediaAreaBounds.height;
|
||||
cameraDockBounds.maxHeight = mediaAreaBounds.height;
|
||||
}
|
||||
|
||||
cameraDockBounds.top = 0;
|
||||
cameraDockBounds.left = 0;
|
||||
cameraDockBounds.minWidth = 0;
|
||||
cameraDockBounds.height = 0;
|
||||
cameraDockBounds.width = 0;
|
||||
cameraDockBounds.maxWidth = 0;
|
||||
cameraDockBounds.zIndex = 0;
|
||||
return cameraDockBounds;
|
||||
}
|
||||
cameraDockBounds.top = navBarHeight;
|
||||
cameraDockBounds.left = !isRTL ? mediaAreaBounds.left : null;
|
||||
cameraDockBounds.right = isRTL ? sidebarSize : null;
|
||||
cameraDockBounds.minWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.width = mediaAreaBounds.width;
|
||||
cameraDockBounds.maxWidth = mediaAreaBounds.width;
|
||||
cameraDockBounds.zIndex = 1;
|
||||
|
||||
calculatesMediaBounds(
|
||||
return cameraDockBounds;
|
||||
};
|
||||
|
||||
const calculatesMediaBounds = (
|
||||
mediaAreaBounds,
|
||||
cameraDockBounds,
|
||||
sidebarNavWidth,
|
||||
sidebarContentWidth,
|
||||
sidebarContentHeight,
|
||||
) {
|
||||
const { layoutContextState } = this.props;
|
||||
const {
|
||||
deviceType, input, fullscreen, isRTL,
|
||||
} = layoutContextState;
|
||||
) => {
|
||||
const mediaBounds = {};
|
||||
const { element: fullscreenElement } = fullscreen;
|
||||
const sidebarSize = sidebarNavWidth + sidebarContentWidth;
|
||||
@ -462,12 +232,12 @@ class VideoFocusLayout extends Component {
|
||||
return mediaBounds;
|
||||
}
|
||||
|
||||
if (deviceType === DEVICE_TYPE.MOBILE) {
|
||||
if (isMobile) {
|
||||
mediaBounds.height = mediaAreaBounds.height - cameraDockBounds.height;
|
||||
mediaBounds.left = mediaAreaBounds.left;
|
||||
mediaBounds.top = mediaAreaBounds.top + cameraDockBounds.height;
|
||||
mediaBounds.width = mediaAreaBounds.width;
|
||||
} else if (input.cameraDock.numCameras > 0) {
|
||||
} else if (cameraDockInput.numCameras > 0) {
|
||||
mediaBounds.height = windowHeight() - sidebarContentHeight;
|
||||
mediaBounds.left = !isRTL ? sidebarNavWidth : 0;
|
||||
mediaBounds.right = isRTL ? sidebarNavWidth : 0;
|
||||
@ -477,50 +247,55 @@ class VideoFocusLayout extends Component {
|
||||
} else {
|
||||
mediaBounds.height = mediaAreaBounds.height;
|
||||
mediaBounds.width = mediaAreaBounds.width;
|
||||
mediaBounds.top = DEFAULT_VALUES.navBarHeight + this.bannerAreaHeight();
|
||||
mediaBounds.top = DEFAULT_VALUES.navBarHeight + bannerAreaHeight();
|
||||
mediaBounds.left = !isRTL ? mediaAreaBounds.left : null;
|
||||
mediaBounds.right = isRTL ? sidebarSize : null;
|
||||
mediaBounds.zIndex = 1;
|
||||
}
|
||||
|
||||
return mediaBounds;
|
||||
}
|
||||
};
|
||||
|
||||
calculatesLayout() {
|
||||
const { layoutContextState, layoutContextDispatch } = this.props;
|
||||
const { deviceType, input, isRTL } = layoutContextState;
|
||||
const calculatesLayout = () => {
|
||||
const {
|
||||
calculatesNavbarBounds,
|
||||
calculatesActionbarBounds,
|
||||
calculatesSidebarNavWidth,
|
||||
calculatesSidebarNavHeight,
|
||||
calculatesSidebarNavBounds,
|
||||
calculatesSidebarContentWidth,
|
||||
calculatesSidebarContentBounds,
|
||||
calculatesMediaAreaBounds,
|
||||
isTablet,
|
||||
} = props;
|
||||
const { captionsMargin } = DEFAULT_VALUES;
|
||||
|
||||
const sidebarNavWidth = this.calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = this.calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = this.calculatesSidebarContentWidth();
|
||||
const sidebarNavBounds = this.calculatesSidebarNavBounds(
|
||||
const sidebarNavWidth = calculatesSidebarNavWidth();
|
||||
const sidebarNavHeight = calculatesSidebarNavHeight();
|
||||
const sidebarContentWidth = calculatesSidebarContentWidth();
|
||||
const sidebarNavBounds = calculatesSidebarNavBounds();
|
||||
const sidebarContentBounds = calculatesSidebarContentBounds(sidebarNavWidth.width);
|
||||
const mediaAreaBounds = calculatesMediaAreaBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const sidebarContentBounds = this.calculatesSidebarContentBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const mediaAreaBounds = this.calculatesMediaAreaBounds(
|
||||
sidebarNavWidth.width, sidebarContentWidth.width,
|
||||
);
|
||||
const navbarBounds = this.calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = this.calculatesActionbarBounds(mediaAreaBounds);
|
||||
const navbarBounds = calculatesNavbarBounds(mediaAreaBounds);
|
||||
const actionbarBounds = calculatesActionbarBounds(mediaAreaBounds);
|
||||
const sidebarSize = sidebarContentWidth.width + sidebarNavWidth.width;
|
||||
const cameraDockBounds = this.calculatesCameraDockBounds(mediaAreaBounds, sidebarSize);
|
||||
const sidebarContentHeight = this.calculatesSidebarContentHeight();
|
||||
const mediaBounds = this.calculatesMediaBounds(
|
||||
const cameraDockBounds = calculatesCameraDockBounds(mediaAreaBounds, sidebarSize);
|
||||
const sidebarContentHeight = calculatesSidebarContentHeight();
|
||||
const mediaBounds = calculatesMediaBounds(
|
||||
mediaAreaBounds,
|
||||
cameraDockBounds,
|
||||
sidebarNavWidth.width,
|
||||
sidebarContentWidth.width,
|
||||
sidebarContentHeight.height,
|
||||
);
|
||||
const isBottomResizable = input.cameraDock.numCameras > 0;
|
||||
const isBottomResizable = cameraDockInput.numCameras > 0;
|
||||
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_NAVBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.navBar.hasNavBar,
|
||||
display: navbarInput.hasNavBar,
|
||||
width: navbarBounds.width,
|
||||
height: navbarBounds.height,
|
||||
top: navbarBounds.top,
|
||||
@ -533,7 +308,7 @@ class VideoFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_ACTIONBAR_OUTPUT,
|
||||
value: {
|
||||
display: input.actionBar.hasActionBar,
|
||||
display: actionbarInput.hasActionBar,
|
||||
width: actionbarBounds.width,
|
||||
height: actionbarBounds.height,
|
||||
innerHeight: actionbarBounds.innerHeight,
|
||||
@ -557,7 +332,7 @@ class VideoFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_NAVIGATION_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarNavigation.isOpen,
|
||||
display: sidebarNavigationInput.isOpen,
|
||||
minWidth: sidebarNavWidth.minWidth,
|
||||
width: sidebarNavWidth.width,
|
||||
maxWidth: sidebarNavWidth.maxWidth,
|
||||
@ -566,8 +341,7 @@ class VideoFocusLayout extends Component {
|
||||
left: sidebarNavBounds.left,
|
||||
right: sidebarNavBounds.right,
|
||||
tabOrder: DEFAULT_VALUES.sidebarNavTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarNavBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -585,7 +359,7 @@ class VideoFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_OUTPUT,
|
||||
value: {
|
||||
display: input.sidebarContent.isOpen,
|
||||
display: sidebarContentInput.isOpen,
|
||||
minWidth: sidebarContentWidth.minWidth,
|
||||
width: sidebarContentWidth.width,
|
||||
maxWidth: sidebarContentWidth.maxWidth,
|
||||
@ -595,10 +369,9 @@ class VideoFocusLayout extends Component {
|
||||
top: sidebarContentBounds.top,
|
||||
left: sidebarContentBounds.left,
|
||||
right: sidebarContentBounds.right,
|
||||
currentPanelType: input.currentPanelType,
|
||||
currentPanelType,
|
||||
tabOrder: DEFAULT_VALUES.sidebarContentTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: sidebarContentBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -624,7 +397,7 @@ class VideoFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_CAMERA_DOCK_OUTPUT,
|
||||
value: {
|
||||
display: input.cameraDock.numCameras > 0,
|
||||
display: cameraDockInput.numCameras > 0,
|
||||
minWidth: cameraDockBounds.minWidth,
|
||||
width: cameraDockBounds.width,
|
||||
maxWidth: cameraDockBounds.maxWidth,
|
||||
@ -649,15 +422,14 @@ class VideoFocusLayout extends Component {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_PRESENTATION_OUTPUT,
|
||||
value: {
|
||||
display: input.presentation.isOpen,
|
||||
display: presentationInput.isOpen,
|
||||
width: mediaBounds.width,
|
||||
height: mediaBounds.height,
|
||||
top: mediaBounds.top,
|
||||
left: mediaBounds.left,
|
||||
right: isRTL ? mediaBounds.right : null,
|
||||
tabOrder: DEFAULT_VALUES.presentationTabOrder,
|
||||
isResizable: deviceType !== DEVICE_TYPE.MOBILE
|
||||
&& deviceType !== DEVICE_TYPE.TABLET,
|
||||
isResizable: !isMobile && !isTablet,
|
||||
zIndex: mediaBounds.zIndex,
|
||||
},
|
||||
});
|
||||
@ -694,11 +466,9 @@ class VideoFocusLayout extends Component {
|
||||
right: isRTL ? mediaBounds.right : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default LayoutContextFunc.withConsumer(VideoFocusLayout);
|
||||
export default VideoFocusLayout;
|
||||
|
@ -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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import LockViewersComponent from './component';
|
||||
import { updateLockSettings, updateWebcamsOnlyForModerator } from './service';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { LockStruct } from './context';
|
||||
import { withUsersConsumer } from '/imports/ui/components/components-data/users-context/context';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import LockViewersNotifyComponent from './component';
|
||||
|
||||
|
@ -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/api/users';
|
||||
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';
|
||||
|
@ -11,8 +11,8 @@ import logoutRouteHandler from '/imports/utils/logoutRouteHandler';
|
||||
import Rating from './rating/component';
|
||||
import { styles } from './styles';
|
||||
import logger from '/imports/startup/client/logger';
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import AudioManager from '/imports/ui/services/audio-manager';
|
||||
import { meetingIsBreakout } from '/imports/ui/components/app/service';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { withModalMounter } from '/imports/ui/components/modal/service';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
|
@ -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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/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';
|
||||
@ -11,7 +11,7 @@ import { UsersContext } from '/imports/ui/components/components-data/users-conte
|
||||
import NoteService from '/imports/ui/components/note/service';
|
||||
import Service from './service';
|
||||
import NavBar from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectInput, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
|
||||
const PUBLIC_CONFIG = Meteor.settings.public;
|
||||
const ROLE_MODERATOR = PUBLIC_CONFIG.user.role_moderator;
|
||||
@ -28,8 +28,6 @@ const checkUnreadMessages = ({
|
||||
};
|
||||
|
||||
const NavBarContainer = ({ children, ...props }) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const usingChatContext = useContext(ChatContext);
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const usingGroupChatContext = useContext(GroupChatContext);
|
||||
@ -37,13 +35,15 @@ const NavBarContainer = ({ children, ...props }) => {
|
||||
const { users } = usingUsersContext;
|
||||
const { groupChat: groupChats } = usingGroupChatContext;
|
||||
const { ...rest } = props;
|
||||
const {
|
||||
input, output,
|
||||
} = layoutContextState;
|
||||
const { sidebarContent, sidebarNavigation } = input;
|
||||
const { sidebarNavPanel } = sidebarNavigation;
|
||||
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const sidebarNavigation = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const navBar = layoutSelectOutput((i) => i.navBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const { navBar } = output;
|
||||
const { sidebarNavPanel } = sidebarNavigation;
|
||||
|
||||
const hasUnreadNotes = NoteService.hasUnreadNotes(sidebarContentPanel);
|
||||
const hasUnreadMessages = checkUnreadMessages(
|
||||
{ groupChatsMessages, groupChats, users: users[Auth.meetingID] },
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import VoiceUsers from '/imports/api/voice-users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
@ -6,7 +6,7 @@ import { debounce } from 'lodash';
|
||||
import TalkingIndicator from './component';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import { meetingIsBreakout } from '/imports/ui/components/app/service';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
|
||||
const APP_CONFIG = Meteor.settings.public.app;
|
||||
const { enableTalkingIndicator } = APP_CONFIG;
|
||||
@ -15,12 +15,13 @@ const TALKING_INDICATORS_MAX = 8;
|
||||
|
||||
const TalkingIndicatorContainer = (props) => {
|
||||
if (!enableTalkingIndicator) return null;
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent, sidebarNavigation } = input;
|
||||
const { sidebarNavPanel } = sidebarNavigation;
|
||||
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const sidebarNavigation = layoutSelectInput((i) => i.sidebarNavigation);
|
||||
const { sidebarNavPanel } = sidebarNavigation;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const sidebarNavigationIsOpen = sidebarNavigation.isOpen;
|
||||
const sidebarContentIsOpen = sidebarContent.isOpen;
|
||||
return (
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Note from './component';
|
||||
import NoteService from './service';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../layout/context';
|
||||
|
||||
const NoteContainer = ({ children, ...props }) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch, layoutContextState } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { cameraDock } = input;
|
||||
const cameraDock = layoutSelectInput((i) => i.cameraDock);
|
||||
const { isResizing } = cameraDock;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return (
|
||||
<Note {...{ layoutContextDispatch, isResizing, ...props }}>
|
||||
{children}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Note from '/imports/api/note';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import _ from 'lodash';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Meetings, { MeetingTimeRemaining } from '/imports/api/meetings';
|
||||
import { MeetingTimeRemaining } from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import BreakoutRemainingTime from '/imports/ui/components/breakout-room/breakout-remaining-time/container';
|
||||
import { styles } from './styles.scss';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../layout/context';
|
||||
import { ACTIONS } from '../layout/enums';
|
||||
|
||||
import breakoutService from '/imports/ui/components/breakout-room/service';
|
||||
@ -76,10 +77,10 @@ const intlMessages = defineMessages({
|
||||
|
||||
const NotificationsBarContainer = (props) => {
|
||||
const { message, color } = props;
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { notificationsBar } = input;
|
||||
|
||||
const notificationsBar = layoutSelectInput((i) => i.notificationsBar);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { hasNotification } = notificationsBar;
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -8,14 +8,14 @@ import { Session } from 'meteor/session';
|
||||
import Service from './service';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { UsersContext } from '../components-data/users-context/context';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutDispatch } from '../layout/context';
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const PUBLIC_CHAT_KEY = CHAT_CONFIG.public_id;
|
||||
|
||||
const PollContainer = ({ ...props }) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Polls from '/imports/api/polls';
|
||||
import { CurrentPoll } from '/imports/api/polls';
|
||||
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
@ -217,7 +217,7 @@ export default {
|
||||
{ fields: { presenter: 1 } },
|
||||
).presenter,
|
||||
pollTypes,
|
||||
currentPoll: () => Polls.findOne({ meetingId: Auth.meetingID }),
|
||||
currentPoll: () => CurrentPoll.findOne({ meetingId: Auth.meetingID }),
|
||||
pollAnswerIds,
|
||||
POLL_AVATAR_COLOR,
|
||||
isDefaultPoll,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import PollingService from './service';
|
||||
import PollService from '/imports/ui/components/poll/service';
|
||||
|
@ -5,34 +5,40 @@ import MediaService, {
|
||||
shouldEnableSwapLayout,
|
||||
} from '/imports/ui/components/media/service';
|
||||
import { notify } from '/imports/ui/services/notification';
|
||||
import { Session } from 'meteor/session';
|
||||
import PresentationService from './service';
|
||||
import { Slides } from '/imports/api/slides';
|
||||
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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import LayoutContext from '../layout/context';
|
||||
import {
|
||||
layoutSelect,
|
||||
layoutSelectInput,
|
||||
layoutSelectOutput,
|
||||
layoutDispatch,
|
||||
} from '../layout/context';
|
||||
import WhiteboardService from '/imports/ui/components/whiteboard/service';
|
||||
import { DEVICE_TYPE } from '../layout/enums';
|
||||
|
||||
const ROLE_VIEWER = Meteor.settings.public.user.role_viewer;
|
||||
|
||||
const PresentationContainer = ({ presentationPodIds, mountPresentation, ...props }) => {
|
||||
const fullscreenElementId = 'Presentation';
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const {
|
||||
input, output, layoutType, fullscreen, deviceType,
|
||||
} = layoutContextState;
|
||||
const { cameraDock } = input;
|
||||
const { numCameras } = cameraDock;
|
||||
const { presentation } = output;
|
||||
const { element } = fullscreen;
|
||||
const fullscreenContext = (element === fullscreenElementId);
|
||||
const { layoutSwapped, podId } = props;
|
||||
|
||||
const cameraDock = layoutSelectInput((i) => i.cameraDock);
|
||||
const presentation = layoutSelectOutput((i) => i.presentation);
|
||||
const layoutType = layoutSelect((i) => i.layoutType);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const deviceType = layoutSelect((i) => i.deviceType);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { numCameras } = cameraDock;
|
||||
const { element } = fullscreen;
|
||||
const fullscreenElementId = 'Presentation';
|
||||
const fullscreenContext = (element === fullscreenElementId);
|
||||
|
||||
const isIphone = !!(navigator.userAgent.match(/iPhone/i));
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Cursor from '/imports/ui/components/cursor/service';
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
|
||||
const getCurrentCursor = (cursorId) => {
|
||||
const cursor = Cursor.findOne({ _id: cursorId });
|
||||
|
@ -1,12 +1,9 @@
|
||||
import React, { useContext } from 'react';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import React from 'react';
|
||||
import { layoutSelectOutput } from '../../layout/context';
|
||||
import PresentationArea from './component';
|
||||
|
||||
const PresentationAreaContainer = () => {
|
||||
const layoutManager = useContext(LayoutContext);
|
||||
const { layoutContextState } = layoutManager;
|
||||
const { output } = layoutContextState;
|
||||
const { presentation } = output;
|
||||
const presentation = layoutSelectOutput((i) => i.presentation);
|
||||
|
||||
return <PresentationArea {...{ ...presentation }} />;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Users from '/imports/api/users/';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
@ -11,16 +11,16 @@ import {
|
||||
isGloballyBroadcasting,
|
||||
} from './service';
|
||||
import ScreenshareComponent from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutSelect, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
|
||||
const ScreenshareContainer = (props) => {
|
||||
const fullscreenElementId = 'Screenshare';
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { output, fullscreen } = layoutContextState;
|
||||
const { screenShare } = output;
|
||||
const screenShare = layoutSelectOutput((i) => i.screenShare);
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { element } = fullscreen;
|
||||
const fullscreenElementId = 'Screenshare';
|
||||
const fullscreenContext = (element === fullscreenElementId);
|
||||
|
||||
if (isVideoBroadcasting()) {
|
||||
|
@ -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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/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';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import SettingsService from '/imports/ui/services/settings';
|
||||
import Settings from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutDispatch } from '../layout/context';
|
||||
|
||||
import {
|
||||
getUserRoles,
|
||||
@ -12,8 +12,7 @@ import {
|
||||
} from './service';
|
||||
|
||||
const SettingsContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <Settings {...props} layoutContextDispatch={layoutContextDispatch} />;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Settings from '/imports/ui/services/settings';
|
||||
import { notify } from '/imports/ui/services/notification';
|
||||
|
@ -1,25 +1,22 @@
|
||||
import React from 'react';
|
||||
import SidebarContent from './component';
|
||||
import { LayoutContextFunc } from '../layout/context';
|
||||
import { layoutSelectInput, layoutSelectOutput, layoutDispatch } from '../layout/context';
|
||||
|
||||
const SidebarContentContainer = (props) => {
|
||||
const { layoutContextState, layoutContextDispatch } = props;
|
||||
const {
|
||||
output, input,
|
||||
} = layoutContextState;
|
||||
const { sidebarContent: sidebarContentInput } = input;
|
||||
const SidebarContentContainer = () => {
|
||||
const sidebarContentInput = layoutSelectInput((i) => i.sidebarContent);
|
||||
const sidebarContentOutput = layoutSelectOutput((i) => i.sidebarContent);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
const { sidebarContentPanel } = sidebarContentInput;
|
||||
const { sidebarContent } = output;
|
||||
|
||||
if (sidebarContent.display === false) return null;
|
||||
if (sidebarContentOutput.display === false) return null;
|
||||
|
||||
return (
|
||||
<SidebarContent
|
||||
{...sidebarContent}
|
||||
{...sidebarContentOutput}
|
||||
contextDispatch={layoutContextDispatch}
|
||||
sidebarContentPanel={sidebarContentPanel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutContextFunc.withConsumer(SidebarContentContainer);
|
||||
export default SidebarContentContainer;
|
||||
|
@ -1,21 +1,19 @@
|
||||
import React from 'react';
|
||||
import { LayoutContextFunc } from '../layout/context';
|
||||
import { layoutDispatch, layoutSelectOutput } from '../layout/context';
|
||||
import SidebarNavigation from './component';
|
||||
|
||||
const SidebarNavigationContainer = (props) => {
|
||||
const { layoutContextState, layoutContextDispatch, openPanel } = props;
|
||||
const { output } = layoutContextState;
|
||||
const { sidebarNavigation } = output;
|
||||
const SidebarNavigationContainer = () => {
|
||||
const sidebarNavigation = layoutSelectOutput((i) => i.sidebarNavigation);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
if (sidebarNavigation.display === false) return null;
|
||||
|
||||
return (
|
||||
<SidebarNavigation
|
||||
{...sidebarNavigation}
|
||||
openPanel={openPanel}
|
||||
contextDispatch={layoutContextDispatch}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutContextFunc.withConsumer(SidebarNavigationContainer);
|
||||
export default SidebarNavigationContainer;
|
||||
|
@ -49,10 +49,10 @@ class StatusNotifier extends Component {
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
emojiUsers, raiseHandAudioAlert, raiseHandPushAlert, status, isViewer,
|
||||
emojiUsers, raiseHandAudioAlert, raiseHandPushAlert, status, isViewer, isPresenter,
|
||||
} = this.props;
|
||||
|
||||
if (isViewer) {
|
||||
if (isViewer && !isPresenter) {
|
||||
if (this.statusNotifierId) toast.dismiss(this.statusNotifierId);
|
||||
return false;
|
||||
}
|
||||
|
@ -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/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/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';
|
||||
@ -14,10 +14,12 @@ const StatusNotifierContainer = (props) => {
|
||||
const { users } = usingUsersContext;
|
||||
const currentUser = users[Auth.meetingID][Auth.userID];
|
||||
const isViewer = currentUser.role === ROLE_VIEWER;
|
||||
const isPresenter = currentUser.presenter;
|
||||
return (
|
||||
<StatusNotifier {...{
|
||||
...props,
|
||||
isViewer,
|
||||
isPresenter,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -4,10 +4,13 @@ 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 from '/imports/api/users';
|
||||
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 AnnotationsTextService from '/imports/ui/components/whiteboard/annotations/text/service';
|
||||
import { Annotations as AnnotationsLocal } from '/imports/ui/components/whiteboard/service';
|
||||
|
||||
import SubscriptionRegistry, { subscriptionReactivity } from '../../services/subscription-registry/subscriptionRegistry';
|
||||
|
||||
const CHAT_CONFIG = Meteor.settings.public.chat;
|
||||
const CHAT_ENABLED = CHAT_CONFIG.enabled;
|
||||
@ -19,12 +22,14 @@ const SUBSCRIPTIONS = [
|
||||
'voiceUsers', 'whiteboard-multi-user', 'screenshare', 'group-chat',
|
||||
'presentation-pods', 'users-settings', 'guestUser', 'users-infos', 'note', 'meeting-time-remaining',
|
||||
'local-settings', 'users-typing', 'record-meetings', 'video-streams',
|
||||
'connection-status', 'voice-call-states', 'external-video-meetings',
|
||||
'connection-status', 'voice-call-states', 'external-video-meetings', 'breakouts',
|
||||
];
|
||||
|
||||
const EVENT_NAME = 'bbb-group-chat-messages-subscription-has-stoppped';
|
||||
const EVENT_NAME_SUBSCRIPTION_READY = 'bbb-group-chat-messages-subscriptions-ready';
|
||||
|
||||
let oldRole = '';
|
||||
|
||||
class Subscriptions extends Component {
|
||||
componentDidUpdate() {
|
||||
const { subscriptionsReady } = this.props;
|
||||
@ -41,7 +46,6 @@ class Subscriptions extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default withTracker(() => {
|
||||
const { credentials } = Auth;
|
||||
const { meetingId, requesterUserId } = credentials;
|
||||
@ -51,8 +55,6 @@ export default withTracker(() => {
|
||||
};
|
||||
}
|
||||
|
||||
const currentUser = Users.findOne({ intId: requesterUserId }, { fields: { role: 1 } });
|
||||
|
||||
const subscriptionErrorHandler = {
|
||||
onError: (error) => {
|
||||
logger.error({
|
||||
@ -63,24 +65,47 @@ export default withTracker(() => {
|
||||
},
|
||||
};
|
||||
|
||||
let subscriptionsHandlers = SUBSCRIPTIONS.map((name) => {
|
||||
if ((!TYPING_INDICATOR_ENABLED && name.indexOf('typing') !== -1)
|
||||
|| (!CHAT_ENABLED && name.indexOf('chat') !== -1)) return;
|
||||
const currentUser = Users.findOne({ intId: requesterUserId }, { fields: { role: 1 } });
|
||||
|
||||
return Meteor.subscribe(name, subscriptionErrorHandler);
|
||||
let subscriptionsHandlers = SUBSCRIPTIONS.map((name) => {
|
||||
let subscriptionHandlers = subscriptionErrorHandler;
|
||||
if ((!TYPING_INDICATOR_ENABLED && name.indexOf('typing') !== -1)
|
||||
|| (!CHAT_ENABLED && name.indexOf('chat') !== -1)) return null;
|
||||
|
||||
if (name === 'users') {
|
||||
subscriptionHandlers = {
|
||||
...subscriptionHandlers,
|
||||
onStop: () => {
|
||||
const event = new Event(EVENT_NAME);
|
||||
window.dispatchEvent(event);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return SubscriptionRegistry.createSubscription(name, subscriptionHandlers);
|
||||
});
|
||||
|
||||
if (currentUser) {
|
||||
subscriptionsHandlers.push(Meteor.subscribe('meetings', currentUser.role, subscriptionErrorHandler));
|
||||
subscriptionsHandlers.push(Meteor.subscribe('users', currentUser.role, {
|
||||
...subscriptionErrorHandler,
|
||||
onStop: () => {
|
||||
const event = new Event(EVENT_NAME);
|
||||
window.dispatchEvent(event);
|
||||
},
|
||||
}));
|
||||
subscriptionsHandlers.push(Meteor.subscribe('breakouts', currentUser.role, subscriptionErrorHandler));
|
||||
subscriptionsHandlers.push(Meteor.subscribe('guestUser', currentUser.role, subscriptionErrorHandler));
|
||||
if (currentUser && (oldRole !== currentUser?.role)) {
|
||||
// stop subscription from the client-side as the server-side only watch moderators
|
||||
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);
|
||||
// stop role dependent subscriptions
|
||||
[
|
||||
SubscriptionRegistry.getSubscription('meetings'),
|
||||
SubscriptionRegistry.getSubscription('users'),
|
||||
SubscriptionRegistry.getSubscription('breakouts'),
|
||||
SubscriptionRegistry.getSubscription('guestUser'),
|
||||
].forEach((item) => {
|
||||
if (item) item.stop();
|
||||
});
|
||||
}
|
||||
oldRole = currentUser?.role;
|
||||
}
|
||||
|
||||
const annotationsHandler = Meteor.subscribe('annotations', {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import ChatListItem from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelect, layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
|
||||
const ChatListItemContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input, idChatOpen } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const idChatOpen = layoutSelect((i) => i.idChatOpen);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const sidebarContentIsOpen = sidebarContent.isOpen;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import Users from '/imports/api/users';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import VoiceUsers from '/imports/api/voice-users';
|
||||
import GroupChat from '/imports/api/group-chat';
|
||||
import Breakouts from '/imports/api/breakouts/';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Breakouts from '/imports/ui/local-collections/breakouts-collection/breakouts';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Storage from '/imports/ui/services/storage/session';
|
||||
import { EMOJI_STATUSES } from '/imports/utils/statuses';
|
||||
|
@ -44,6 +44,8 @@ class UserContent extends PureComponent {
|
||||
forcePollOpen,
|
||||
hasBreakoutRoom,
|
||||
pendingUsers,
|
||||
isWaitingRoomEnabled,
|
||||
isGuestLobbyMessageEnabled,
|
||||
requestUserInformation,
|
||||
currentClosedChats,
|
||||
sidebarContentPanel,
|
||||
@ -51,6 +53,9 @@ class UserContent extends PureComponent {
|
||||
startedChats,
|
||||
} = this.props;
|
||||
|
||||
const showWaitingRoom = (isGuestLobbyMessageEnabled && isWaitingRoomEnabled)
|
||||
|| pendingUsers.length > 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
data-test="userListContent"
|
||||
@ -82,7 +87,7 @@ class UserContent extends PureComponent {
|
||||
intl,
|
||||
}}
|
||||
/>
|
||||
{pendingUsers.length > 0 && currentUser.role === ROLE_MODERATOR
|
||||
{showWaitingRoom && currentUser.role === ROLE_MODERATOR
|
||||
? (
|
||||
<WaitingUsers
|
||||
{...{
|
||||
|
@ -4,19 +4,20 @@ 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/api/guest-users/';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import GuestUsers from '/imports/ui/local-collections/guest-users-collection/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';
|
||||
|
||||
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
||||
const STARTED_CHAT_LIST_KEY = 'startedChatList';
|
||||
|
||||
const UserContentContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
const currentUser = {
|
||||
@ -25,11 +26,14 @@ const UserContentContainer = (props) => {
|
||||
locked: users[Auth.meetingID][Auth.userID].locked,
|
||||
role: users[Auth.meetingID][Auth.userID].role,
|
||||
};
|
||||
const { isGuestLobbyMessageEnabled } = WaitingUsersService;
|
||||
|
||||
return (
|
||||
<UserContent
|
||||
{...{
|
||||
layoutContextDispatch,
|
||||
sidebarContentPanel,
|
||||
isGuestLobbyMessageEnabled,
|
||||
...props,
|
||||
}}
|
||||
currentUser={currentUser}
|
||||
@ -47,4 +51,5 @@ export default withTracker(() => ({
|
||||
approved: false,
|
||||
denied: false,
|
||||
}).fetch(),
|
||||
isWaitingRoomEnabled: WaitingUsersService.isWaitingRoomEnabled(),
|
||||
}))(UserContentContainer);
|
||||
|
@ -1,15 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import UserCaptionsItem from './component';
|
||||
import CaptionsService from '/imports/ui/components/captions/service';
|
||||
import LayoutContext from '../../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../../layout/context';
|
||||
|
||||
const UserCaptionsItemContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <UserCaptionsItem {...{ sidebarContentPanel, layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
||||
|
@ -1,16 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import NoteService from '/imports/ui/components/note/service';
|
||||
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
|
||||
import UserNotes from './component';
|
||||
import LayoutContext from '../../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../../layout/context';
|
||||
|
||||
const UserNotesContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
return <UserNotes {...{ layoutContextDispatch, sidebarContentPanel, ...props }} />;
|
||||
};
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import BreakoutService from '/imports/ui/components/breakout-room/service';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import UserListItem from './component';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import LayoutContext from '../../../../layout/context';
|
||||
import { layoutDispatch } from '../../../../layout/context';
|
||||
|
||||
const UserListItemContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <UserListItem {...{ layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
const isMe = (intId) => intId === Auth.userID;
|
||||
|
@ -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/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/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';
|
||||
|
@ -60,11 +60,13 @@ const WaitingUsers = ({
|
||||
>
|
||||
<Icon iconName="user" />
|
||||
<span>{intl.formatMessage(intlMessages.title)}</span>
|
||||
<div className={styles.unreadMessages}>
|
||||
<div className={styles.unreadMessagesText}>
|
||||
{pendingUsers.length}
|
||||
{pendingUsers.length > 0 && (
|
||||
<div className={styles.unreadMessages}>
|
||||
<div className={styles.unreadMessagesText}>
|
||||
{pendingUsers.length}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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/api/users';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Service from './service';
|
||||
import VideoPreview from './component';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Meetings from '/imports/api/meetings/';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import Users from '/imports/api/users/';
|
||||
import VideoStreams from '/imports/api/video-streams';
|
||||
|
@ -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/api/meetings';
|
||||
import Users from '/imports/api/users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||
import VideoStreams from '/imports/api/video-streams';
|
||||
import UserListService from '/imports/ui/components/user-list/service';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
|
@ -1,14 +1,13 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import VideoList from '/imports/ui/components/video-provider/video-list/component';
|
||||
import VideoService from '/imports/ui/components/video-provider/service';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelect, layoutSelectOutput, layoutDispatch } from '../../layout/context';
|
||||
|
||||
const VideoListContainer = ({ children, ...props }) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { layoutType, output } = layoutContextState;
|
||||
const { cameraDock } = output;
|
||||
const layoutType = layoutSelect((i) => i.layoutType);
|
||||
const cameraDock = layoutSelectOutput((i) => i.cameraDock);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const { streams } = props;
|
||||
return (
|
||||
|
@ -1,17 +1,17 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import VoiceUsers from '/imports/api/voice-users/';
|
||||
import VideoListItem from './component';
|
||||
import LayoutContext from '/imports/ui/components/layout/context';
|
||||
import { layoutSelect, layoutDispatch } from '/imports/ui/components/layout/context';
|
||||
|
||||
const VideoListItemContainer = (props) => {
|
||||
const { cameraId } = props;
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { fullscreen } = layoutContextState;
|
||||
|
||||
const fullscreen = layoutSelect((i) => i.fullscreen);
|
||||
const { element } = fullscreen;
|
||||
const isFullscreenContext = (element === cameraId);
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return (
|
||||
<VideoListItem
|
||||
|
@ -1,22 +1,20 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import GuestUsers from '/imports/api/guest-users/';
|
||||
import GuestUsers from '/imports/ui/local-collections/guest-users-collection/guest-users';
|
||||
import { UsersContext } from '/imports/ui/components/components-data/users-context/context';
|
||||
import WaitingComponent from './component';
|
||||
import LayoutContext from '../../layout/context';
|
||||
import { layoutSelectInput, layoutDispatch } from '../../layout/context';
|
||||
import { PANELS } from '../../layout/enums';
|
||||
|
||||
const USER_CONFIG = Meteor.settings.public.user;
|
||||
const ROLE_MODERATOR = USER_CONFIG.role_moderator;
|
||||
|
||||
const WaitingContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextState, layoutContextDispatch } = layoutContext;
|
||||
const { input } = layoutContextState;
|
||||
const { sidebarContent } = input;
|
||||
const sidebarContent = layoutSelectInput((i) => i.sidebarContent);
|
||||
const { sidebarContentPanel } = sidebarContent;
|
||||
const managementPanelIsOpen = sidebarContentPanel === PANELS.WAITING_USERS;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
const usingUsersContext = useContext(UsersContext);
|
||||
const { users } = usingUsersContext;
|
||||
|
@ -45,6 +45,10 @@ const intlMessages = defineMessages({
|
||||
id: 'app.userList.guest.pendingGuestUsers',
|
||||
description: 'Title for the waiting users',
|
||||
},
|
||||
noPendingUsers: {
|
||||
id: 'app.userList.guest.noPendingUsers',
|
||||
description: 'Label for no users waiting',
|
||||
},
|
||||
rememberChoice: {
|
||||
id: 'app.userList.guest.rememberChoice',
|
||||
description: 'Remember label for checkbox',
|
||||
@ -120,6 +124,14 @@ const renderGuestUserItem = (
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderNoUserWaitingItem = (message) => (
|
||||
<div className={styles.pendingUsers}>
|
||||
<p className={styles.noPendingUsers}>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderPendingUsers = (message, usersArray, action, intl) => {
|
||||
if (!usersArray.length) return null;
|
||||
return (
|
||||
@ -147,25 +159,6 @@ const renderPendingUsers = (message, usersArray, action, intl) => {
|
||||
const WaitingUsers = (props) => {
|
||||
const [rememberChoice, setRememberChoice] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
authenticatedUsers,
|
||||
guestUsers,
|
||||
layoutContextDispatch,
|
||||
} = props;
|
||||
|
||||
if (!authenticatedUsers.length && !guestUsers.length) {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
||||
value: false,
|
||||
});
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
||||
value: PANELS.NONE,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
intl,
|
||||
authenticatedUsers,
|
||||
@ -180,6 +173,28 @@ const WaitingUsers = (props) => {
|
||||
allowRememberChoice,
|
||||
} = props;
|
||||
|
||||
const existPendingUsers = authenticatedUsers.length > 0 || guestUsers.length > 0;
|
||||
|
||||
const closePanel = () => {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
||||
value: false,
|
||||
});
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
||||
value: PANELS.NONE,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const {
|
||||
isWaitingRoomEnabled,
|
||||
} = props;
|
||||
if (!isWaitingRoomEnabled && !existPendingUsers) {
|
||||
closePanel();
|
||||
}
|
||||
});
|
||||
|
||||
const onCheckBoxChange = (e) => {
|
||||
const { checked } = e.target;
|
||||
setRememberChoice(checked);
|
||||
@ -189,6 +204,7 @@ const WaitingUsers = (props) => {
|
||||
if (shouldExecutePolicy) {
|
||||
changeGuestPolicy(policyRule);
|
||||
}
|
||||
closePanel();
|
||||
return cb();
|
||||
};
|
||||
|
||||
@ -251,16 +267,7 @@ const WaitingUsers = (props) => {
|
||||
className={styles.title}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
||||
value: false,
|
||||
});
|
||||
layoutContextDispatch({
|
||||
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
||||
value: PANELS.NONE,
|
||||
});
|
||||
}}
|
||||
onClick={() => closePanel()}
|
||||
label={intl.formatMessage(intlMessages.title)}
|
||||
icon="left_arrow"
|
||||
className={styles.hideBtn}
|
||||
@ -287,6 +294,7 @@ const WaitingUsers = (props) => {
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
{existPendingUsers && (
|
||||
<div>
|
||||
<div>
|
||||
<p className={styles.mainTitle}>{intl.formatMessage(intlMessages.optionTitle)}</p>
|
||||
@ -307,6 +315,7 @@ const WaitingUsers = (props) => {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
{renderPendingUsers(
|
||||
intl.formatMessage(intlMessages.pendingUsers,
|
||||
{ 0: authenticatedUsers.length }),
|
||||
@ -321,6 +330,9 @@ const WaitingUsers = (props) => {
|
||||
guestUsersCall,
|
||||
intl,
|
||||
)}
|
||||
{!existPendingUsers && (
|
||||
renderNoUserWaitingItem(intl.formatMessage(intlMessages.noPendingUsers))
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import GuestUsers from '/imports/api/guest-users/';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import GuestUsers from '/imports/ui/local-collections/guest-users-collection/guest-users';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Service from './service';
|
||||
import WaitingComponent from './component';
|
||||
import LayoutContext from '../layout/context';
|
||||
import { layoutDispatch } from '../layout/context';
|
||||
|
||||
const WaitingContainer = (props) => {
|
||||
const layoutContext = useContext(LayoutContext);
|
||||
const { layoutContextDispatch } = layoutContext;
|
||||
const layoutContextDispatch = layoutDispatch();
|
||||
|
||||
return <WaitingComponent {...{ layoutContextDispatch, ...props }} />;
|
||||
};
|
||||
|
||||
@ -37,6 +37,7 @@ export default withTracker(() => {
|
||||
guestUsers,
|
||||
authenticatedUsers,
|
||||
guestUsersCall: Service.guestUsersCall,
|
||||
isWaitingRoomEnabled: Service.isWaitingRoomEnabled(),
|
||||
changeGuestPolicy: Service.changeGuestPolicy,
|
||||
isGuestLobbyMessageEnabled: Service.isGuestLobbyMessageEnabled,
|
||||
setGuestLobbyMessage: Service.setGuestLobbyMessage,
|
||||
|
@ -1,10 +1,10 @@
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import { makeCall } from '/imports/ui/services/api';
|
||||
|
||||
const guestUsersCall = (guestsArray, status) => makeCall('allowPendingUsers', guestsArray, status);
|
||||
|
||||
const changeGuestPolicy = policyRule => makeCall('changeGuestPolicy', policyRule);
|
||||
const changeGuestPolicy = (policyRule) => makeCall('changeGuestPolicy', policyRule);
|
||||
|
||||
const getGuestPolicy = () => {
|
||||
const meeting = Meetings.findOne(
|
||||
@ -15,6 +15,8 @@ const getGuestPolicy = () => {
|
||||
return meeting.usersProp.guestPolicy;
|
||||
};
|
||||
|
||||
const isWaitingRoomEnabled = () => getGuestPolicy() === 'ASK_MODERATOR';
|
||||
|
||||
const isGuestLobbyMessageEnabled = Meteor.settings.public.app.enableGuestLobbyMessage;
|
||||
|
||||
// We use the dynamicGuestPolicy rule for allowing the rememberChoice checkbox
|
||||
@ -37,6 +39,7 @@ export default {
|
||||
guestUsersCall,
|
||||
changeGuestPolicy,
|
||||
getGuestPolicy,
|
||||
isWaitingRoomEnabled,
|
||||
isGuestLobbyMessageEnabled,
|
||||
getGuestLobbyMessage,
|
||||
setGuestLobbyMessage,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user