[RN] Don't override config values

This commit is contained in:
Lyubo Marinov 2017-07-21 16:12:02 -05:00
parent b0ffe2e63f
commit 96e0c56bde
9 changed files with 341 additions and 315 deletions

View File

@ -19,7 +19,6 @@ import analytics from './modules/analytics/analytics';
import EventEmitter from "events";
import { getLocationContextRoot } from './react/features/app';
import {
AVATAR_ID_COMMAND,
AVATAR_URL_COMMAND,
@ -50,6 +49,7 @@ import {
trackAdded,
trackRemoved
} from './react/features/base/tracks';
import { getLocationContextRoot } from './react/features/base/util';
import { statsEmitter } from './react/features/connection-indicator';
import { showDesktopPicker } from './react/features/desktop-picker';
import {

View File

@ -2,9 +2,9 @@ import { setRoom } from '../base/conference';
import { setLocationURL } from '../base/connection';
import { setConfig } from '../base/config';
import { loadConfig } from '../base/lib-jitsi-meet';
import { parseURIString } from '../base/util';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
import { _parseURIString } from './functions';
declare var APP: Object;
@ -19,9 +19,7 @@ declare var APP: Object;
*/
export function appNavigate(uri: ?string) {
return (dispatch: Dispatch<*>, getState: Function) =>
_appNavigateToOptionalLocation(
dispatch, getState,
_parseURIString(uri));
_appNavigateToOptionalLocation(dispatch, getState, parseURIString(uri));
}
/**
@ -133,7 +131,7 @@ function _appNavigateToOptionalLocation(
// default.
if (!location || !location.host) {
const defaultLocation
= _parseURIString(getState()['features/app'].app._getDefaultURL());
= parseURIString(getState()['features/app'].app._getDefaultURL());
if (location) {
location.host = defaultLocation.host;
@ -211,9 +209,7 @@ function _loadConfig(location: Object) {
// The React Native app supports an app-specific scheme which is sure to not
// be supported by fetch (or whatever loadConfig utilizes).
if (protocol !== 'http:' && protocol !== 'https:') {
protocol = 'https:';
}
protocol !== 'http:' && protocol !== 'https:' && (protocol = 'https:');
// TDOO userinfo

View File

@ -1,8 +1,8 @@
import { AbstractApp } from './AbstractApp';
import { getLocationContextRoot } from '../functions';
import { getLocationContextRoot } from '../../base/util';
import '../../room-lock';
import { AbstractApp } from './AbstractApp';
/**
* Root application component.
*

View File

@ -3,132 +3,6 @@ import { RouteRegistry } from '../base/react';
import { Conference } from '../conference';
import { WelcomePage } from '../welcome';
/**
* The RegExp pattern of the authority of a URI.
*
* @private
* @type {string}
*/
const _URI_AUTHORITY_PATTERN = '(//[^/?#]+)';
/**
* The RegExp pattern of the path of a URI.
*
* @private
* @type {string}
*/
const _URI_PATH_PATTERN = '([^?#]*)';
/**
* The RegExp patther of the protocol of a URI.
*
* @private
* @type {string}
*/
const _URI_PROTOCOL_PATTERN = '([a-z][a-z0-9\\.\\+-]*:)';
/**
* Fixes the hier-part of a specific URI (string) so that the URI is well-known.
* For example, certain Jitsi Meet deployments are not conventional but it is
* possible to translate their URLs into conventional.
*
* @param {string} uri - The URI (string) to fix the hier-part of.
* @private
* @returns {string}
*/
function _fixURIStringHierPart(uri) {
// Rewrite the specified URL in order to handle special cases such as
// hipchat.com and enso.me which do not follow the common pattern of most
// Jitsi Meet deployments.
// hipchat.com
let regex
= new RegExp(
`^${_URI_PROTOCOL_PATTERN}//hipchat\\.com/video/call/`,
'gi');
let match = regex.exec(uri);
if (!match) {
// enso.me
regex
= new RegExp(
`^${_URI_PROTOCOL_PATTERN}//enso\\.me/(?:call|meeting)/`,
'gi');
match = regex.exec(uri);
}
if (match) {
/* eslint-disable no-param-reassign, prefer-template */
uri
= match[1] /* protocol */
+ '//enso.hipchat.me/'
+ uri.substring(regex.lastIndex); /* room (name) */
/* eslint-enable no-param-reassign, prefer-template */
}
return uri;
}
/**
* Fixes the scheme part of a specific URI (string) so that it contains a
* well-known scheme such as HTTP(S). For example, the mobile app implements an
* app-specific URI scheme in addition to Universal Links. The app-specific
* scheme may precede or replace the well-known scheme. In such a case, dealing
* with the app-specific scheme only complicates the logic and it is simpler to
* get rid of it (by translating the app-specific scheme into a well-known
* scheme).
*
* @param {string} uri - The URI (string) to fix the scheme of.
* @private
* @returns {string}
*/
function _fixURIStringScheme(uri) {
const regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}+`, 'gi');
const match = regex.exec(uri);
if (match) {
// As an implementation convenience, pick up the last scheme and make
// sure that it is a well-known one.
let protocol = match[match.length - 1].toLowerCase();
if (protocol !== 'http:' && protocol !== 'https:') {
protocol = 'https:';
}
/* eslint-disable no-param-reassign */
uri = uri.substring(regex.lastIndex);
if (uri.startsWith('//')) {
// The specified URL was not a room name only, it contained an
// authority.
uri = protocol + uri;
}
/* eslint-enable no-param-reassign */
}
return uri;
}
/**
* Gets the (Web application) context root defined by a specific location (URI).
*
* @param {Object} location - The location (URI) which defines the (Web
* application) context root.
* @returns {string} - The (Web application) context root defined by the
* specified {@code location} (URI).
*/
export function getLocationContextRoot(location: Object) {
const pathname = location.pathname;
const contextRootEndIndex = pathname.lastIndexOf('/');
return (
contextRootEndIndex === -1
? '/'
: pathname.substring(0, contextRootEndIndex + 1));
}
/**
* Determines which route is to be rendered in order to depict a specific Redux
* store.
@ -159,137 +33,3 @@ export function _getRouteToRender(stateOrGetState) {
return RouteRegistry.getRouteByComponent(component);
}
/**
* Parses a specific URI string into an object with the well-known properties of
* the {@link Location} and/or {@link URL} interfaces implemented by Web
* browsers. The parsing attempts to be in accord with IETF's RFC 3986.
*
* @param {string} str - The URI string to parse.
* @returns {{
* hash: string,
* host: (string|undefined),
* hostname: (string|undefined),
* pathname: string,
* port: (string|undefined),
* protocol: (string|undefined),
* search: string
* }}
*/
function _parseStandardURIString(str: string) {
/* eslint-disable no-param-reassign */
const obj = {};
let regex;
let match;
// protocol
regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}`, 'gi');
match = regex.exec(str);
if (match) {
obj.protocol = match[1].toLowerCase();
str = str.substring(regex.lastIndex);
}
// authority
regex = new RegExp(`^${_URI_AUTHORITY_PATTERN}`, 'gi');
match = regex.exec(str);
if (match) {
let authority = match[1].substring(/* // */ 2);
str = str.substring(regex.lastIndex);
// userinfo
const userinfoEndIndex = authority.indexOf('@');
if (userinfoEndIndex !== -1) {
authority = authority.substring(userinfoEndIndex + 1);
}
obj.host = authority;
// port
const portBeginIndex = authority.lastIndexOf(':');
if (portBeginIndex !== -1) {
obj.port = authority.substring(portBeginIndex + 1);
authority = authority.substring(0, portBeginIndex);
}
// hostname
obj.hostname = authority;
}
// pathname
regex = new RegExp(`^${_URI_PATH_PATTERN}`, 'gi');
match = regex.exec(str);
let pathname;
if (match) {
pathname = match[1];
str = str.substring(regex.lastIndex);
}
if (pathname) {
if (!pathname.startsWith('/')) {
pathname = `/${pathname}`;
}
} else {
pathname = '/';
}
obj.pathname = pathname;
// query
if (str.startsWith('?')) {
let hashBeginIndex = str.indexOf('#', 1);
if (hashBeginIndex === -1) {
hashBeginIndex = str.length;
}
obj.search = str.substring(0, hashBeginIndex);
str = str.substring(hashBeginIndex);
} else {
obj.search = ''; // Google Chrome
}
// fragment
obj.hash = str.startsWith('#') ? str : '';
/* eslint-enable no-param-reassign */
return obj;
}
/**
* Parses a specific URI which (supposedly) references a Jitsi Meet resource
* (location).
*
* @param {(string|undefined)} uri - The URI to parse which (supposedly)
* references a Jitsi Meet resource (location).
* @returns {{
* room: (string|undefined)
* }}
*/
export function _parseURIString(uri: ?string) {
if (typeof uri !== 'string') {
return undefined;
}
const obj
= _parseStandardURIString(
_fixURIStringHierPart(_fixURIStringScheme(uri)));
// Add the properties that are specific to a Jitsi Meet resource (location)
// such as contextRoot, room:
// contextRoot
obj.contextRoot = getLocationContextRoot(obj);
// The room (name) is the last component of pathname.
const { pathname } = obj;
obj.room = pathname.substring(pathname.lastIndexOf('/') + 1) || undefined;
return obj;
}

