Merge branch 'remove-react-router' of https://github.com/antobinary/bigbluebutton into post-router-style-fix

This commit is contained in:
KDSBrowne 2018-10-15 14:54:15 +00:00
commit 14066b4280
30 changed files with 1057 additions and 341 deletions

View File

@ -23,3 +23,4 @@ shell-server@0.3.0
http@1.3.0
dynamic-import@0.2.0
rocketchat:streamer
session

View File

@ -2,18 +2,33 @@
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import renderRoutes from '/imports/startup/client/routes';
import logger from '/imports/startup/client/logger';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
import { joinRouteHandler, authenticatedRouteHandler } from '/imports/startup/client/auth';
import Base from '/imports/startup/client/base';
import { Session } from 'meteor/session';
Meteor.startup(() => {
render(renderRoutes(), document.getElementById('app'));
render(<LoadingScreen />, document.getElementById('app'));
// Logs all uncaught exceptions to the client logger
window.addEventListener('error', (e) => {
const stack = e.error.stack;
const { stack } = e.error;
let message = e.error.toString();
// Checks if stack includes the message, if not add the two together.
(stack.includes(message)) ? message = stack : message += `\n${stack}`;
logger.error(message);
});
// TODO make this a Promise
joinRouteHandler((value, error) => {
authenticatedRouteHandler((valueInner, errorInner) => {
// set defaults
Session.set('isChatOpen', false);
Session.set('idChatOpen', '');
Session.set('isMeetingEnded', false);
render(<Base />, document.getElementById('app'));
});
});
});

View File

@ -28,7 +28,7 @@ export default class Acl {
authToken,
validated: true,
connectionStatus: 'online',
// TODO: We cant check for approved until we move subscription login out of <Base />
// TODO: We cant check for approved until we move subscription login out of <Base /> // TODO 4767
// approved: true,
});

View File

