diff --git a/conference.js b/conference.js index ffda92fb2..736b231b8 100644 --- a/conference.js +++ b/conference.js @@ -203,10 +203,8 @@ function maybeRedirectToWelcomePage(options) { // save whether current user is guest or not, before navigating // to close page window.sessionStorage.setItem('guest', APP.tokenData.isGuest); - if (options.feedbackSubmitted) - window.location.pathname = "close.html"; - else - window.location.pathname = "close2.html"; + assignWindowLocationPathname( + options.feedbackSubmitted ? "close.html" : "close2.html"); return; } @@ -219,11 +217,42 @@ function maybeRedirectToWelcomePage(options) { if (config.enableWelcomePage) { setTimeout(() => { APP.settings.setWelcomePageEnabled(true); - window.location.pathname = "/"; + assignWindowLocationPathname('./'); }, 3000); } } +/** + * Assigns a specific pathname to window.location.pathname taking into account + * the context root of the Web app. + * + * @param {string} pathname - The pathname to assign to + * window.location.pathname. If the specified pathname is relative, the context + * root of the Web app will be prepended to the specified pathname before + * assigning it to window.location.pathname. + * @return {void} + */ +function assignWindowLocationPathname(pathname) { + const windowLocation = window.location; + + if (!pathname.startsWith('/')) { + // XXX To support a deployment in a sub-directory, assume that the room + // (name) is the last non-directory component of the path (name). + let contextRoot = windowLocation.pathname; + + contextRoot + = contextRoot.substring(0, contextRoot.lastIndexOf('/') + 1); + + // A pathname equal to ./ specifies the current directory. It will be + // fine but pointless to include it because contextRoot is the current + // directory. + pathname.startsWith('./') && (pathname = pathname.substring(2)); + pathname = contextRoot + pathname; + } + + windowLocation.pathname = pathname; +} + /** * Create local tracks of specified types. * @param {Object} options @@ -323,7 +352,7 @@ class ConferenceConnector { case ConferenceErrors.NOT_ALLOWED_ERROR: { // let's show some auth not allowed page - window.location.pathname = "authError.html"; + assignWindowLocationPathname('authError.html'); } break; diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.js index 5162ac232..787cdb567 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.js @@ -38,7 +38,7 @@ export class AbstractApp extends Component { } /** - * Initializes a new App instance. + * Initializes a new AbstractApp instance. * * @param {Object} props - The read-only React Component props with which * the new instance is to be initialized. @@ -334,13 +334,7 @@ export class AbstractApp extends Component { // (2) A replace function would be provided to the Route in case it // chose to redirect to another path. this._onRouteEnter(route, nextState, pathname => { - // FIXME In order to minimize the modifications related to the - // removal of react-router, the Web implementation is provided - // bellow because the replace function is used on Web only at the - // time of this writing. Provide a platform-agnostic implementation. - // It should likely find the best Route matching the specified - // pathname and navigate to it. - window.location.pathname = pathname; + this._openURL(pathname); // Do not proceed with the route because it chose to redirect to // another path. diff --git a/react/features/app/components/App.web.js b/react/features/app/components/App.web.js index 9afe0a606..38cdfed01 100644 --- a/react/features/app/components/App.web.js +++ b/react/features/app/components/App.web.js @@ -14,6 +14,27 @@ export class App extends AbstractApp { */ static propTypes = AbstractApp.propTypes + /** + * Initializes a new App instance. + * + * @param {Object} props - The read-only React Component props with which + * the new instance is to be initialized. + */ + constructor(props) { + super(props); + + this.state = { + ...this.state, + + /** + * The context root of window.location i.e. this Web App. + * + * @type {string} + */ + windowLocationContextRoot: this._getWindowLocationContextRoot() + }; + } + /** * Inits the app before component will mount. * @@ -35,6 +56,22 @@ export class App extends AbstractApp { return window.location; } + /** + * Gets the context root of this Web App from window.location. + * + * @private + * @returns {string} The context root of window.location i.e. this Web App. + */ + _getWindowLocationContextRoot() { + const pathname = this._getWindowLocation().pathname; + const contextRootEndIndex = pathname.lastIndexOf('/'); + + return ( + contextRootEndIndex === -1 + ? '/' + : pathname.substring(0, contextRootEndIndex + 1)); + } + /** * Navigates to a specific Route (via platform-specific means). * @@ -53,6 +90,7 @@ export class App extends AbstractApp { = path.replace( /:room/g, store.getState()['features/base/conference'].room); + path = this._routePath2WindowLocationPathname(path); // Navigate to the specified Route. const windowLocation = this._getWindowLocation(); @@ -69,4 +107,22 @@ export class App extends AbstractApp { windowLocation.pathname = path; } } + + /** + * Converts a specific Route path to a window.location.pathname. + * + * @param {string} path - A Route path to be converted to/represeted as a + * window.location.pathname. + * @private + * @returns {string} A window.location.pathname-compatible representation of + * the specified Route path. + */ + _routePath2WindowLocationPathname(path) { + let pathname = this.state.windowLocationContextRoot; + + pathname.endsWith('/') || (pathname += '/'); + pathname += path.startsWith('/') ? path.substring(1) : path; + + return pathname; + } } diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index 9e03c6849..d501129c7 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -76,15 +76,18 @@ class WelcomePage extends AbstractWelcomePage { } /** - * Returns the domain name. + * Returns the URL of this WelcomePage for display purposes. For + * historic/legacy reasons, the return value is referred to as domain. * * @private - * @returns {string} Domain name. + * @returns {string} The URL of this WelcomePage for display purposes. */ _getDomain() { - const windowLocation = window.location; + // As the returned URL is for display purposes, do not return the + // userinfo, query and fragment URI parts. + const wl = window.location; - return `${windowLocation.protocol}//${windowLocation.host}/`; + return `${wl.protocol}//${wl.host}${wl.pathname}`; } /** diff --git a/utils.js b/utils.js index 00192dbef..860f03257 100644 --- a/utils.js +++ b/utils.js @@ -10,26 +10,22 @@ * Builds and returns the room name. */ function getRoomName () { // eslint-disable-line no-unused-vars + var getroomnode = config.getroomnode; var path = window.location.pathname; var roomName; - // determinde the room node from the url - // TODO: just the roomnode or the whole bare jid? - if (config.getroomnode && typeof config.getroomnode === 'function') { + // Determine the room node from the URL. + if (getroomnode && typeof getroomnode === 'function') { // custom function might be responsible for doing the pushstate - roomName = config.getroomnode(path); + roomName = getroomnode.call(config, path); } else { - /* fall back to default strategy - * this is making assumptions about how the URL->room mapping happens. - * It currently assumes deployment at root, with a rewrite like the - * following one (for nginx): - location ~ ^/([a-zA-Z0-9]+)$ { - rewrite ^/(.*)$ / break; - } - */ - if (path.length > 1) { - roomName = path.substr(1).toLowerCase(); - } + // Fall back to the default strategy of making assumptions about how the + // URL maps to the room (name). It currently assumes a deployment in + // which the last non-directory component of the path (name) is the + // room. + roomName + = path.substring(path.lastIndexOf('/') + 1).toLowerCase() + || undefined; } return roomName;