[RN] Converge with Web's ExternalAPI a bit

Web's ExternalAPI accepts an object with properties as one of its
constructor arguments and from which it generated a URL. Mobile's
JitsiMeetView.loadURLObject is supposed to accept pretty much the same
object.
This commit is contained in:
Lyubo Marinov 2017-07-27 13:50:09 -05:00
parent 9778aabe98
commit e542af28a2
4 changed files with 171 additions and 87 deletions

View File

@ -1,5 +1,6 @@
import EventEmitter from 'events'; import EventEmitter from 'events';
import { urlObjectToString } from '../../../react/features/base/util';
import { import {
PostMessageTransportBackend, PostMessageTransportBackend,
Transport Transport
@ -58,28 +59,6 @@ function changeParticipantNumber(APIInstance, number) {
APIInstance._numberOfParticipants += number; APIInstance._numberOfParticipants += number;
} }
/**
* Generates array with URL params based on the passed config object that will
* be used for the Jitsi Meet URL generation.
*
* @param {Object} config - The config object.
* @returns {Array<string>} The array with URL param strings.
*/
function configToURLParamsArray(config = {}) {
const params = [];
for (const key in config) { // eslint-disable-line guard-for-in
try {
params.push(
`${key}=${encodeURIComponent(JSON.stringify(config[key]))}`);
} catch (e) {
console.warn(`Error encoding ${key}: ${e}`);
}
}
return params;
}
/** /**
* Generates the URL for the iframe. * Generates the URL for the iframe.
* *
@ -92,42 +71,17 @@ function configToURLParamsArray(config = {}) {
* configuration options defined in interface_config.js to be overridden. * configuration options defined in interface_config.js to be overridden.
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for * @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
* authentication. * authentication.
* @param {boolean} [options.noSsl] - If the value is true https won't be used. * @param {boolean} [options.noSSL] - If the value is true https won't be used.
* @param {string} [options.roomName] - The name of the room to join. * @param {string} [options.roomName] - The name of the room to join.
* @returns {string} The URL. * @returns {string} The URL.
*/ */
function generateURL(domain, options = {}) { function generateURL(domain, options = {}) {
const { return urlObjectToString({
configOverwrite, ...options,
interfaceConfigOverwrite, url:
jwt, `${options.noSSL ? 'http' : 'https'}://${domain
noSSL, }/#jitsi_meet_external_api_id=${id}`
roomName });
} = options;
let url = `${noSSL ? 'http' : 'https'}://${domain}/${roomName || ''}`;
if (jwt) {
url += `?jwt=${jwt}`;
}
url += `#jitsi_meet_external_api_id=${id}`;
const configURLParams = configToURLParamsArray(configOverwrite);
if (configURLParams.length) {
url += `&config.${configURLParams.join('&config.')}`;
}
const interfaceConfigURLParams
= configToURLParamsArray(interfaceConfigOverwrite);
if (interfaceConfigURLParams.length) {
url += `&interfaceConfig.${
interfaceConfigURLParams.join('&interfaceConfig.')}`;
}
return url;
} }
/** /**

View File

@ -90,17 +90,7 @@ function _appNavigateToMandatoryLocation(
* @returns {void} * @returns {void}
*/ */
function dispatchSetLocationURL() { function dispatchSetLocationURL() {
dispatch( dispatch(setLocationURL(new URL(newLocation.toString())));
setLocationURL(
new URL(
(newLocation.protocol || 'https:')
// TODO userinfo
+ newLocation.host
+ (newLocation.pathname || '/')
+ newLocation.search
+ newLocation.hash)));
} }
/** /**
@ -147,9 +137,7 @@ function _appNavigateToOptionalLocation(
} }
} }
if (!location.protocol) { location.protocol || (location.protocol = 'https:');
location.protocol = 'https:';
}
_appNavigateToMandatoryLocation(dispatch, getState, location); _appNavigateToMandatoryLocation(dispatch, getState, location);
} }
@ -213,5 +201,7 @@ function _loadConfig(location: Object) {
// TDOO userinfo // TDOO userinfo
return loadConfig(protocol + location.host + (location.contextRoot || '/')); return (
loadConfig(
`${protocol}//${location.host}${location.contextRoot || '/'}`));
} }

View File

@ -1,13 +1,14 @@
/* @flow */ /* @flow */
/** /**
* Parses the parameters from the URL and returns them as a JS object. * Parses the query/search or fragment/hash parameters out of a specific URL and
* returns them as a JS object.
* *
* @param {string} url - The URL to parse. * @param {string} url - The URL to parse.
* @param {boolean} dontParse - If false or undefined some transformations * @param {boolean} dontParse - If falsy, some transformations (for parsing the
* (for parsing the value as JSON) are going to be executed. * value as JSON) will be executed.
* @param {string} source - Values - "hash"/"search" if "search" the parameters * @param {string} source - If {@code 'search'}, the parameters will parsed out
* will parsed from location.search otherwise from location.hash. * of {@code url.search}; otherwise, out of {@code url.hash}.
* @returns {Object} * @returns {Object}
*/ */
export default function parseURLParams( export default function parseURLParams(

View File

@ -1,5 +1,3 @@
/* @flow */
/** /**
* The {@link RegExp} pattern of the authority of a URI. * The {@link RegExp} pattern of the authority of a URI.
* *
@ -127,6 +125,30 @@ export function getLocationContextRoot(location: Object) {
: pathname.substring(0, contextRootEndIndex + 1)); : pathname.substring(0, contextRootEndIndex + 1));
} }
/**
* Constructs a new {@code Array} with URL parameter {@code String}s out of a
* specific {@code Object}.
*
* @param {Object} obj - The {@code Object} to turn into URL parameter
* {@code String}s.
* @returns {Array<string>} The {@code Array} with URL parameter {@code String}s
* constructed out of the specified {@code obj}.
*/
function _objectToURLParamsArray(obj = {}) {
const params = [];
for (const key in obj) { // eslint-disable-line guard-for-in
try {
params.push(
`${key}=${encodeURIComponent(JSON.stringify(obj[key]))}`);
} catch (e) {
console.warn(`Error encoding ${key}: ${e}`);
}
}
return params;
}
/** /**
* Parses a specific URI string into an object with the well-known properties of * 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 * the {@link Location} and/or {@link URL} interfaces implemented by Web
@ -147,7 +169,9 @@ export function getLocationContextRoot(location: Object) {
export function parseStandardURIString(str: string) { export function parseStandardURIString(str: string) {
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
const obj = {}; const obj = {
toString: _standardURIToString
};
let regex; let regex;
let match; let match;
@ -200,9 +224,7 @@ export function parseStandardURIString(str: string) {
str = str.substring(regex.lastIndex); str = str.substring(regex.lastIndex);
} }
if (pathname) { if (pathname) {
if (!pathname.startsWith('/')) { pathname.startsWith('/') || (pathname = `/${pathname}`);
pathname = `/${pathname}`;
}
} else { } else {
pathname = '/'; pathname = '/';
} }
@ -263,6 +285,32 @@ export function parseURIString(uri: ?string) {
return obj; return obj;
} }
/**
* Implements {@code href} and {@code toString} for the {@code Object} returned
* by {@link #parseStandardURIString}.
*
* @param {Object} [thiz] - An {@code Object} returned by
* {@code #parseStandardURIString} if any; otherwise, it is presumed that the
* function is invoked on such an instance.
* @returns {string}
*/
function _standardURIToString(thiz: ?Object) {
// eslint-disable-next-line no-invalid-this
const { hash, host, pathname, protocol, search } = thiz || this;
let str = '';
protocol && (str += protocol);
// TODO userinfo
host && (str += `//${host}`);
str += pathname || '/';
search && (str += search);
hash && (str += hash);
return str;
}
/** /**
* Attempts to return a {@code String} representation of a specific * Attempts to return a {@code String} representation of a specific
* {@code Object} which is supposed to represent a URL. Obviously, if a * {@code Object} which is supposed to represent a URL. Obviously, if a
@ -285,7 +333,7 @@ export function toURLString(obj: ?(string | Object)): ?string {
if (obj instanceof URL) { if (obj instanceof URL) {
str = obj.href; str = obj.href;
} else { } else {
str = _urlObjectToString(obj); str = urlObjectToString(obj);
} }
} }
break; break;
@ -303,12 +351,103 @@ export function toURLString(obj: ?(string | Object)): ?string {
* {@code Object} similar to the one accepted by the constructor * {@code Object} similar to the one accepted by the constructor
* of Web's ExternalAPI. * of Web's ExternalAPI.
* *
* @param {Object} obj - The URL to return a {@code String} representation of. * @param {Object} o - The URL to return a {@code String} representation of.
* @returns {string} - A {@code String} representation of the specified * @returns {string} - A {@code String} representation of the specified
* {@code obj}. * {@code Object}.
*/ */
function _urlObjectToString({ url }: Object): ?string { export function urlObjectToString(o: Object): ?string {
// TODO Support properties other than url. Support (pretty much) all const url = parseStandardURIString(o.url || '');
// properties accepted by the constructor of Web's ExternalAPI.
return url; // protocol
if (!url.protocol) {
let protocol = o.protocol || o.scheme;
if (protocol) {
// Protocol is supposed to be the scheme and the final ':'. Anyway,
// do not make a fuss if the final ':' is not there.
protocol.endsWith(':') || (protocol += ':');
url.protocol = protocol;
}
}
// authority & pathname
let { pathname } = url;
if (!url.host) {
// Web's ExternalAPI domain
//
// It may be host/hostname and pathname with the latter denoting the
// tenant.
const { host, hostname, pathname: contextRoot, port }
= parseStandardURIString(o.domain || o.host || o.hostname);
// authority
if (host) {
url.host = host;
url.hostname = hostname;
url.port = port;
}
// pathname
pathname === '/' && contextRoot !== '/' && (pathname = contextRoot);
}
// pathname
// Web's ExternalAPI roomName
const room = o.roomName || o.room;
if (room
&& (url.pathname.endsWith('/')
|| !url.pathname.endsWith(`/${room}`))) {
pathname.endsWith('/') || (pathname += '/');
pathname += room;
}
url.pathname = pathname;
// query/search
// Web's ExternalAPI jwt
const { jwt } = o;
if (jwt) {
let { search } = url;
if (search.indexOf('?jwt=') === -1 && search.indexOf('&jwt=') === -1) {
search.startsWith('?') || (search = `?${search}`);
search.length === 1 || (search += '&');
search += `jwt=${jwt}`;
url.search = search;
}
}
// fragment/hash
let { hash } = url;
for (const configName of [ 'config', 'interfaceConfig' ]) {
const urlParamsArray
= _objectToURLParamsArray(
o[`${configName}Overwrite`]
|| o[configName]
|| o[`${configName}Override`]);
if (urlParamsArray.length) {
let urlParamsString
= `${configName}.${urlParamsArray.join(`&${configName}.`)}`;
if (hash.length) {
urlParamsString = `&${urlParamsString}`;
} else {
hash = '#';
}
hash += urlParamsString;
}
}
url.hash = hash;
return url.toString() || undefined;
} }