@ -4,17 +4,21 @@ import { setCustomLogoUrl } from '/imports/ui/components/user-list/service';
import { log, makeCall } from '/imports/ui/services/api';
import deviceInfo from '/imports/utils/deviceInfo';
import logger from '/imports/startup/client/logger';
import { Session } from 'meteor/session';
// disconnected and trying to open a new connection
const STATUS_CONNECTING = 'connecting';
const METADATA_KEY = 'metadata';
export function joinRouteHandler(nextState, replace, callback) {
const { sessionToken } = nextState.location.query;
export function joinRouteHandler(callback) {
const urlParams = new URLSearchParams(window.location.search);
const sessionToken = urlParams.get('sessionToken');
console.log('joinRouteHandler_2', sessionToken);
if (!nextState || !sessionToken) {
replace({ pathname: '/error/404' });
callback();
if (!sessionToken) {
Session.set('hasError', true);
Session.set('codeError', '404');
callback('failed - no sessionToken');
}
// Old credentials stored in memory were being used when joining a new meeting
@ -31,67 +35,70 @@ export function joinRouteHandler(nextState, replace, callback) {
externUserID, fullname, confname, customdata,
} = response;
console.log({ returncode });
if (returncode === 'FAILED') {
replace({ pathname: '/error/404' });
callback();
Session.set('hasError', true);
Session.set('codeError', '404');
callback('failed unhappily');
} else {
setCustomLogoUrl(customLogoURL);
let metakeys = 0;
if (metadata) {
metakeys = metadata.length
? metadata.reduce((acc, meta) => {
const key = Object.keys(meta).shift();
const handledHTML5Parameters = [
'html5autoswaplayout', 'html5autosharewebcam', 'html5hidepresentation',
];
if (handledHTML5Parameters.indexOf(key) === -1) {
return acc;
}
/* this reducer transforms array of objects in a single object and
forces the metadata a be boolean value */
let value = meta[key];
try {
value = JSON.parse(meta[key]);
} catch (e) {
log('error', `Caught: ${e.message}`);
}
return { ...acc, [key]: value };
}, {}) : {};
}
if (customdata.length) {
makeCall('addUserSettings', meetingID, internalUserID, customdata);
}
SessionStorage.setItem(METADATA_KEY, metakeys);
Auth.set(
meetingID, internalUserID, authToken, logoutUrl,
sessionToken, fullname, externUserID, confname,
);
Session.set('isUserListOpen', deviceInfo.type().isPhone);
const userInfo = window.navigator;
// Browser information is sent once on startup
// Sent here instead of Meteor.startup, as the
// user might not be validated by then, thus user's data
// would not be sent with this information
const clientInfo = {
language: userInfo.language,
userAgent: userInfo.userAgent,
screenSize: { width: window.screen.width, height: window.screen.height },
windowSize: { width: window.innerWidth, height: window.innerHeight },
bbbVersion: Meteor.settings.public.app.bbbServerVersion,
location: window.location.href,
};
logger.info(clientInfo);
callback('all is cool'); // TODO 4767
}
setCustomLogoUrl(customLogoURL);
const metakeys = metadata.length
? metadata.reduce((acc, meta) => {
const key = Object.keys(meta).shift();
const handledHTML5Parameters = [
'html5autoswaplayout', 'html5autosharewebcam', 'html5hidepresentation',
];
if (handledHTML5Parameters.indexOf(key) === -1) {
return acc;
}
/* this reducer transforms array of objects in a single object and
forces the metadata a be boolean value */
let value = meta[key];
try {
value = JSON.parse(meta[key]);
} catch (e) {
log('error', `Caught: ${e.message}`);
}
return { ...acc, [key]: value };
}, {}) : {};
if (customdata.length) {
makeCall('addUserSettings', meetingID, internalUserID, customdata);
}
SessionStorage.setItem(METADATA_KEY, metakeys);
Auth.set(
meetingID, internalUserID, authToken, logoutUrl,
sessionToken, fullname, externUserID, confname,
);
const path = deviceInfo.type().isPhone ? '/' : '/users';
const userInfo = window.navigator;
// Browser information is sent once on startup
// Sent here instead of Meteor.startup, as the
// user might not be validiated by then, thus user's data
// would not be sent with this information
const clientInfo = {
language: userInfo.language,
userAgent: userInfo.userAgent,
screenSize: { width: window.screen.width, height: window.screen.height },
windowSize: { width: window.innerWidth, height: window.innerHeight },
bbbVersion: Meteor.settings.public.app.bbbServerVersion,
location: window.location.href,
};
replace({ pathname: path });
logger.info(clientInfo);
return callback();
});
}
@ -138,7 +145,7 @@ function _addReconnectObservable() {
});
}
export function authenticatedRouteHandler(nextState, replace, callback) {
export function authenticatedRouteHandler(callback) {
if (Auth.loggedIn) {
callback();
}
@ -149,7 +156,8 @@ export function authenticatedRouteHandler(nextState, replace, callback) {
.then(callback)
.catch((reason) => {
log('error', reason);
replace({ pathname: `/error/${reason.error}` });
Session.set('hasError', true);
Session.set('codeError', reason.error);
callback();
});
}

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth';
import AppContainer from '/imports/ui/components/app/container';
@ -14,6 +13,7 @@ import Users from '/imports/api/users';
import Annotations from '/imports/api/annotations';
import AnnotationsLocal from '/imports/ui/components/whiteboard/service';
import GroupChat from '/imports/api/group-chat';
import { Session } from 'meteor/session';
import IntlStartup from './intl';
const CHAT_CONFIG = Meteor.settings.public.chat;
@ -21,20 +21,16 @@ const PUBLIC_GROUP_CHAT_ID = CHAT_CONFIG.public_group_id;
const PUBLIC_CHAT_TYPE = CHAT_CONFIG.type_public;
const propTypes = {
error: PropTypes.object,
errorCode: PropTypes.number,
subscriptionsReady: PropTypes.bool.isRequired,
locale: PropTypes.string,
endedCode: PropTypes.string,
approved: PropTypes.bool,
meetingEnded: PropTypes.bool,
};
const defaultProps = {
error: undefined,
errorCode: undefined,
locale: undefined,
endedCode: undefined,
approved: undefined,
meetingEnded: false,
};
class Base extends Component {
@ -43,11 +39,9 @@ class Base extends Component {
this.state = {
loading: false,
error: props.error || null,
};
this.updateLoadingState = this.updateLoadingState.bind(this);
this.updateErrorState = this.updateErrorState.bind(this);
}
componentWillUpdate() {
@ -63,29 +57,23 @@ class Base extends Component {
});
}
updateErrorState(error = null) {
this.setState({
error,
});
}
renderByState() {
const { updateLoadingState, updateErrorState } = this;
const stateControls = { updateLoadingState, updateErrorState };
const { updateLoadingState } = this;
const stateControls = { updateLoadingState };
const { loading, error } = this.state;
const { loading } = this.state;
const { subscriptionsReady, errorCode } = this.props;
const { endedCode } = this.props.params;
const codeError = Session.get('codeError');
const { subscriptionsReady, meetingEnded } = this.props;
if (endedCode) {
if (meetingEnded) {
AudioManager.exitAudio();
return (<MeetingEnded code={endedCode} />);
return (<MeetingEnded code={Session.get('codeError')} />);
}
if (error || errorCode) {
logger.error(`User could not log in HTML5, hit ${errorCode}`);
return (<ErrorScreen code={errorCode}>{error}</ErrorScreen>);
if (codeError) {
logger.error(`User could not log in HTML5, hit ${codeError}`);
return (<ErrorScreen code={codeError} />);
}
if (loading || !subscriptionsReady) {
@ -101,9 +89,9 @@ class Base extends Component {
}
render() {
const { updateLoadingState, updateErrorState } = this;
const { updateLoadingState } = this;
const { locale } = this.props;
const stateControls = { updateLoadingState, updateErrorState };
const stateControls = { updateLoadingState };
return (
<IntlStartup locale={locale} baseControls={stateControls}>
@ -122,9 +110,7 @@ const SUBSCRIPTIONS_NAME = [
'group-chat', 'presentation-pods', 'users-settings',
];
const BaseContainer = withRouter(withTracker(({ params, router }) => {
if (params.errorCode) return params;
const BaseContainer = withTracker(() => {
const { locale } = Settings.application;
const { credentials, loggedIn } = Auth;
const { meetingId, requesterUserId } = credentials;
@ -139,7 +125,8 @@ const BaseContainer = withRouter(withTracker(({ params, router }) => {
const subscriptionErrorHandler = {
onError: (error) => {
logger.error(error);
return router.push('/logout');
Session.set('isMeetingEnded', true);
Session.set('codeError', error.error);
},
};
@ -179,11 +166,12 @@ const BaseContainer = withRouter(withTracker(({ params, router }) => {
const subscriptionsReady = subscriptionsHandlers.every(handler => handler.ready());
return {
approved: Users.findOne({ userId: Auth.userID, approved: true, guest: true }),
meetingEnded: Session.get('isMeetingEnded'),
locale,
subscriptionsReady,
annotationsHandler,
groupChatMessageHandler,
};
})(Base));
})(Base);
export default BaseContainer;

View File

@ -1,57 +0,0 @@
import React from 'react';
import { Router, Route, Redirect, IndexRoute, useRouterHistory } from 'react-router';
import { createHistory } from 'history';
import LoadingScreen from '/imports/ui/components/loading-screen/component';
import ChatContainer from '/imports/ui/components/chat/container';
import UserListContainer from '/imports/ui/components/user-list/container';
import { joinRouteHandler, logoutRouteHandler, authenticatedRouteHandler } from './auth';
import Base from './base';
const browserHistory = useRouterHistory(createHistory)({
basename: Meteor.settings.public.app.basename,
});
const disconnect = () => {
Meteor.disconnect();
};
const renderRoutes = () => (
<Router history={browserHistory} >
<Route path="/logout" onEnter={logoutRouteHandler} />
<Route
path="/join"
component={LoadingScreen}
onEnter={joinRouteHandler}
/>
<Route path="/" component={Base} onEnter={authenticatedRouteHandler} >
<IndexRoute components={{}} />
<Route name="users" path="users" components={{ userList: UserListContainer }} />
<Route
name="chat"
path="users/chat/:chatID"
components={{
userList: UserListContainer,
chat: ChatContainer,
}}
/>
<Redirect from="users/chat" to="/users/chat/public" />
</Route>
<Route
name="meeting-ended"
path="/ended/:endedCode"
component={Base}
onEnter={disconnect}
onLeave={logoutRouteHandler}
/>
<Route
name="error"
path="/error/:errorCode"
component={Base}
onEnter={disconnect}
/>
<Redirect from="*" to="/error/404" />
</Router>
);
export default renderRoutes;

View File

@ -12,6 +12,8 @@ import NotificationsBarContainer from '../notifications-bar/container';
import AudioContainer from '../audio/container';
import ChatAlertContainer from '../chat/alert/container';
import { styles } from './styles';
import UserListContainer from '../user-list/container';
import ChatContainer from '../chat/container';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const USERLIST_COMPACT_WIDTH = 50;
@ -42,7 +44,7 @@ const propTypes = {
media: PropTypes.element,
actionsbar: PropTypes.element,
closedCaption: PropTypes.element,
userList: PropTypes.element,
userListIsOpen: PropTypes.bool.isRequired,
chat: PropTypes.element,
locale: PropTypes.string,
intl: intlShape.isRequired,
@ -55,7 +57,6 @@ const defaultProps = {
media: null,
actionsbar: null,
closedCaption: null,
userList: null,
chat: null,
locale: 'en',
};
@ -64,6 +65,8 @@ class App extends Component {
constructor() {
super();
console.log('1');
this.state = {
compactUserList: false,
enableResize: !window.matchMedia(MOBILE_MEDIA).matches,
@ -141,31 +144,33 @@ class App extends Component {
}
renderUserList() {
const { intl, chatIsOpen } = this.props;
let { userList } = this.props;
const {
intl, chatIsOpen, userListIsOpen,
} = this.props;
const { compactUserList } = this.state;
if (!userList) return null;
if (!userListIsOpen) return null;
const userListStyle = {};
userListStyle[styles.compact] = compactUserList;
userList = React.cloneElement(userList, {
compact: compactUserList,
});
// userList = React.cloneElement(userList, {
// compact: compactUserList, // TODO 4767
// });
return (
<div
className={cx(styles.userList, userListStyle)}
aria-label={intl.formatMessage(intlMessages.userListLabel)}
aria-hidden={chatIsOpen}
aria-hidden={chatIsOpen /* TODO 4767 -- Why is this not `userListIsOpen` */}
>
{userList}
<UserListContainer />
</div>
);
}
renderUserListResizable() {
const { userList } = this.props;
const { userListIsOpen } = this.props;
// Variables for resizing user-list.
const USERLIST_MIN_WIDTH_PX = 100;
@ -175,7 +180,7 @@ class App extends Component {
// decide whether using pixel or percentage unit as a default width for userList
const USERLIST_DEFAULT_WIDTH = (window.innerWidth * (USERLIST_DEFAULT_WIDTH_RELATIVE / 100.0)) < USERLIST_MAX_WIDTH_PX ? `${USERLIST_DEFAULT_WIDTH_RELATIVE}%` : USERLIST_MAX_WIDTH_PX;
if (!userList) return null;
if (!userListIsOpen) return null;
const resizableEnableOptions = {
top: false,
@ -209,29 +214,29 @@ class App extends Component {
}
renderChat() {
const { chat, intl } = this.props;
const { intl, chatIsOpen } = this.props;
if (!chat) return null;
if (!chatIsOpen) return null;
return (
<section
className={styles.chat}
aria-label={intl.formatMessage(intlMessages.chatLabel)}
>
{chat}
<ChatContainer />
</section>
);
}
renderChatResizable() {
const { chat } = this.props;
const { chatIsOpen } = this.props;
// Variables for resizing chat.
const CHAT_MIN_WIDTH = '10%';
const CHAT_MAX_WIDTH = '25%';
const CHAT_DEFAULT_WIDTH = '15%';
if (!chat) return null;
if (!chatIsOpen) return null;
const resizableEnableOptions = {
top: false,
@ -260,7 +265,7 @@ class App extends Component {
renderMedia() {
const {
media, intl, chatIsOpen, userlistIsOpen,
media, intl, chatIsOpen, userListIsOpen,
} = this.props;
if (!media) return null;
@ -269,7 +274,7 @@ class App extends Component {
<section
className={styles.media}
aria-label={intl.formatMessage(intlMessages.mediaLabel)}
aria-hidden={userlistIsOpen || chatIsOpen}
aria-hidden={userListIsOpen || chatIsOpen}
>
{media}
{this.renderClosedCaption()}
@ -279,7 +284,7 @@ class App extends Component {
renderActionsBar() {
const {
actionsbar, intl, userlistIsOpen, chatIsOpen,
actionsbar, intl, userListIsOpen, chatIsOpen,
} = this.props;
if (!actionsbar) return null;
@ -288,7 +293,7 @@ class App extends Component {
<section
className={styles.actionsbar}
aria-label={intl.formatMessage(intlMessages.actionsBarLabel)}
aria-hidden={userlistIsOpen || chatIsOpen}
aria-hidden={userListIsOpen || chatIsOpen}
>
{actionsbar}
</section>
@ -296,7 +301,7 @@ class App extends Component {
}
render() {
const { params, userlistIsOpen } = this.props;
const { params, userListIsOpen } = this.props;
const { enableResize } = this.state;
return (
@ -309,14 +314,14 @@ class App extends Component {
{this.renderActionsBar()}
</div>
{enableResize ? this.renderUserListResizable() : this.renderUserList()}
{userlistIsOpen && enableResize ? <div className={styles.userlistPad} /> : null}
{userListIsOpen && enableResize ? <div className={styles.userlistPad} /> : null}
{enableResize ? this.renderChatResizable() : this.renderChat()}
{this.renderSidebar()}
</section>
<ModalContainer />
<AudioContainer />
<ToastContainer />
<ChatAlertContainer currentChatID={params.chatID} />
{/* <ChatAlertContainer currentChatID={params.chatID} /> TODO 4767 */}
</main>
);
}

View File

@ -1,6 +1,5 @@
import React, { cloneElement } from 'react';
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
import Auth from '/imports/ui/services/auth';
@ -28,7 +27,6 @@ const propTypes = {
navbar: PropTypes.node,
actionsbar: PropTypes.node,
media: PropTypes.node,
location: PropTypes.shape({}).isRequired,
};
const defaultProps = {
@ -45,7 +43,6 @@ const intlMessages = defineMessages({
});
const AppContainer = (props) => {
// inject location on the navbar container
const {
navbar,
actionsbar,
@ -53,11 +50,9 @@ const AppContainer = (props) => {
...otherProps
} = props;
const navbarWithLocation = cloneElement(navbar, { location: props.location });
return (
<App
navbar={navbarWithLocation}
navbar={navbar}
actionsbar={actionsbar}
media={media}
{...otherProps}
@ -66,7 +61,7 @@ const AppContainer = (props) => {
};
export default withRouter(injectIntl(withModalMounter(withTracker(({ router, intl, baseControls }) => {
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
const currentUser = Users.findOne({ userId: Auth.userID });
const isMeetingBreakout = meetingIsBreakout();
@ -82,7 +77,8 @@ export default withRouter(injectIntl(withModalMounter(withTracker(({ router, int
const hasNewConnection = 'connectionId' in fields && (fields.connectionId !== Meteor.connection._lastSessionId);
if (fields.ejected || hasNewConnection) {
router.push(`/ended/${403}`);
Session.set('codeError', '403');
Session.set('isMeetingEnded', true);
}
},
});
@ -93,7 +89,8 @@ export default withRouter(injectIntl(withModalMounter(withTracker(({ router, int
if (isMeetingBreakout) {
Auth.clearCredentials().then(window.close);
} else {
router.push(`/ended/${410}`);
Session.set('codeError', '410');
Session.set('isMeetingEnded', true);
}
},
});
@ -108,10 +105,10 @@ export default withRouter(injectIntl(withModalMounter(withTracker(({ router, int
return {
closedCaption: getCaptionsStatus() ? <ClosedCaptionsContainer /> : null,
fontSize: getFontSize(),
userlistIsOpen: window.location.pathname.includes('users'),
chatIsOpen: window.location.pathname.includes('chat'),
userListIsOpen: Boolean(Session.get('isUserListOpen')),
chatIsOpen: Boolean(Session.get('isChatOpen')),
};
})(AppContainer))));
})(AppContainer)));
AppContainer.defaultProps = defaultProps;
AppContainer.propTypes = propTypes;

View File

@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import injectNotify from '/imports/ui/components/toast/inject-notify/component';
import { Link } from 'react-router';
import { styles } from '../../styles.scss';
const ALERT_INTERVAL = 2000; // 2 seconds
@ -15,11 +14,11 @@ const propTypes = {
class ChatPushAlert extends React.Component {
static link(message, chatId) {
return (
<Link className={styles.link} to={`/users/chat/${chatId}`}>
{message}
</Link>
);
// return (
// {/*<Link className={styles.link} to={`/users/chat/${chatId}`}>*/}
// {/*{message}*/}
// {/*</Link> // TODO 4767 */}
// );
}
constructor(props) {

View File

@ -1,9 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { defineMessages, injectIntl } from 'react-intl';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import Button from '/imports/ui/components/button/component';
import { Session } from 'meteor/session';
import { styles } from './styles';
import MessageForm from './message-form/component';
import MessageList from './message-list/component';
@ -54,33 +54,33 @@ const Chat = (props) => {
data-test="chatTitle"
className={styles.title}
>
<Link
to="/users"
role="button"
<Button
onClick={() => {
Session.set('isChatOpen', false);
alert('A');
}}
aria-label={intl.formatMessage(intlMessages.hideChatLabel, { 0: title })}
accessKey={HIDE_CHAT_AK}
>
<Icon iconName="left_arrow" /> {title}
</Link>
label={title}
icon="left_arrow"
/>
</div>
{
chatID !== 'public' ?
<Link
to="/users"
role="button"
tabIndex={-1}
>
<Button
className={styles.closeBtn}
icon="close"
size="md"
hideLabel
onClick={() => actions.handleClosePrivateChat(chatID)}
aria-label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
accessKey={CLOSE_CHAT_AK}
/>
</Link> :
<Button
className={styles.closeBtn}
icon="close"
size="md"
hideLabel
onClick={() => {
actions.handleClosePrivateChat(chatID);
Session.set('idChatOpen', '');
}}
aria-label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
label={intl.formatMessage(intlMessages.closeChatLabel, { 0: title })}
accessKey={CLOSE_CHAT_AK}
/>
:
<ChatDropdown />
}
</header>

View File

@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import { withTracker } from 'meteor/react-meteor-data';
import { Session } from 'meteor/session';
import Chat from './component';
import ChatService from './service';
@ -30,7 +31,7 @@ const intlMessages = defineMessages({
class ChatContainer extends Component {
componentDidMount() {
// in case of reopening a chat, need to make sure it's removed from closed list
ChatService.removeFromClosedChatsSession(this.props.chatID);
ChatService.removeFromClosedChatsSession(this.props.chatID); // TODO 4767
}
render() {
return (
@ -41,9 +42,8 @@ class ChatContainer extends Component {
}
}
export default injectIntl(withTracker(({ params, intl }) => {
const chatID = params.chatID || PUBLIC_CHAT_KEY;
export default injectIntl(withTracker(({ intl }) => {
const chatID = Session.get('idChatOpen') || PUBLIC_CHAT_KEY;
let messages = [];
let isChatLocked = ChatService.isChatLocked(chatID);
let title = intl.formatMessage(intlMessages.titlePublic);

View File

@ -39,7 +39,7 @@ class MessageList extends Component {
const { scrollArea } = this;
this.setState({
scrollArea: this.scrollArea
scrollArea: this.scrollArea,
});
this.scrollTo(this.props.scrollPosition);
@ -60,7 +60,7 @@ class MessageList extends Component {
partnerIsLoggedOut,
} = this.props;
if(!this.state.scrollArea && nextState.scrollArea) return true;
if (!this.state.scrollArea && nextState.scrollArea) return true;
const switchingCorrespondent = chatId !== nextProps.chatId;
const hasNewUnreadMessages = hasUnreadMessages !== nextProps.hasUnreadMessages;
@ -164,14 +164,16 @@ class MessageList extends Component {
}
render() {
const { messages, intl } = this.props;
const {
messages, intl, id, lastReadMessageTime, handleReadMessage,
} = this.props;
const isEmpty = messages.length === 0;
return (
<div className={styles.messageListWrapper}>
<div
role="log"
ref={(ref) => { if (ref != null) { this.scrollArea = ref; } }}
id={this.props.id}
id={id}
className={styles.messageList}
aria-live="polite"
aria-atomic="false"
@ -180,14 +182,14 @@ class MessageList extends Component {
>
{messages.map(message => (
<MessageListItem
handleReadMessage={this.props.handleReadMessage}
handleReadMessage={handleReadMessage}
className={styles.messageListItem}
key={message.id}
messages={message.content}
user={message.sender}
time={message.time}
chatAreaId={this.props.id}
lastReadMessageTime={this.props.lastReadMessageTime}
chatAreaId={id}
lastReadMessageTime={lastReadMessageTime}
scrollArea={this.state.scrollArea}
/>
))}

View File

@ -11,7 +11,7 @@ const propTypes = {
/* We should recheck this proptype, sometimes we need to create an container and send to dropdown,
but with this */
// proptype, is not possible.
children: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
children: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => { // TODO 4767
if (propValue[key].type !== ListItem &&
propValue[key].type !== ListSeparator &&
propValue[key].type !== ListTitle) {

View File

@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import { withRouter } from 'react-router';
import { styles } from './styles';
const intlMessages = defineMessages({
@ -37,7 +36,9 @@ const defaultProps = {
class ErrorScreen extends React.PureComponent {
render() {
const {
intl, code, children, router,
intl,
code,
children,
} = this.props;
let formatedMessage = intl.formatMessage(intlMessages[defaultProps.code]);
@ -60,7 +61,7 @@ class ErrorScreen extends React.PureComponent {
<div className={styles.content}>
<Button
size="sm"
onClick={() => router.push('/logout/')}
onClick={() => Session.set('isMeetingEnded', true)}
label={intl.formatMessage(intlMessages.leave)}
/>
</div>
@ -69,7 +70,7 @@ class ErrorScreen extends React.PureComponent {
}
}
export default withRouter(injectIntl(ErrorScreen));
export default injectIntl(ErrorScreen);
ErrorScreen.propTypes = propTypes;
ErrorScreen.defaultProps = defaultProps;

View File

@ -7,6 +7,7 @@ import { styles } from './styles';
const propTypes = {
handleEndMeeting: PropTypes.func.isRequired,
confirmLeaving: PropTypes.func.isRequired,
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
@ -55,15 +56,14 @@ const intlMessages = defineMessages({
const LeaveConfirmation = ({
intl,
router,
handleEndMeeting,
showEndMeeting,
showFeedback,
confirmLeaving,
}) => (
<Modal
title={intl.formatMessage(intlMessages.title)}
confirm={{
callback: showFeedback,
callback: confirmLeaving,
label: intl.formatMessage(intlMessages.confirmLabel),
description: intl.formatMessage(intlMessages.confirmDesc),
}}
@ -79,7 +79,7 @@ const LeaveConfirmation = ({
className={styles.endMeeting}
label={intl.formatMessage(intlMessages.endMeetingLabel)}
onClick={handleEndMeeting}
aria-describedby={'modalEndMeetingDesc'}
aria-describedby="modalEndMeetingDesc"
/> : null
}
<div id="modalEndMeetingDesc" hidden>{intl.formatMessage(intlMessages.endMeetingDesc)}</div>

View File

@ -1,8 +1,8 @@
import React from 'react';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import getFromUserSettings from '/imports/ui/services/users-settings';
import { Session } from 'meteor/session';
import LogoutConfirmation from './component';
import {
isModerator,
@ -13,14 +13,15 @@ const LogoutConfirmationContainer = props => (
<LogoutConfirmation {...props} />
);
export default withRouter(withTracker(({ router }) => {
const APP_CONFIG = Meteor.settings.public.app;
const shouldShowFeedback = !meetingIsBreakout() && getFromUserSettings('askForFeedbackOnLogout', APP_CONFIG.askForFeedbackOnLogout);
const showFeedback = shouldShowFeedback ? () => router.push('/ended/430') : () => router.push('/logout');
export default withTracker(() => {
const confirmLeaving = () => {
Session.set('isMeetingEnded', true);
Session.set('codeError', '430');
};
return {
showEndMeeting: !meetingIsBreakout() && isModerator(),
handleEndMeeting: endMeeting,
showFeedback,
confirmLeaving,
};
})(LogoutConfirmationContainer));
})(LogoutConfirmationContainer);

View File

@ -1,10 +1,10 @@
import React from 'react';
import { withRouter } from 'react-router';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import Auth from '/imports/ui/services/auth';
import Button from '/imports/ui/components/button/component';
import getFromUserSettings from '/imports/ui/services/users-settings';
import { logoutRouteHandler } from '/imports/startup/client/auth';
import Rating from './rating/component';
import { styles } from './styles';
@ -64,9 +64,6 @@ const propTypes = {
formatMessage: PropTypes.func.isRequired,
}).isRequired,
code: PropTypes.string.isRequired,
router: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
};
class MeetingEnded extends React.PureComponent {
@ -96,12 +93,8 @@ class MeetingEnded extends React.PureComponent {
selected,
} = this.state;
const {
router,
} = this.props;
if (selected <= 0) {
router.push('/logout');
logoutRouteHandler();
return;
}
@ -122,7 +115,9 @@ class MeetingEnded extends React.PureComponent {
};
fetch(url, options)
.finally(() => router.push('/logout'));
.finally(() => {
logoutRouteHandler();
});
}
render() {
@ -173,4 +168,4 @@ class MeetingEnded extends React.PureComponent {
MeetingEnded.propTypes = propTypes;
export default injectIntl(withRouter(MeetingEnded));
export default injectIntl(MeetingEnded);

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import { Session } from 'meteor/session';
import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth';
import { meetingIsBreakout } from '/imports/ui/components/app/service';
@ -20,7 +20,7 @@ const NavBarContainer = ({ children, ...props }) => (
</NavBar>
);
export default withRouter(withTracker(({ location, router }) => {
export default withTracker(() => {
const CLIENT_TITLE = getFromUserSettings('clientTitle', PUBLIC_CONFIG.app.clientTitle);
let meetingTitle;
@ -54,7 +54,7 @@ export default withRouter(withTracker(({ location, router }) => {
const breakouts = Service.getBreakouts();
const currentUserId = Auth.userID;
const isExpanded = location.pathname.indexOf('/users') !== -1;
const isExpanded = Boolean(Session.get('isUserListOpen'));
return {
isExpanded,
@ -66,11 +66,7 @@ export default withRouter(withTracker(({ location, router }) => {
isBreakoutRoom: meetingIsBreakout(),
beingRecorded: meetingRecorded,
toggleUserList: () => {
if (location.pathname.indexOf('/users') !== -1) {
router.push('/');
} else {
router.push('/users');
}
Session.set('isUserListOpen', !isExpanded);
},
};
})(NavBarContainer));
})(NavBarContainer);

View File

@ -208,8 +208,6 @@ class Settings extends Component {
render() {
const {
intl,
router,
location,
mountModal,
} = this.props;
@ -219,7 +217,7 @@ class Settings extends Component {
confirm={{
callback: () => {
this.updateSettings(this.state.current);
router.push(location.pathname);
// router.push(location.pathname); // TODO 4767
/* We need to use mountModal(null) here to prevent submenu state updates,
* from re-opening the modal.
*/

View File

@ -1,6 +1,5 @@
import React from 'react';
import { withTracker } from 'meteor/react-meteor-data';
import { withRouter } from 'react-router';
import SettingsService from '/imports/ui/services/settings';
import Settings from './component';
@ -15,7 +14,7 @@ const SettingsContainer = props => (
<Settings {...props} />
);
export default withRouter(withTracker(() => ({
export default withTracker(() => ({
audio: SettingsService.audio,
dataSaving: SettingsService.dataSaving,
application: SettingsService.application,
@ -25,4 +24,4 @@ export default withRouter(withTracker(() => ({
locales: getClosedCaptionLocales(),
availableLocales: getAvailableLocales(),
isModerator: getUserRoles() === 'MODERATOR',
}))(SettingsContainer));
}))(SettingsContainer);

View File

@ -1,12 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withRouter, Link } from 'react-router';
import cx from 'classnames';
import { defineMessages, injectIntl } from 'react-intl';
import { styles } from './styles';
import ChatAvatar from './chat-avatar/component';
import ChatIcon from './chat-icon/component';
import ChatUnreadCounter from './chat-unread-messages/component';
import Button from '../../button/component';
const intlMessages = defineMessages({
titlePublic: {
@ -23,10 +23,6 @@ const intlMessages = defineMessages({
},
});
const CHAT_CONFIG = Meteor.settings.public.chat;
const PRIVATE_CHAT_PATH = CHAT_CONFIG.path_route;
const CLOSED_CHAT_PATH = 'users/';
const SHORTCUTS_CONFIG = Meteor.settings.public.app.shortcuts;
const TOGGLE_CHAT_PUB_AK = SHORTCUTS_CONFIG.togglePublicChat.accesskey;
@ -57,25 +53,28 @@ const ChatListItem = (props) => {
intl,
tabIndex,
isPublicChat,
location,
} = props;
let linkPath = [PRIVATE_CHAT_PATH, chat.id].join('');
linkPath = location.pathname.includes(linkPath) ? CLOSED_CHAT_PATH : linkPath;
const isCurrentChat = chat.id === openChat;
const linkClasses = {};
linkClasses[styles.active] = isCurrentChat;
return (
<Link
data-test="publicChatLink"
to={linkPath}
<Button
className={cx(styles.chatListItem, linkClasses)}
role="button"
aria-expanded={isCurrentChat}
tabIndex={tabIndex}
accessKey={isPublicChat(chat) ? TOGGLE_CHAT_PUB_AK : null}
color="primary"
onClick={() => {
Session.set('isChatOpen', true);
Session.set('idChatOpen', chat.id);
}}
id="chat-toggle-button"
// label={"~"}
label={isPublicChat(chat) ? intl.formatMessage(intlMessages.titlePublic) : chat.name}
>
<div className={styles.chatListItemLink}>
<div className={styles.chatIcon}>
{chat.icon ?
@ -90,7 +89,8 @@ const ChatListItem = (props) => {
<div className={styles.chatName}>
{!compact ?
<span className={styles.chatNameMain}>
{isPublicChat(chat) ? intl.formatMessage(intlMessages.titlePublic) : chat.name}
{isPublicChat(chat) ?
intl.formatMessage(intlMessages.titlePublic) : chat.name}
</span> : null}
</div>
{(chat.unreadCounter > 0) ?
@ -99,11 +99,11 @@ const ChatListItem = (props) => {
/>
: null}
</div>
</Link>
</Button>
);
};
ChatListItem.propTypes = propTypes;
ChatListItem.defaultProps = defaultProps;
export default withRouter(injectIntl(ChatListItem));
export default injectIntl(ChatListItem);

View File

@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import { styles } from './styles';
import CustomLogo from './custom-logo/component';
@ -40,10 +39,6 @@ const defaultProps = {
};
class UserList extends Component {
constructor(props) {
super(props);
}
render() {
const {
intl,
@ -113,4 +108,4 @@ class UserList extends Component {
UserList.propTypes = propTypes;
UserList.defaultProps = defaultProps;
export default withRouter(injectWbResizeEvent(injectIntl(UserList)));
export default injectWbResizeEvent(injectIntl(UserList));

View File

@ -258,7 +258,7 @@ const getOpenChats = (chatID) => {
const isVoiceOnlyUser = userId => userId.toString().startsWith('v_');
const getAvailableActions = (currentUser, user, router, isBreakoutRoom) => {
const getAvailableActions = (currentUser, user, isBreakoutRoom) => {
const isDialInUser = isVoiceOnlyUser(user.id) || user.isPhoneUser;
const hasAuthority = currentUser.isModerator || user.isCurrent;

View File

@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import { injectIntl } from 'react-intl';
import UserDropdown from './user-dropdown/component';
@ -21,7 +20,6 @@ const propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
router: PropTypes.shape({}).isRequired,
isBreakoutRoom: PropTypes.bool,
getAvailableActions: PropTypes.func.isRequired,
meeting: PropTypes.shape({}).isRequired,
@ -50,7 +48,6 @@ class UserListItem extends Component {
changeRole,
setEmojiStatus,
currentUser,
router,
isBreakoutRoom,
getAvailableActions,
handleEmojiChange,
@ -73,7 +70,6 @@ class UserListItem extends Component {
changeRole,
setEmojiStatus,
currentUser,
router,
isBreakoutRoom,
getAvailableActions,
handleEmojiChange,
@ -89,4 +85,4 @@ class UserListItem extends Component {
UserListItem.propTypes = propTypes;
UserListItem.defaultProps = defaultProps;
export default withRouter(injectIntl(UserListItem));
export default injectIntl(UserListItem);

View File

@ -14,6 +14,7 @@ import _ from 'lodash';
import { styles } from './styles';
import UserName from './../user-name/component';
import UserIcons from './../user-icons/component';
import { Session } from 'meteor/session';
const messages = defineMessages({
presenter: {
@ -169,7 +170,6 @@ class UserDropdown extends Component {
intl,
currentUser,
user,
router,
isBreakoutRoom,
getAvailableActions,
handleEmojiChange,
@ -181,7 +181,7 @@ class UserDropdown extends Component {
changeRole,
} = this.props;
const actionPermissions = getAvailableActions(currentUser, user, router, isBreakoutRoom);
const actionPermissions = getAvailableActions(currentUser, user, isBreakoutRoom);
const actions = [];
const {
@ -233,7 +233,10 @@ class UserDropdown extends Component {
actions.push(this.makeDropdownItem(
'openChat',
intl.formatMessage(messages.ChatLabel),
() => this.onActionsHide(router.push(`/users/chat/${user.id}`)),
() => {
Session.set('idChatOpen', user.id);
console.error(`__ idChatOpen: ${Session.get('idChatOpen')}`);
},
'chat',
));
}

View File

@ -50,13 +50,13 @@ export function log(type = 'error', message, ...args) {
screenSize: { width: window.screen.width, height: window.screen.height },
windowSize: { width: window.innerWidth, height: window.innerHeight },
bbbVersion: Meteor.settings.public.app.bbbServerVersion,
location: window.location.href,
location: window.location.href, // TODO 4767
};
const logContents = { ...args };
const topic = logContents[0] ? logContents[0].topic : null;
const messageOrStack = message.stack || message.message || JSON.stringify(message);
console.debug(`CLIENT LOG (${topic ? type.toUpperCase() + '.' + topic : type.toUpperCase()}): `, messageOrStack, ...args);
console.debug(`CLIENT LOG (${topic ? `${type.toUpperCase()}.${topic}` : type.toUpperCase()}): `, messageOrStack, ...args);
Meteor.call('logClient', type, messageOrStack, {
clientInfo,

View File

@ -755,16 +755,6 @@
"readable-stream": "2.3.6"
}
},
"create-react-class": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz",
"integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=",
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1"
}
},
"cross-spawn": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
@ -1911,11 +1901,6 @@
"warning": "3.0.0"
}
},
"hoist-non-react-statics": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
"integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs="
},
"hosted-git-info": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
@ -4646,20 +4631,6 @@
"react-dom": "16.0.1"
}
},
"react-router": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-3.0.5.tgz",
"integrity": "sha1-w7eHN1gEWou8lWKu9P9LyMznwTY=",
"requires": {
"create-react-class": "15.6.2",
"history": "3.3.0",
"hoist-non-react-statics": "1.2.0",
"invariant": "2.2.2",
"loose-envify": "1.3.1",
"prop-types": "15.6.2",
"warning": "3.0.0"
}
},
"react-tabs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-tabs/-/react-tabs-2.1.1.tgz",

View File

@ -58,7 +58,6 @@
"react-intl": "~2.4.0",
"react-modal": "~3.0.4",
"react-render-in-browser": "^1.0.0",
"react-router": "~3.0.2",
"react-tabs": "~2.1.0",
"react-toastify": "~2.1.2",
"react-toggle": "~4.0.2",

View File

@ -0,0 +1,402 @@
{
"public": {
"app": {
"mobileFont": 16,
"desktopFont": 14,
"audioChatNotification": false,
"autoJoin": true,
"listenOnlyMode": true,
"forceListenOnly": false,
"skipCheck": false,
"clientTitle": "BigBlueButton",
"appName": "BigBlueButton HTML5 Client",
"bbbServerVersion": "2.0-rc",
"copyright": "©2018 BigBlueButton Inc.",
"html5ClientBuild": "HTML5_CLIENT_VERSION",
"lockOnJoin": true,
"basename": "/html5client",
"askForFeedbackOnLogout": false,
"defaultSettings": {
"application": {
"chatAudioAlerts": false,
"chatPushAlerts": false,
"fontSize": "16px",
"fallbackLocale": "en"
},
"audio": {
"inputDeviceId": "undefined",
"outputDeviceId": "undefined"
},
"dataSaving": {
"viewParticipantsWebcams": true,
"viewScreenshare": true
},
"cc": {
"backgroundColor": "#FFFFFF",
"fontColor": "#000000",
"enabled": false,
"fontFamily": "Calibri",
"fontSize": "16px",
"takeOwnership": false
},
"participants": {
"muteAll": false,
"lockAll": false,
"microphone": false,
"publicChat": false,
"privateChat": false,
"layout": false
}
},
"shortcuts": {
"openOptions": {"accesskey": "O", "descId": "openOptions"},
"toggleUserList": {"accesskey": "U", "descId": "toggleUserList"},
"toggleMute": {"accesskey": "M", "descId": "toggleMute"},
"joinAudio": {"accesskey": "J", "descId": "joinAudio"},
"leaveAudio": {"accesskey": "L", "descId": "leaveAudio"},
"togglePublicChat": {"accesskey": "P", "descId": "togglePublicChat"},
"hidePrivateChat": {"accesskey": "H", "descId": "hidePrivateChat"},
"closePrivateChat": {"accesskey": "G", "descId": "closePrivateChat"},
"openActions": {"accesskey": "A", "descId": "openActions"},
"openStatus": {"accesskey": "S", "descId": "openStatus"}
},
"branding": {
"displayBrandingArea": false
},
"allowHTML5Moderator": true,
"allowModeratorToUnmuteAudio": true,
"httpsConnection": false,
"connectionTimeout": 60000,
"showHelpButton": true
},
"kurento": {
"wsUrl": "HOST",
"chromeDefaultExtensionKey": "akgoaoikmbmhcopjgakkcepdgdgkjfbc",
"chromeDefaultExtensionLink": "https://chrome.google.com/webstore/detail/bigbluebutton-screenshare/akgoaoikmbmhcopjgakkcepdgdgkjfbc",
"chromeExtensionKey": "KEY",
"chromeExtensionLink": "LINK",
"chromeScreenshareSources": ["window", "screen"],
"firefoxScreenshareSource": "window",
"cameraConstraints": {
"width": {
"max": 640
},
"height": {
"max": 480
}
},
"enableScreensharing": false,
"enableVideo": false,
"enableVideoStats": false,
"enableListenOnly": false
},
"acl": {
"viewer": {
"subscriptions": [
"users",
"cursor",
"screenshare",
"meetings",
"polls",
"chat",
"presentations",
"annotations",
"slides",
"captions",
"breakouts",
"voiceUsers",
"whiteboard-multi-user",
"presentation-pods",
"group-chat",
"group-chat-msg",
"users-settings"
],
"methods": [
"listenOnlyToggle",
"userLogout",
"setEmojiStatus",
"toggleSelfVoice",
"publishVote",
"sendChat",
"createGroupChat",
"destroyGroupChat",
"sendGroupChatMsg"
]
},
"moderator": {
"methods": [
"assignPresenter",
"removeUser",
"muteUser",
"unmuteUser",
"endMeeting",
"toggleVoice",
"clearPublicChatHistory",
"changeRole",
"ejectUserFromVoice",
"toggleRecording"
]
},
"presenter": {
"methods": [
"assignPresenter",
"switchSlide",
"modifyWhiteboardAccess",
"undoAnnotation",
"clearWhiteboard",
"moveCursor",
"sendAnnotation",
"removePresentation",
"setPresentation",
"zoomSlide",
"requestPresentationUploadToken"
]
}
},
"chat": {
"min_message_length": 1,
"max_message_length": 5000,
"grouping_messages_window": 10000,
"type_system": "SYSTEM_MESSAGE",
"type_public": "PUBLIC_ACCESS",
"type_private": "PRIVATE_ACCESS",
"system_userid": "SYSTEM_MESSAGE",
"system_username": "SYSTEM_MESSAGE",
"public_id": "public",
"public_group_id": "MAIN-PUBLIC-GROUP-CHAT",
"public_userid": "public_chat_userid",
"public_username": "public_chat_username",
"storage_key": "UNREAD_CHATS",
"system_messages_keys": {
"chat_clear": "PUBLIC_CHAT_CLEAR"
}
},
"media": {
"WebRTCHangupRetryInterval": 2000,
"vertoServerAddress": "HOST",
"freeswitchProfilePassword": "1234",
"vertoPort": "8082",
"useSIPAudio": true,
"stunTurnServersFetchAddress": "/bigbluebutton/api/stuns",
"mediaTag": "#remote-media",
"callTransferTimeout": 5000,
"callHangupTimeout": 2000,
"callHangupMaximumRetries": 10,
"echoTestNumber": "9196"
},
"presentation": {
"defaultPresentationFile": "default.pdf",
"uploadEndpoint": "/bigbluebutton/presentation/upload",
"uploadSizeMin": 0,
"uploadSizeMax": 50000000,
"uploadValidMimeTypes": [
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.oasis.opendocument.text",
"application/rtf",
"text/plain",
"application/vnd.oasis.opendocument.spreadsheet",
"application/vnd.oasis.opendocument.presentation",
"application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.graphics",
"application/vnd.oasis.opendocument.chart",
"application/vnd.oasis.opendocument.image",
"application/pdf",
"image/jpeg",
"image/png"
]
},
"user": {
"role_moderator": "MODERATOR",
"role_viewer": "VIEWER",
"role_presenter": "PRESENTER"
},
"whiteboard": {
"annotations": {
"status": {
"start": "DRAW_START",
"update": "DRAW_UPDATE",
"end": "DRAW_END"
}
},
"toolbar": {
"colors": [
{
"label": "black",
"value": "#000000"
},
{
"label": "white",
"value": "#ffffff"
},
{
"label": "red",
"value": "#ff0000"
},
{
"label": "orange",
"value": "#ff8800"
},
{
"label": "eletricLime",
"value": "#ccff00"
},
{
"label": "Lime",
"value": "#00ff00"
},
{
"label": "Cyan",
"value": "#00ffff"
},
{
"label": "dodgerBlue",
"value": "#0088ff"
},
{
"label": "blue",
"value": "#0000ff"
},
{
"label": "violet",
"value": "#8800ff"
},
{
"label": "magenta",
"value": "#ff00ff"
},
{
"label": "silver",
"value": "#c0c0c0"
}
],
"thickness": [
{
"value": 14
},
{
"value": 12
},
{
"value": 10
},
{
"value": 8
},
{
"value": 6
},
{
"value": 4
},
{
"value": 2
},
{
"value": 1
}
],
"font_sizes": [
{
"value": 36
},
{
"value": 32
},
{
"value": 28
},
{
"value": 24
},
{
"value": 20
},
{
"value": 16
}
],
"tools": [
{
"icon": "text_tool",
"value": "text"
},
{
"icon": "line_tool",
"value": "line"
},
{
"icon": "circle_tool",
"value": "ellipse"
},
{
"icon": "triangle_tool",
"value": "triangle"
},
{
"icon": "rectangle_tool",
"value": "rectangle"
},
{
"icon": "pen_tool",
"value": "pencil"
},
{
"icon": "hand",
"value": "hand"
}
]
}
},
"clientLog": {
"server": { "enabled": true, "level": "info" },
"console": { "enabled": true, "level": "debug" },
"external": { "enabled": true, "level": "info", "url": "https://LOG_HOST/html5Log", "method": "POST" }
}
},
"private": {
"app": {
"captionsChunkLength": 1000,
"pencilChunkLength": 100
},
"redis": {
"host": "127.0.0.1",
"post": "6379",
"timeout": 5000,
"debug": true,
"channels": {
"toAkkaApps": "to-akka-apps-redis-channel"
},
"subscribeTo": [
"to-html5-redis-channel",
"from-akka-apps-*"
],
"async": [
"from-akka-apps-wb-redis-channel"
],
"ignored": [
"CheckAlivePongSysMsg",
"DoLatencyTracerMsg"
]
},
"serverLog": {
"level": "info"
}
}
}

View File

@ -0,0 +1,402 @@
{
"public": {
"app": {
"mobileFont": 16,
"desktopFont": 14,
"audioChatNotification": false,
"autoJoin": true,
"listenOnlyMode": true,
"forceListenOnly": false,
"skipCheck": false,
"clientTitle": "BigBlueButton",
"appName": "BigBlueButton HTML5 Client",
"bbbServerVersion": "2.0-rc",
"copyright": "©2018 BigBlueButton Inc.",
"html5ClientBuild": "HTML5_CLIENT_VERSION",
"lockOnJoin": true,
"basename": "/html5client",
"askForFeedbackOnLogout": false,
"defaultSettings": {
"application": {
"chatAudioAlerts": false,
"chatPushAlerts": false,
"fontSize": "16px",
"fallbackLocale": "en"
},
"audio": {
"inputDeviceId": "undefined",
"outputDeviceId": "undefined"
},
"dataSaving": {
"viewParticipantsWebcams": true,
"viewScreenshare": true
},
"cc": {
"backgroundColor": "#FFFFFF",
"fontColor": "#000000",
"enabled": false,
"fontFamily": "Calibri",
"fontSize": "16px",
"takeOwnership": false
},
"participants": {
"muteAll": false,
"lockAll": false,
"microphone": false,
"publicChat": false,
"privateChat": false,
"layout": false
}
},
"shortcuts": {
"openOptions": {"accesskey": "O", "descId": "openOptions"},
"toggleUserList": {"accesskey": "U", "descId": "toggleUserList"},
"toggleMute": {"accesskey": "M", "descId": "toggleMute"},
"joinAudio": {"accesskey": "J", "descId": "joinAudio"},
"leaveAudio": {"accesskey": "L", "descId": "leaveAudio"},
"togglePublicChat": {"accesskey": "P", "descId": "togglePublicChat"},
"hidePrivateChat": {"accesskey": "H", "descId": "hidePrivateChat"},
"closePrivateChat": {"accesskey": "G", "descId": "closePrivateChat"},
"openActions": {"accesskey": "A", "descId": "openActions"},
"openStatus": {"accesskey": "S", "descId": "openStatus"}
},
"branding": {
"displayBrandingArea": false
},
"allowHTML5Moderator": true,
"allowModeratorToUnmuteAudio": true,
"httpsConnection": true,
"connectionTimeout": 10000,
"showHelpButton": true
},
"kurento": {
"wsUrl": "HOST",
"chromeDefaultExtensionKey": "akgoaoikmbmhcopjgakkcepdgdgkjfbc",
"chromeDefaultExtensionLink": "https://chrome.google.com/webstore/detail/bigbluebutton-screenshare/akgoaoikmbmhcopjgakkcepdgdgkjfbc",
"chromeExtensionKey": "KEY",
"chromeExtensionLink": "LINK",
"chromeScreenshareSources": ["window", "screen"],
"firefoxScreenshareSource": "window",
"cameraConstraints": {
"width": {
"max": 640
},
"height": {
"max": 480
}
},
"enableScreensharing": false,
"enableVideo": false,
"enableVideoStats": false,
"enableListenOnly": false
},
"acl": {
"viewer": {
"subscriptions": [
"users",
"cursor",
"screenshare",
"meetings",
"polls",
"chat",
"presentations",
"annotations",
"slides",
"captions",
"breakouts",
"voiceUsers",
"whiteboard-multi-user",
"presentation-pods",
"group-chat",
"group-chat-msg",
"users-settings"
],
"methods": [
"listenOnlyToggle",
"userLogout",
"setEmojiStatus",
"toggleSelfVoice",
"publishVote",
"sendChat",
"createGroupChat",
"destroyGroupChat",
"sendGroupChatMsg"
]
},
"moderator": {
"methods": [
"assignPresenter",
"removeUser",
"muteUser",
"unmuteUser",
"endMeeting",
"toggleVoice",
"clearPublicChatHistory",
"changeRole",
"ejectUserFromVoice",
"toggleRecording"
]
},
"presenter": {
"methods": [
"assignPresenter",
"switchSlide",
"modifyWhiteboardAccess",
"undoAnnotation",
"clearWhiteboard",
"moveCursor",
"sendAnnotation",
"removePresentation",
"setPresentation",
"zoomSlide",
"requestPresentationUploadToken"
]
}
},
"chat": {
"min_message_length": 1,
"max_message_length": 5000,
"grouping_messages_window": 10000,
"type_system": "SYSTEM_MESSAGE",
"type_public": "PUBLIC_ACCESS",
"type_private": "PRIVATE_ACCESS",
"system_userid": "SYSTEM_MESSAGE",
"system_username": "SYSTEM_MESSAGE",
"public_id": "public",
"public_group_id": "MAIN-PUBLIC-GROUP-CHAT",
"public_userid": "public_chat_userid",
"public_username": "public_chat_username",
"storage_key": "UNREAD_CHATS",
"system_messages_keys": {
"chat_clear": "PUBLIC_CHAT_CLEAR"
}
},
"media": {
"WebRTCHangupRetryInterval": 2000,
"vertoServerAddress": "HOST",
"freeswitchProfilePassword": "1234",
"vertoPort": "8082",
"useSIPAudio": true,
"stunTurnServersFetchAddress": "/bigbluebutton/api/stuns",
"mediaTag": "#remote-media",
"callTransferTimeout": 5000,
"callHangupTimeout": 2000,
"callHangupMaximumRetries": 10,
"echoTestNumber": "9196"
},
"presentation": {
"defaultPresentationFile": "default.pdf",
"uploadEndpoint": "/bigbluebutton/presentation/upload",
"uploadSizeMin": 0,
"uploadSizeMax": 50000000,
"uploadValidMimeTypes": [
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.oasis.opendocument.text",
"application/rtf",
"text/plain",
"application/vnd.oasis.opendocument.spreadsheet",
"application/vnd.oasis.opendocument.presentation",
"application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.graphics",
"application/vnd.oasis.opendocument.chart",
"application/vnd.oasis.opendocument.image",
"application/pdf",
"image/jpeg",
"image/png"
]
},
"user": {
"role_moderator": "MODERATOR",
"role_viewer": "VIEWER",
"role_presenter": "PRESENTER"
},
"whiteboard": {
"annotations": {
"status": {
"start": "DRAW_START",
"update": "DRAW_UPDATE",
"end": "DRAW_END"
}
},
"toolbar": {
"colors": [
{
"label": "black",
"value": "#000000"
},
{
"label": "white",
"value": "#ffffff"
},
{
"label": "red",
"value": "#ff0000"
},
{
"label": "orange",
"value": "#ff8800"
},
{
"label": "eletricLime",
"value": "#ccff00"
},
{
"label": "Lime",
"value": "#00ff00"
},
{
"label": "Cyan",
"value": "#00ffff"
},
{
"label": "dodgerBlue",
"value": "#0088ff"
},
{
"label": "blue",
"value": "#0000ff"
},
{
"label": "violet",
"value": "#8800ff"
},
{
"label": "magenta",
"value": "#ff00ff"
},
{
"label": "silver",
"value": "#c0c0c0"
}
],
"thickness": [
{
"value": 14
},
{
"value": 12
},
{
"value": 10
},
{
"value": 8
},
{
"value": 6
},
{
"value": 4
},
{
"value": 2
},
{
"value": 1
}
],
"font_sizes": [
{
"value": 36
},
{
"value": 32
},
{
"value": 28
},
{
"value": 24
},
{
"value": 20
},
{
"value": 16
}
],
"tools": [
{
"icon": "text_tool",
"value": "text"
},
{
"icon": "line_tool",
"value": "line"
},
{
"icon": "circle_tool",
"value": "ellipse"
},
{
"icon": "triangle_tool",
"value": "triangle"
},
{
"icon": "rectangle_tool",
"value": "rectangle"
},
{
"icon": "pen_tool",
"value": "pencil"
},
{
"icon": "hand",
"value": "hand"
}
]
}
},
"clientLog": {
"server": { "enabled": true, "level": "info" },
"console": { "enabled": false, "level": "debug" },
"external": { "enabled": false, "level": "info", "url": "https://LOG_HOST/html5Log", "method": "POST" }
}
},
"private": {
"app": {
"captionsChunkLength": 1000,
"pencilChunkLength": 100
},
"redis": {
"host": "127.0.0.1",
"post": "6379",
"timeout": 5000,
"debug": true,
"channels": {
"toAkkaApps": "to-akka-apps-redis-channel"
},
"subscribeTo": [
"to-html5-redis-channel",
"from-akka-apps-*"
],
"async": [
"from-akka-apps-wb-redis-channel"
],
"ignored": [
"CheckAlivePongSysMsg",
"DoLatencyTracerMsg"
]
},
"serverLog": {
"level": "info"
}
}
}