core: refactor routing (continued)

This commit is contained in:
Lyubo Marinov 2018-07-11 22:57:44 -05:00
parent 155e02bbfb
commit c203215c54
12 changed files with 72 additions and 84 deletions

View File

@ -2320,13 +2320,14 @@ export default {
* @private * @private
*/ */
_initDeviceList() { _initDeviceList() {
if (JitsiMeetJS.mediaDevices.isDeviceListAvailable() const { mediaDevices } = JitsiMeetJS;
&& JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
JitsiMeetJS.mediaDevices.enumerateDevices(devices => { if (mediaDevices.isDeviceListAvailable()
// Ugly way to synchronize real device IDs with local && mediaDevices.isDeviceChangeAvailable()) {
// storage and settings menu. This is a workaround until mediaDevices.enumerateDevices(devices => {
// getConstraints() method will be implemented // Ugly way to synchronize real device IDs with local storage
// in browsers. // and settings menu. This is a workaround until
// getConstraints() method will be implemented in browsers.
const { dispatch } = APP.store; const { dispatch } = APP.store;
if (this.localAudio) { if (this.localAudio) {
@ -2334,7 +2335,6 @@ export default {
micDeviceId: this.localAudio.getDeviceId() micDeviceId: this.localAudio.getDeviceId()
})); }));
} }
if (this.localVideo) { if (this.localVideo) {
dispatch(updateSettings({ dispatch(updateSettings({
cameraDeviceId: this.localVideo.getDeviceId() cameraDeviceId: this.localVideo.getDeviceId()
@ -2347,9 +2347,8 @@ export default {
}); });
this.deviceChangeListener = devices => this.deviceChangeListener = devices =>
window.setTimeout( window.setTimeout(() => this._onDeviceListChanged(devices), 0);
() => this._onDeviceListChanged(devices), 0); mediaDevices.addEventListener(
JitsiMeetJS.mediaDevices.addEventListener(
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED, JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
this.deviceChangeListener); this.deviceChangeListener);
} }

View File

@ -78,10 +78,10 @@ function _appNavigateToMandatoryLocation(
*/ */
function loadConfigSettled(error, config) { function loadConfigSettled(error, config) {
// Due to the asynchronous nature of the loading, the specified config // Due to the asynchronous nature of the loading, the specified config
// may or may not be required by the time the notification arrives. // may or may not be required by the time the notification arrives. If
// If we receive the config for a location we are no longer interested // we receive the config for a location we are no longer interested in,
// in, "ignore" it - deliver it to the external API, for example, but do // "ignore" it - deliver it to the external API, for example, but do not
// not proceed with the appNavigate procedure/process. // proceed with the appNavigate procedure/process.
if (getState()['features/base/config'].locationURL === locationURL) { if (getState()['features/base/config'].locationURL === locationURL) {
dispatch(setLocationURL(locationURL)); dispatch(setLocationURL(locationURL));
dispatch(setConfig(config)); dispatch(setConfig(config));
@ -90,8 +90,8 @@ function _appNavigateToMandatoryLocation(
error || (error = new Error('Config no longer needed!')); error || (error = new Error('Config no longer needed!'));
// XXX The failure could be, for example, because of a // XXX The failure could be, for example, because of a
// certificate-related error. In which case the connection will // certificate-related error. In which case the connection will fail
// fail later in Strophe anyway. // later in Strophe anyway.
dispatch(loadConfigError(error, locationURL)); dispatch(loadConfigError(error, locationURL));
throw error; throw error;

View File

@ -23,8 +23,8 @@ import { OverlayContainer } from '../../overlay';
import { appNavigate, appWillMount, appWillUnmount } from '../actions'; import { appNavigate, appWillMount, appWillUnmount } from '../actions';
/** /**
* The default URL to open if no other was specified to {@code AbstractApp} * The default URL to open if no other was specified to {@code AbstractApp} via
* via props. * props.
* *
* FIXME: This is not at the best place here. This should be either in the * FIXME: This is not at the best place here. This should be either in the
* base/settings feature or a default in base/config. * base/settings feature or a default in base/config.
@ -78,6 +78,12 @@ export class AbstractApp extends Component {
this.state = { this.state = {
/**
* The state of the »possible« async initialization of
* the {@code AbstractApp}.
*/
appAsyncInitialized: false,
/** /**
* The Route rendered by this {@code AbstractApp}. * The Route rendered by this {@code AbstractApp}.
* *
@ -85,12 +91,6 @@ export class AbstractApp extends Component {
*/ */
route: {}, route: {},
/**
* The state of the »possible« async initialization of
* the {@code AbstractApp}.
*/
appAsyncInitialized: false,
/** /**
* The redux store used by this {@code AbstractApp}. * The redux store used by this {@code AbstractApp}.
* *
@ -239,8 +239,7 @@ export class AbstractApp extends Component {
* @returns {ReactElement} * @returns {ReactElement}
*/ */
render() { render() {
const { appAsyncInitialized, route } = this.state; const { appAsyncInitialized, route: { component } } = this.state;
const { component } = route;
if (appAsyncInitialized && component) { if (appAsyncInitialized && component) {
return ( return (
@ -357,7 +356,7 @@ export class AbstractApp extends Component {
return ( return (
this.props.defaultURL this.props.defaultURL
|| this._getStore().getState()['features/base/settings'] || this._getStore().getState()['features/base/settings']
.serverURL .serverURL
|| DEFAULT_URL); || DEFAULT_URL);
} }
@ -435,9 +434,7 @@ export class AbstractApp extends Component {
// performed before setState completes, the app may not navigate to the // performed before setState completes, the app may not navigate to the
// expected route. In order to mitigate the problem, _navigate was // expected route. In order to mitigate the problem, _navigate was
// changed to return a Promise. // changed to return a Promise.
return new Promise(resolve => { return new Promise(resolve => this.setState({ route }, resolve));
this.setState({ route }, resolve);
});
} }
/** /**

View File

@ -3,7 +3,6 @@
import { NativeModules } from 'react-native'; import { NativeModules } from 'react-native';
export * from './functions.any'; export * from './functions.any';
export * from './router';
/** /**
* Returns application name. * Returns application name.

View File

@ -1,7 +1,6 @@
// @flow // @flow
export * from './functions.any'; export * from './functions.any';
export * from './router';
declare var interfaceConfig: Object; declare var interfaceConfig: Object;

View File

@ -1,4 +1,5 @@
// @flow // @flow
import type { Component } from 'react'; import type { Component } from 'react';
import { isRoomValid } from '../base/conference'; import { isRoomValid } from '../base/conference';
@ -35,11 +36,11 @@ export type Route = {
* Determines which route is to be rendered in order to depict a specific Redux * Determines which route is to be rendered in order to depict a specific Redux
* store. * store.
* *
* @param {(Object|Function)} stateful - Redux state or Regux getState() * @param {(Function|Object)} stateful - THe redux store, state, or
* method. * {@code getState} function.
* @returns {Promise<Route>} * @returns {Promise<Route>}
*/ */
export function getRouteToRender(stateful: Object | Function): Promise<Route> { export function _getRouteToRender(stateful: Function | Object): Promise<Route> {
const state = toState(stateful); const state = toState(stateful);
const { room } = state['features/base/conference']; const { room } = state['features/base/conference'];
const isMobileApp = navigator.product === 'ReactNative'; const isMobileApp = navigator.product === 'ReactNative';
@ -51,11 +52,11 @@ export function getRouteToRender(stateful: Object | Function): Promise<Route> {
}; };
return new Promise(resolve => { return new Promise(resolve => {
// First check if the current endpoint supports WebRTC. We are // First, check if the current endpoint supports WebRTC. We are
// intentionally not performing the check for mobile browsers because: // intentionally not performing the check for mobile browsers because:
// - the welcome page is mobile ready // - the WelcomePage is mobile ready;
// - if the URL points to a conference, getDeepLinkingPage will take // - if the URL points to a conference, getDeepLinkingPage will take
// care of it // care of it.
if (!isMobileBrowser && !JitsiMeetJS.isWebRtcSupported()) { if (!isMobileBrowser && !JitsiMeetJS.isWebRtcSupported()) {
route.component = UnsupportedDesktopBrowser; route.component = UnsupportedDesktopBrowser;
resolve(route); resolve(route);
@ -70,8 +71,8 @@ export function getRouteToRender(stateful: Object | Function): Promise<Route> {
} else { } else {
// Update the location if it doesn't match. This happens when a // Update the location if it doesn't match. This happens when a
// room is joined from the welcome page. The reason for doing // room is joined from the welcome page. The reason for doing
// this instead of using the history API is that we want to // this instead of using the history API is that we want to load
// load the config.js which takes the room into account. // the config.js which takes the room into account.
const { locationURL } = state['features/base/connection']; const { locationURL } = state['features/base/connection'];
// eslint-disable-next-line no-negated-condition // eslint-disable-next-line no-negated-condition
@ -91,8 +92,8 @@ export function getRouteToRender(stateful: Object | Function): Promise<Route> {
} }
if (!isWelcomePageUserEnabled(state)) { if (!isWelcomePageUserEnabled(state)) {
// Web: if the welcome page is disabled, go directly to a // Web: if the welcome page is disabled, go directly to a random
// random room. // room.
let href = window.location.href; let href = window.location.href;

View File

@ -7,7 +7,7 @@ import {
} from '../base/connection'; } from '../base/connection';
import { MiddlewareRegistry } from '../base/redux'; import { MiddlewareRegistry } from '../base/redux';
import { getRouteToRender } from './router'; import { _getRouteToRender } from './getRouteToRender';
MiddlewareRegistry.register(store => next => action => { MiddlewareRegistry.register(store => next => action => {
switch (action.type) { switch (action.type) {
@ -74,7 +74,7 @@ function _navigate({ getState }) {
const state = getState(); const state = getState();
const { app } = state['features/app']; const { app } = state['features/app'];
getRouteToRender(state).then(route => app._navigate(route)); _getRouteToRender(state).then(route => app._navigate(route));
} }
/** /**

View File

@ -149,6 +149,24 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
export { default as getRoomName } from './getRoomName'; export { default as getRoomName } from './getRoomName';
export { parseURLParams }; export { parseURLParams };
/**
* Promise wrapper on obtain config method. When HttpConfigFetch will be moved
* to React app it's better to use load config instead.
*
* @param {string} location - URL of the domain from which the config is to be
* obtained.
* @param {string} room - Room name.
* @private
* @returns {Promise<void>}
*/
export function obtainConfig(location: string, room: string): Promise<void> {
return new Promise((resolve, reject) =>
_obtainConfig(location, room, (success, error) => {
success ? resolve() : reject(error);
})
);
}
/** /**
* Sends HTTP POST request to specified {@code endpoint}. In request the name * Sends HTTP POST request to specified {@code endpoint}. In request the name
* of the room is included in JSON format: * of the room is included in JSON format:
@ -163,10 +181,7 @@ export { parseURLParams };
* @param {Function} complete - The callback to invoke upon success or failure. * @param {Function} complete - The callback to invoke upon success or failure.
* @returns {void} * @returns {void}
*/ */
export function obtainConfig( function _obtainConfig(endpoint: string, roomName: string, complete: Function) {
endpoint: string,
roomName: string,
complete: Function) {
logger.info(`Send config request to ${endpoint} for room: ${roomName}`); logger.info(`Send config request to ${endpoint} for room: ${roomName}`);
$.ajax( $.ajax(
endpoint, endpoint,

View File

@ -20,10 +20,8 @@ export {
*/ */
export function connect() { export function connect() {
return (dispatch: Dispatch<*>, getState: Function) => { return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
// XXX Lib-jitsi-meet does not accept uppercase letters. // XXX Lib-jitsi-meet does not accept uppercase letters.
const room = state['features/base/conference'].room.toLowerCase(); const room = getState()['features/base/conference'].room.toLowerCase();
// XXX For web based version we use conference initialization logic // XXX For web based version we use conference initialization logic
// from the old app (at the moment of writing). // from the old app (at the moment of writing).

View File

@ -184,8 +184,6 @@ class Conference extends Component<Props> {
componentWillReceiveProps(nextProps: Props) { componentWillReceiveProps(nextProps: Props) {
const { const {
_locationURL: oldLocationURL, _locationURL: oldLocationURL,
_onConnect,
_onDisconnect,
_participantCount: oldParticipantCount, _participantCount: oldParticipantCount,
_room: oldRoom, _room: oldRoom,
_setToolboxVisible _setToolboxVisible
@ -197,10 +195,10 @@ class Conference extends Component<Props> {
} = nextProps; } = nextProps;
// If the location URL changes we need to reconnect. // If the location URL changes we need to reconnect.
oldLocationURL !== newLocationURL && _onDisconnect(); oldLocationURL !== newLocationURL && this.props._onDisconnect();
// Start the connection process when there is a (valid) room. // Start the connection process when there is a (valid) room.
oldRoom !== newRoom && newRoom && _onConnect(); oldRoom !== newRoom && newRoom && this.props._onConnect();
if (oldParticipantCount === 1) { if (oldParticipantCount === 1) {
newParticipantCount > 1 && _setToolboxVisible(false); newParticipantCount > 1 && _setToolboxVisible(false);
@ -342,9 +340,7 @@ class Conference extends Component<Props> {
* @returns {React$Node} * @returns {React$Node}
*/ */
_renderConferenceNotification() { _renderConferenceNotification() {
return ConferenceNotification return ConferenceNotification ? <ConferenceNotification /> : undefined;
? <ConferenceNotification />
: undefined;
} }
} }
@ -356,6 +352,7 @@ class Conference extends Component<Props> {
* @returns {{ * @returns {{
* _onConnect: Function, * _onConnect: Function,
* _onDisconnect: Function, * _onDisconnect: Function,
* _onHardwareBackPress: Function,
* _setToolboxVisible: Function * _setToolboxVisible: Function
* }} * }}
*/ */

View File

@ -28,24 +28,6 @@ declare var interfaceConfig: Object;
const logger = require('jitsi-meet-logger').getLogger(__filename); const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Promise wrapper on obtain config method. When HttpConfigFetch will be moved
* to React app it's better to use load config instead.
*
* @param {string} location - URL of the domain from which the config is to be
* obtained.
* @param {string} room - Room name.
* @private
* @returns {Promise}
*/
function _obtainConfig(location: string, room: string) {
return new Promise((resolve, reject) =>
obtainConfig(location, room, (success, error) => {
success ? resolve() : reject(error);
})
);
}
/** /**
* DOM events for when full screen mode has changed. Different browsers need * DOM events for when full screen mode has changed. Different browsers need
* different vendor prefixes. * different vendor prefixes.
@ -119,7 +101,7 @@ class Conference extends Component<Props> {
const { configLocation } = config; const { configLocation } = config;
if (configLocation) { if (configLocation) {
_obtainConfig(configLocation, this.props._room) obtainConfig(configLocation, this.props._room)
.then(() => { .then(() => {
const now = window.performance.now(); const now = window.performance.now();
@ -133,8 +115,8 @@ class Conference extends Component<Props> {
// Show obtain config error. // Show obtain config error.
APP.UI.messageHandler.showError({ APP.UI.messageHandler.showError({
titleKey: 'connection.CONNFAIL', descriptionKey: 'dialog.connectError',
descriptionKey: 'dialog.connectError' titleKey: 'connection.CONNFAIL'
}); });
}); });
} else { } else {
@ -253,7 +235,8 @@ class Conference extends Component<Props> {
* @param {Object} state - The Redux state. * @param {Object} state - The Redux state.
* @private * @private
* @returns {{ * @returns {{
* _iAmRecorder: boolean * _iAmRecorder: boolean,
* _room: ?string
* }} * }}
*/ */
function _mapStateToProps(state) { function _mapStateToProps(state) {

View File

@ -10,11 +10,11 @@ import { NetworkActivityIndicator } from '../../mobile/network-activity';
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay'; import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
/** /**
* {@code BlankPage} React {@code Component}'s prop types. * The type of React {@code Component} props of {@link BlankPage}.
*/ */
type Props = { type Props = {
dispatch: Dispatch<*> dispatch: Dispatch<*>
} };
/** /**
* The React {@code Component} displayed by {@code AbstractApp} when it has no * The React {@code Component} displayed by {@code AbstractApp} when it has no