View File

@ -71,8 +71,6 @@ const _INTERCEPT_COMPONENT_RULES = [
}
];
export { getLocationContextRoot, _parseURIString } from './functions.native';
/**
* Determines which route is to be rendered in order to depict a specific Redux
* store.

View File

@ -1,9 +1,11 @@
/* @flow */
import _ from 'lodash';
import type { Dispatch } from 'redux';
import { conferenceWillLeave } from '../conference';
import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
import { parseStandardURIString } from '../util';
import {
CONNECTION_DISCONNECTED,
@ -21,29 +23,8 @@ import {
export function connect() {
return (dispatch: Dispatch<*>, getState: Function) => {
const state = getState();
let { options } = state['features/base/connection'];
options = {
// Lib-jitsi-meet wants the config passed in multiple places and
// here is the latest one I have discovered.
...state['features/base/config'],
// TODO It is probable that config should override the options that
// have been automatically constructed by the app. Unfortunately,
// config may specify URLs such as bosh at the time of this writing
// which react-native cannot parse (because they do not have a
// protocol/scheme).
...options
};
const options = _constructOptions(state);
const { issuer, jwt } = state['features/jwt'];
const { room } = state['features/base/conference'];
// XXX The Jitsi Meet deployments require the room argument to be in
// lower case at the time of this writing but, unfortunately, they do
// not ignore case themselves.
options.bosh += room ? `?room=${room.toLowerCase()}` : '';
const connection
= new JitsiMeetJS.JitsiConnection(
options.appId,
@ -202,6 +183,51 @@ export function connectionFailed(
};
}
/**
* Constructs options to be passed to the constructor of {@code JitsiConnection}
* based on the redux state.
*
* @param {Object} state - The redux state.
* @returns {Object} The options to be passed to the constructor of
* {@code JitsiConnection}.
*/
function _constructOptions(state) {
const defaultOptions = state['features/base/connection'].options;
const options = _.merge(
{},
defaultOptions,
// Lib-jitsi-meet wants the config passed in multiple places and here is
// the latest one I have discovered.
state['features/base/config'],
);
let { bosh } = options;
if (bosh) {
// Append room to the URL's search.
const { room } = state['features/base/conference'];
// XXX The Jitsi Meet deployments require the room argument to be in
// lower case at the time of this writing but, unfortunately, they do
// not ignore case themselves.
room && (bosh += `?room=${room.toLowerCase()}`);
// XXX By default, config.js does not add a protocol to the BOSH URL.
// Which trips React Native. Make sure there is a protocol in order to
// satisfy React Native.
if (bosh !== defaultOptions.bosh
&& !parseStandardURIString(bosh).protocol) {
const { protocol } = parseStandardURIString(defaultOptions.bosh);
protocol && (bosh = protocol + bosh);
}
options.bosh = bosh;
}
return options;
}
/**
* Closes connection.
*

View File

@ -1,6 +1,7 @@
/* @flow */
import { assign, ReducerRegistry, set } from '../redux';
import { parseURIString } from '../util';
import {
CONNECTION_DISCONNECTED,
@ -119,38 +120,38 @@ function _connectionWillConnect(
/**
* Constructs options to be passed to the constructor of {@code JitsiConnection}
* based on a specific domain.
* based on a specific location URL.
*
* @param {string} domain - The domain with which the returned options are to be
* populated.
* @param {string} locationURL - The location URL with which the returned
* options are to be constructed.
* @private
* @returns {Object}
* @returns {Object} The options to be passed to the constructor of
* {@code JitsiConnection} based on the location URL.
*/
function _constructOptions(domain: string) {
function _constructOptions(locationURL: URL) {
const locationURI = parseURIString(locationURL.href);
// FIXME The HTTPS scheme for the BOSH URL works with meet.jit.si on both
// mobile & Web. It also works with beta.meet.jit.si on Web. Unfortunately,
// it doesn't work with beta.meet.jit.si on mobile. Temporarily, use the
// HTTP scheme for the BOSH URL with beta.meet.jit.si on mobile.
let boshProtocol;
let { protocol } = locationURI;
const domain = locationURI.hostname;
if (domain === 'beta.meet.jit.si') {
if (typeof window === 'object') {
if (!protocol && domain === 'beta.meet.jit.si') {
const windowLocation = window.location;
if (windowLocation) {
// React Native doesn't have a window.location at the time of
// this writing, let alone a window.location.protocol.
boshProtocol = windowLocation.protocol;
}
}
boshProtocol || (boshProtocol = 'http:');
windowLocation && (protocol = windowLocation.protocol);
protocol || (protocol = 'http:');
}
// Default to the HTTPS scheme for the BOSH URL.
boshProtocol || (boshProtocol = 'https:');
protocol || (protocol = 'https:');
return {
bosh: `${String(boshProtocol)}//${domain}/http-bind`,
bosh:
`${String(protocol)}//${domain}${locationURI.contextRoot || '/'
}http-bind`,
hosts: {
domain,
@ -176,6 +177,6 @@ function _setLocationURL(
{ locationURL }: { locationURL: ?URL }) {
return assign(state, {
locationURL,
options: locationURL ? _constructOptions(locationURL.host) : undefined
options: locationURL ? _constructOptions(locationURL) : undefined
});
}

View File

@ -1,2 +1,3 @@
export * from './loadScript';
export * from './randomUtil';
export * from './uri';

View File

@ -0,0 +1,264 @@
/* @flow */
/**
* The {@link RegExp} pattern of the authority of a URI.
*
* @private
* @type {string}
*/
const _URI_AUTHORITY_PATTERN = '(//[^/?#]+)';
/**
* The {@link RegExp} pattern of the path of a URI.
*
* @private
* @type {string}
*/
const _URI_PATH_PATTERN = '([^?#]*)';
/**
* The {@link RegExp} pattern of the protocol of a URI.
*
* @private
* @type {string}
*/
const _URI_PROTOCOL_PATTERN = '([a-z][a-z0-9\\.\\+-]*:)';
/**
* Fixes the hier-part of a specific URI (string) so that the URI is well-known.
* For example, certain Jitsi Meet deployments are not conventional but it is
* possible to translate their URLs into conventional.
*
* @param {string} uri - The URI (string) to fix the hier-part of.
* @private
* @returns {string}
*/
function _fixURIStringHierPart(uri) {
// Rewrite the specified URL in order to handle special cases such as
// hipchat.com and enso.me which do not follow the common pattern of most
// Jitsi Meet deployments.
// hipchat.com
let regex
= new RegExp(
`^${_URI_PROTOCOL_PATTERN}//hipchat\\.com/video/call/`,
'gi');
let match = regex.exec(uri);
if (!match) {
// enso.me
regex
= new RegExp(
`^${_URI_PROTOCOL_PATTERN}//enso\\.me/(?:call|meeting)/`,
'gi');
match = regex.exec(uri);
}
if (match) {
/* eslint-disable no-param-reassign, prefer-template */
uri
= match[1] /* protocol */
+ '//enso.hipchat.me/'
+ uri.substring(regex.lastIndex); /* room (name) */
/* eslint-enable no-param-reassign, prefer-template */
}
return uri;
}
/**
* Fixes the scheme part of a specific URI (string) so that it contains a
* well-known scheme such as HTTP(S). For example, the mobile app implements an
* app-specific URI scheme in addition to Universal Links. The app-specific
* scheme may precede or replace the well-known scheme. In such a case, dealing
* with the app-specific scheme only complicates the logic and it is simpler to
* get rid of it (by translating the app-specific scheme into a well-known
* scheme).
*
* @param {string} uri - The URI (string) to fix the scheme of.
* @private
* @returns {string}
*/
function _fixURIStringScheme(uri: string) {
const regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}+`, 'gi');
const match = regex.exec(uri);
if (match) {
// As an implementation convenience, pick up the last scheme and make
// sure that it is a well-known one.
let protocol = match[match.length - 1].toLowerCase();
if (protocol !== 'http:' && protocol !== 'https:') {
protocol = 'https:';
}
/* eslint-disable no-param-reassign */
uri = uri.substring(regex.lastIndex);
if (uri.startsWith('//')) {
// The specified URL was not a room name only, it contained an
// authority.
uri = protocol + uri;
}
/* eslint-enable no-param-reassign */
}
return uri;
}
/**
* Gets the (Web application) context root defined by a specific location (URI).
*
* @param {Object} location - The location (URI) which defines the (Web
* application) context root.
* @public
* @returns {string} - The (Web application) context root defined by the
* specified {@code location} (URI).
*/
export function getLocationContextRoot(location: Object) {
const pathname = location.pathname;
const contextRootEndIndex = pathname.lastIndexOf('/');
return (
contextRootEndIndex === -1
? '/'
: pathname.substring(0, contextRootEndIndex + 1));
}
/**
* Parses a specific URI string into an object with the well-known properties of
* the {@link Location} and/or {@link URL} interfaces implemented by Web
* browsers. The parsing attempts to be in accord with IETF's RFC 3986.
*
* @param {string} str - The URI string to parse.
* @public
* @returns {{
* hash: string,
* host: (string|undefined),
* hostname: (string|undefined),
* pathname: string,
* port: (string|undefined),
* protocol: (string|undefined),
* search: string
* }}
*/
export function parseStandardURIString(str: string) {
/* eslint-disable no-param-reassign */
const obj = {};
let regex;
let match;
// protocol
regex = new RegExp(`^${_URI_PROTOCOL_PATTERN}`, 'gi');
match = regex.exec(str);
if (match) {
obj.protocol = match[1].toLowerCase();
str = str.substring(regex.lastIndex);
}
// authority
regex = new RegExp(`^${_URI_AUTHORITY_PATTERN}`, 'gi');
match = regex.exec(str);
if (match) {
let authority = match[1].substring(/* // */ 2);
str = str.substring(regex.lastIndex);
// userinfo
const userinfoEndIndex = authority.indexOf('@');
if (userinfoEndIndex !== -1) {
authority = authority.substring(userinfoEndIndex + 1);
}
obj.host = authority;
// port
const portBeginIndex = authority.lastIndexOf(':');
if (portBeginIndex !== -1) {
obj.port = authority.substring(portBeginIndex + 1);
authority = authority.substring(0, portBeginIndex);
}
// hostname
obj.hostname = authority;
}
// pathname
regex = new RegExp(`^${_URI_PATH_PATTERN}`, 'gi');
match = regex.exec(str);
let pathname;
if (match) {
pathname = match[1];
str = str.substring(regex.lastIndex);
}
if (pathname) {
if (!pathname.startsWith('/')) {
pathname = `/${pathname}`;
}
} else {
pathname = '/';
}
obj.pathname = pathname;
// query
if (str.startsWith('?')) {
let hashBeginIndex = str.indexOf('#', 1);
if (hashBeginIndex === -1) {
hashBeginIndex = str.length;
}
obj.search = str.substring(0, hashBeginIndex);
str = str.substring(hashBeginIndex);
} else {
obj.search = ''; // Google Chrome
}
// fragment
obj.hash = str.startsWith('#') ? str : '';
/* eslint-enable no-param-reassign */
return obj;
}
/**
* Parses a specific URI which (supposedly) references a Jitsi Meet resource
* (location).
*
* @param {(string|undefined)} uri - The URI to parse which (supposedly)
* references a Jitsi Meet resource (location).
* @public
* @returns {{
* room: (string|undefined)
* }}
*/
export function parseURIString(uri: ?string) {
if (typeof uri !== 'string') {
return undefined;
}
const obj
= parseStandardURIString(
_fixURIStringHierPart(_fixURIStringScheme(uri)));
// Add the properties that are specific to a Jitsi Meet resource (location)
// such as contextRoot, room:
// contextRoot
obj.contextRoot = getLocationContextRoot(obj);
// The room (name) is the last component of pathname.
const { pathname } = obj;
obj.room = pathname.substring(pathname.lastIndexOf('/') + 1) || undefined;
return obj;
}