diff --git a/modules/config/URLProcessor.js b/modules/config/URLProcessor.js index 35dfbf2e3..d7fed1886 100644 --- a/modules/config/URLProcessor.js +++ b/modules/config/URLProcessor.js @@ -1,10 +1,13 @@ -/* global config, interfaceConfig, loggingConfig, getConfigParamsFromUrl */ +/* global config, getConfigParamsFromUrl, interfaceConfig, loggingConfig */ + const logger = require("jitsi-meet-logger").getLogger(__filename); var configUtils = require('./Util'); var params = {}; -params = getConfigParamsFromUrl(); +if (typeof getConfigParamsFromUrl === 'function') { + params = getConfigParamsFromUrl(); +} var URLProcessor = { setConfigParametersFromUrl: function () { diff --git a/package.json b/package.json index 6abcde676..a0f53e862 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "jssha": "1.5.0", "jws": "3.1.4", "lib-jitsi-meet": "jitsi/lib-jitsi-meet", + "lodash": "4.17.4", "postis": "2.2.0", "react": "15.4.2", "react-dom": "15.4.2", diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js index 23b2402ef..eec0ecb4c 100644 --- a/react/features/app/functions.web.js +++ b/react/features/app/functions.web.js @@ -14,7 +14,6 @@ import { } from '../unsupported-browser'; import { WelcomePage } from '../welcome'; -import URLProcessor from '../../../modules/config/URLProcessor'; import KeyboardShortcut from '../../../modules/keyboardshortcut/keyboardshortcut'; import getTokenData from '../../../modules/tokendata/TokenData'; @@ -115,7 +114,6 @@ export function _getRouteToRender(stateOrGetState: Object | Function) { * @returns {void} */ export function init() { - URLProcessor.setConfigParametersFromUrl(); _initLogging(); APP.keyboardshortcut = KeyboardShortcut; @@ -129,40 +127,6 @@ export function init() { APP.translation.init(); } -/** - * Adjusts the logging levels. - * - * @private - * @returns {void} - */ -function _configureLoggingLevels() { - // NOTE The library Logger is separated from the app loggers, so the levels - // have to be set in two places - - // Set default logging level - const defaultLogLevel - = loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE; - - Logger.setLogLevel(defaultLogLevel); - JitsiMeetJS.setLogLevel(defaultLogLevel); - - // NOTE console was used on purpose here to go around the logging and always - // print the default logging level to the console - console.info(`Default logging level set to: ${defaultLogLevel}`); - - // Set log level for each logger - if (loggingConfig) { - Object.keys(loggingConfig).forEach(loggerName => { - if (loggerName !== 'defaultLogLevel') { - const level = loggingConfig[loggerName]; - - Logger.setLogLevelById(level, loggerName); - JitsiMeetJS.setLogLevelById(level, loggerName); - } - }); - } -} - /** * Initializes logging in the app. * @@ -170,9 +134,6 @@ function _configureLoggingLevels() { * @returns {void} */ function _initLogging() { - // Adjust logging level - _configureLoggingLevels(); - // Create the LogCollector and register it as the global log transport. It // is done early to capture as much logs as possible. Captured logs will be // cached, before the JitsiMeetLogStorage gets ready (statistics module is diff --git a/react/features/base/lib-jitsi-meet/actions.js b/react/features/base/lib-jitsi-meet/actions.js index a659fb770..e6d4eddc8 100644 --- a/react/features/base/lib-jitsi-meet/actions.js +++ b/react/features/base/lib-jitsi-meet/actions.js @@ -47,8 +47,8 @@ export function initLib() { throw new Error('Cannot init lib-jitsi-meet without config'); } - // XXX Temporarily until conference.js is moved to the React app we - // shouldn't use JitsiMeetJS from the React app. + // FIXME Until the logic of conference.js is rewritten into the React + // app we, JitsiMeetJS.init is to not be used for the React app. if (typeof APP !== 'undefined') { return Promise.resolve(); } diff --git a/react/features/base/lib-jitsi-meet/functions.js b/react/features/base/lib-jitsi-meet/functions.js index 62e2bd7d5..6b5b2cfd2 100644 --- a/react/features/base/lib-jitsi-meet/functions.js +++ b/react/features/base/lib-jitsi-meet/functions.js @@ -1,5 +1,7 @@ import { loadScript } from '../../base/util'; +import URLProcessor from '../../../../modules/config/URLProcessor'; + import JitsiMeetJS from './_'; declare var APP: Object; @@ -38,6 +40,12 @@ export function loadConfig(host: string, path: string = '/config.js') { // being used for the React Native app because the old/current Web app uses // config from the global scope. if (typeof APP !== 'undefined') { + // FIXME The following call to setConfigParametersFromUrl is bad design + // but URLProcessor still deals with the global variables config, + // interfaceConfig, and loggingConfig and loadConfig. As the latter will + // surely change in the future, so will the former then. + URLProcessor.setConfigParametersFromUrl(); + return Promise.resolve(window.config); } diff --git a/react/features/base/lib-jitsi-meet/middleware.js b/react/features/base/lib-jitsi-meet/middleware.js index 48a223ddd..deb6c9cd2 100644 --- a/react/features/base/lib-jitsi-meet/middleware.js +++ b/react/features/base/lib-jitsi-meet/middleware.js @@ -1,6 +1,7 @@ /* @flow */ import { SET_CONFIG } from '../config'; +import { setLoggingConfig } from '../logging'; import { PARTICIPANT_LEFT } from '../participants'; import { MiddlewareRegistry } from '../redux'; @@ -106,6 +107,14 @@ function _setConfig({ dispatch, getState }, next, action) { // from there). next(action); + // FIXME Obviously, the following is bad design. However, I'm currently + // introducing the features base/config and base/logging and I'm trying + // to minimize the scope of the changes while I'm attempting to preserve + // compatibility with the existing partially React-ified Web source code + // and what was already executing on React Native. Additionally, I do + // not care to load logging_config.js on React Native. + dispatch(setLoggingConfig(window.loggingConfig)); + dispatch(initLib()); }); } diff --git a/react/features/base/logging/actionTypes.js b/react/features/base/logging/actionTypes.js new file mode 100644 index 000000000..bdce0536b --- /dev/null +++ b/react/features/base/logging/actionTypes.js @@ -0,0 +1,12 @@ +import { Symbol } from '../react'; + +/** + * The type of redux action which sets the configuration of the feature + * base/logging. + * + * { + * type: SET_LOGGING_CONFIG, + * config: Object + * } + */ +export const SET_LOGGING_CONFIG = Symbol('SET_LOGGING_CONFIG'); diff --git a/react/features/base/logging/actions.js b/react/features/base/logging/actions.js new file mode 100644 index 000000000..a8198078b --- /dev/null +++ b/react/features/base/logging/actions.js @@ -0,0 +1,20 @@ +/* @flow */ + +import { SET_LOGGING_CONFIG } from './actionTypes'; + +/** + * Sets the configuration of the feature base/logging. + * + * @param {Object} config - The configuration to set on the features + * base/logging. + * @returns {{ + * type: SET_LOGGING_CONFIG, + * config: Object + * }} + */ +export function setLoggingConfig(config: Object) { + return { + type: SET_LOGGING_CONFIG, + config + }; +} diff --git a/react/features/base/logging/index.js b/react/features/base/logging/index.js new file mode 100644 index 000000000..f3a2aac65 --- /dev/null +++ b/react/features/base/logging/index.js @@ -0,0 +1,5 @@ +export * from './actions'; +export * from './actionTypes'; + +import './middleware'; +import './reducer'; diff --git a/react/features/base/logging/middleware.js b/react/features/base/logging/middleware.js new file mode 100644 index 000000000..82b50855a --- /dev/null +++ b/react/features/base/logging/middleware.js @@ -0,0 +1,131 @@ +/* @flow */ + +import Logger from 'jitsi-meet-logger'; + +import { APP_WILL_MOUNT } from '../../app'; +import JitsiMeetJS, { LIB_WILL_INIT } from '../lib-jitsi-meet'; +import { MiddlewareRegistry } from '../redux'; + +import { SET_LOGGING_CONFIG } from './actionTypes'; + +declare var APP: Object; + +/** + * The Redux middleware of the feature base/logging. + * + * @param {Store} store - The Redux store. + * @returns {Function} + * @private + */ +MiddlewareRegistry.register(store => next => action => { + switch (action.type) { + case APP_WILL_MOUNT: + return _appWillMount(store, next, action); + + case LIB_WILL_INIT: + return _libWillInit(store, next, action); + + case SET_LOGGING_CONFIG: + return _setLoggingConfig(store, next, action); + } + + return next(action); +}); + +/** + * Notifies the feature base/logging that the action {@link APP_WILL_MOUNT} is + * being dispatched within a specific Redux {@code store}. + * + * @param {Store} store - The Redux store in which the specified {@code action} + * is being dispatched. + * @param {Dispatch} next - The Redux {@code dispatch} function to dispatch the + * specified {@code action} to the specified {@code store}. + * @param {Action} action - The Redux action {@code APP_WILL_MOUNT} which is + * being dispatched in the specified {@code store}. + * @private + * @returns {Object} The new state that is the result of the reduction of the + * specified {@code action}. + */ +function _appWillMount({ getState }, next, action) { + const { config } = getState()['features/base/logging']; + + _setLogLevels(Logger, config); + + // FIXME Until the logic of conference.js is rewritten into the React + // app we, JitsiMeetJS.init is to not be used for the React app. + // Consequently, LIB_WILL_INIT will not be dispatched. In the meantime, do + // the following: + typeof APP === 'undefined' || _setLogLevels(JitsiMeetJS, config); + + return next(action); +} + +/** + * Notifies the feature base/logging that the action {@link LIB_WILL_INIT} is + * being dispatched within a specific Redux {@code store}. + * + * @param {Store} store - The Redux store in which the specified {@code action} + * is being dispatched. + * @param {Dispatch} next - The Redux {@code dispatch} function to dispatch the + * specified {@code action} to the specified {@code store}. + * @param {Action} action - The Redux action {@code LIB_WILL_INIT} which is + * being dispatched in the specified {@code store}. + * @private + * @returns {Object} The new state that is the result of the reduction of the + * specified {@code action}. + */ +function _libWillInit({ getState }, next, action) { + _setLogLevels(JitsiMeetJS, getState()['features/base/logging'].config); + + return next(action); +} + +/** + * Notifies the feature base/logging that the action {@link SET_LOGGING_CONFIG} + * is being dispatched within a specific Redux {@code store}. + * + * @param {Store} store - The Redux store in which the specified {@code action} + * is being dispatched. + * @param {Dispatch} next - The Redux {@code dispatch} function to dispatch the + * specified {@code action} to the specified {@code store}. + * @param {Action} action - The Redux action {@code SET_LOGGING_CONFIG} which is + * being dispatched in the specified {@code store}. + * @private + * @returns {Object} The new state that is the result of the reduction of the + * specified {@code action}. + */ +function _setLoggingConfig({ getState }, next, action) { + const oldValue = getState()['features/base/logging'].config; + const result = next(action); + const newValue = getState()['features/base/logging'].config; + + if (oldValue !== newValue) { + _setLogLevels(Logger, newValue); + _setLogLevels(JitsiMeetJS, newValue); + } + + return result; +} + +/** + * Sets the log levels of {@link Logger} or {@link JitsiMeetJS} in accord with + * a specific configuration. + * + * @param {Object} logger - The object on which the log levels are to be set. + * @param {Object} config - The configuration specifying the log levels to be + * set on {@code Logger} or {@code JitsiMeetJS}. + * @private + * @returns {void} + */ +function _setLogLevels(logger, config) { + // XXX The loggers of the library lib-jitsi-meet and the application + // jitsi-meet are separate, so the log levels have to be set in both. + + // First, set the default log level. + logger.setLogLevel(config.defaultLogLevel); + + // Second, set the log level of each logger explictly overriden by config. + Object.keys(config).forEach( + id => + id === 'defaultLogLevel' || logger.setLogLevelById(config[id], id)); +} diff --git a/react/features/base/logging/reducer.js b/react/features/base/logging/reducer.js new file mode 100644 index 000000000..7073d3440 --- /dev/null +++ b/react/features/base/logging/reducer.js @@ -0,0 +1,61 @@ +import { equals, ReducerRegistry } from '../redux'; + +import { SET_LOGGING_CONFIG } from './actionTypes'; + +/** + * The initial state of the feature base/logging. + * + * @type {{ + * config: Object + * }} + */ +const INITIAL_STATE = { + config: { + defaultLogLevel: 'trace', + + // The following are too verbose in their logging with the + // {@link #defaultLogLevel}: + 'modules/statistics/CallStats.js': 'info', + 'modules/xmpp/strophe.util.js': 'log' + } +}; + +ReducerRegistry.register( + 'features/base/logging', + (state = INITIAL_STATE, action) => { + switch (action.type) { + case SET_LOGGING_CONFIG: + return _setLoggingConfig(state, action); + + default: + return state; + } + }); + +/** + * Reduces a specific Redux action SET_LOGGING_CONFIG of the feature + * base/logging. + * + * @param {Object} state - The Redux state of the feature base/logging. + * @param {Action} action - The Redux action SET_LOGGING_CONFIG to reduce. + * @private + * @returns {Object} The new state of the feature base/logging after the + * reduction of the specified action. + */ +function _setLoggingConfig(state, action) { + const config = { + // The config of INITIAL_STATE is the default configuration of the + // feature base/logging. + ...INITIAL_STATE.config, + ...action.config + }; + + if (equals(state.config, config)) { + return state; + } + + return { + ...state, + config + }; +} diff --git a/react/features/base/redux/functions.js b/react/features/base/redux/functions.js index 99aa182ac..6219c8bc3 100644 --- a/react/features/base/redux/functions.js +++ b/react/features/base/redux/functions.js @@ -1,3 +1,5 @@ +import _ from 'lodash'; + /** * Sets specific properties of a specific state to specific values and prevents * unnecessary state changes. @@ -21,6 +23,19 @@ export function assign(target, source) { return t; } +/** + * Determines whether {@code a} equals {@code b} according to deep comparison + * (which makes sense for Redux and its state definition). + * + * @param {*} a - The value to compare to {@code b}. + * @param {*} b - The value to compare to {@code a}. + * @returns {boolean} True if {@code a} equals {@code b} (according to deep + * comparison); false, otherwise. + */ +export function equals(a, b) { + return _.isEqual(a, b); +} + /** * Sets a specific property of a specific state to a specific value. Prevents * unnecessary state changes (when the specified value is equal to the