From d388a7bd3c756bcb0fdc0040a2e8cbc16a628f91 Mon Sep 17 00:00:00 2001 From: Hristo Terezov Date: Thu, 7 May 2020 17:26:37 -0500 Subject: [PATCH] feat(reload): Preserve local track mute state. --- .../do_external_connect.js | 2 +- modules/API/constants.js | 6 +-- react/features/app/actions.js | 46 +++++++++++++++---- react/features/base/config/functions.any.js | 7 ++- react/features/base/devices/functions.js | 2 +- react/features/base/jwt/functions.js | 2 +- react/features/base/settings/functions.any.js | 3 +- react/features/base/settings/middleware.js | 2 +- react/features/base/util/index.js | 1 + .../base/{config => util}/parseURLParams.js | 6 +-- react/features/base/util/uri.js | 22 +++++++++ .../calendar-sync/web/microsoftCalendar.js | 3 +- react/features/dropbox/functions.web.js | 4 +- .../dial-in-info-page/DialInInfoApp.web.js | 2 +- .../components/web/DeviceSelectionPopup.js | 2 +- 15 files changed, 81 insertions(+), 29 deletions(-) rename react/features/base/{config => util}/parseURLParams.js (92%) diff --git a/connection_optimization/do_external_connect.js b/connection_optimization/do_external_connect.js index 538e90187..fbfcfdc21 100644 --- a/connection_optimization/do_external_connect.js +++ b/connection_optimization/do_external_connect.js @@ -1,7 +1,7 @@ /* global config, createConnectionExternally */ import getRoomName from '../react/features/base/config/getRoomName'; -import parseURLParams from '../react/features/base/config/parseURLParams'; +import { parseURLParams } from '../react/features/base/util/parseURLParams'; /** * Implements external connect using createConnectionExternally function defined diff --git a/modules/API/constants.js b/modules/API/constants.js index d7a3f2b26..4d5213719 100644 --- a/modules/API/constants.js +++ b/modules/API/constants.js @@ -1,10 +1,10 @@ -// XXX The function parseURLParams is exported by the feature base/config (as +// XXX The function parseURLParams is exported by the feature base/util (as // defined in the terminology of react/). However, this file is (very likely) // bundled in external_api in addition to app.bundle and, consequently, it is // best to import as little as possible here (rather than the whole feature -// base/config) in order to minimize the amount of source code bundled into +// base/util) in order to minimize the amount of source code bundled into // multiple bundles. -import parseURLParams from '../../react/features/base/config/parseURLParams'; +import { parseURLParams } from '../../react/features/base/util/parseURLParams'; /** * JitsiMeetExternalAPI id - unique for a webpage. diff --git a/react/features/app/actions.js b/react/features/app/actions.js index dba35461d..cbddebfd4 100644 --- a/react/features/app/actions.js +++ b/react/features/app/actions.js @@ -13,8 +13,11 @@ import { } from '../base/config'; import { connect, disconnect, setLocationURL } from '../base/connection'; import { loadConfig } from '../base/lib-jitsi-meet'; -import { createDesiredLocalTracks } from '../base/tracks'; +import { MEDIA_TYPE } from '../base/media'; +import { toState } from '../base/redux'; +import { createDesiredLocalTracks, isLocalVideoTrackMuted, isLocalTrackMuted } from '../base/tracks'; import { + addHashParamsToURL, getBackendSafeRoomName, getLocationContextRoot, parseURIString, @@ -191,18 +194,42 @@ export function reloadNow() { return (dispatch: Dispatch, getState: Function) => { dispatch(setFatalError(undefined)); - const { locationURL } = getState()['features/base/connection']; + const state = getState(); + const { locationURL } = state['features/base/connection']; + + // Preserve the local tracks muted state after the reload. + const newURL = addTrackStateToURL(locationURL, state); logger.info(`Reloading the conference using URL: ${locationURL}`); if (navigator.product === 'ReactNative') { - dispatch(appNavigate(toURLString(locationURL))); + dispatch(appNavigate(toURLString(newURL))); } else { dispatch(reloadWithStoredParams()); } }; } +/** + * Adds the current track state to the passed URL. + * + * @param {URL} url - The URL that will be modified. + * @param {Function|Object} stateful - The redux store or {@code getState} function. + * @returns {URL} - Returns the modified URL. + */ +function addTrackStateToURL(url, stateful) { + const state = toState(stateful); + const tracks = state['features/base/tracks']; + const isVideoMuted = isLocalVideoTrackMuted(tracks); + const isAudioMuted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO); + + return addHashParamsToURL(new URL(url), { // use new URL object in order to not pollute the passed parameter. + 'config.startWithAudioMuted': isAudioMuted, + 'config.startWithVideoMuted': isVideoMuted + }); + +} + /** * Reloads the page by restoring the original URL. * @@ -210,17 +237,20 @@ export function reloadNow() { */ export function reloadWithStoredParams() { return (dispatch: Dispatch, getState: Function) => { - const { locationURL } = getState()['features/base/connection']; + const state = getState(); + const { locationURL } = state['features/base/connection']; + + // Preserve the local tracks muted states. + const newURL = addTrackStateToURL(locationURL, state); const windowLocation = window.location; const oldSearchString = windowLocation.search; - windowLocation.replace(locationURL.toString()); + windowLocation.replace(newURL.toString()); - if (window.self !== window.top - && locationURL.search === oldSearchString) { + if (newURL.search === oldSearchString) { // NOTE: Assuming that only the hash or search part of the URL will // be changed! - // location.reload will not trigger redirect/reload for iframe when + // location.replace will not trigger redirect/reload when // only the hash params are changed. That's why we need to call // reload in addition to replace. windowLocation.reload(); diff --git a/react/features/base/config/functions.any.js b/react/features/base/config/functions.any.js index 167c721fe..f5b1c30b4 100644 --- a/react/features/base/config/functions.any.js +++ b/react/features/base/config/functions.any.js @@ -6,15 +6,14 @@ import _ from 'lodash'; import CONFIG_WHITELIST from './configWhitelist'; import { _CONFIG_STORE_PREFIX } from './constants'; import INTERFACE_CONFIG_WHITELIST from './interfaceConfigWhitelist'; -import parseURLParams from './parseURLParams'; +import { parseURLParams } from '../util'; import logger from './logger'; -// XXX The functions getRoomName and parseURLParams are split out of -// functions.js because they are bundled in both app.bundle and +// XXX The function getRoomName is split out of +// functions.js because it is bundled in both app.bundle and // do_external_connect, webpack 1 does not support tree shaking, and we don't // want all functions to be bundled in do_external_connect. export { default as getRoomName } from './getRoomName'; -export { parseURLParams }; /** * Create a "fake" configuration object for the given base URL. This is used in case the config diff --git a/react/features/base/devices/functions.js b/react/features/base/devices/functions.js index 7ca0fe0f3..c001aa387 100644 --- a/react/features/base/devices/functions.js +++ b/react/features/base/devices/functions.js @@ -1,6 +1,6 @@ // @flow -import { parseURLParams } from '../config'; +import { parseURLParams } from '../util'; import JitsiMeetJS from '../lib-jitsi-meet'; import { updateSettings } from '../settings'; diff --git a/react/features/base/jwt/functions.js b/react/features/base/jwt/functions.js index 22aa59f34..45af42688 100644 --- a/react/features/base/jwt/functions.js +++ b/react/features/base/jwt/functions.js @@ -1,6 +1,6 @@ /* @flow */ -import { parseURLParams } from '../config'; +import { parseURLParams } from '../util'; /** * Retrieves the JSON Web Token (JWT), if any, defined by a specific diff --git a/react/features/base/settings/functions.any.js b/react/features/base/settings/functions.any.js index 55a30b72d..9332da794 100644 --- a/react/features/base/settings/functions.any.js +++ b/react/features/base/settings/functions.any.js @@ -1,6 +1,7 @@ // @flow -import { CONFIG_WHITELIST, parseURLParams } from '../config'; +import { CONFIG_WHITELIST } from '../config'; import { toState } from '../redux'; +import { parseURLParams } from '../util'; import { DEFAULT_SERVER_URL } from './constants'; diff --git a/react/features/base/settings/middleware.js b/react/features/base/settings/middleware.js index badf54941..5ab51e727 100644 --- a/react/features/base/settings/middleware.js +++ b/react/features/base/settings/middleware.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import { APP_WILL_MOUNT } from '../app'; import { setAudioOnly } from '../audio-only'; -import parseURLParams from '../config/parseURLParams'; // minimize imports to avoid circular imports +import { parseURLParams } from '../util'; import { SET_LOCATION_URL } from '../connection/actionTypes'; // minimize imports to avoid circular imports import { getLocalParticipant, participantUpdated } from '../participants'; import { MiddlewareRegistry } from '../redux'; diff --git a/react/features/base/util/index.js b/react/features/base/util/index.js index 57946dadb..df5117eb2 100644 --- a/react/features/base/util/index.js +++ b/react/features/base/util/index.js @@ -3,3 +3,4 @@ export * from './httpUtils'; export * from './loadScript'; export * from './openURLInBrowser'; export * from './uri'; +export * from './parseURLParams'; diff --git a/react/features/base/config/parseURLParams.js b/react/features/base/util/parseURLParams.js similarity index 92% rename from react/features/base/config/parseURLParams.js rename to react/features/base/util/parseURLParams.js index 65d014d6c..76e76c3e6 100644 --- a/react/features/base/config/parseURLParams.js +++ b/react/features/base/util/parseURLParams.js @@ -1,19 +1,19 @@ /* @flow */ -import { reportError } from '../util'; +import { reportError } from './helpers'; /** * 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 {URL} url - The URL to parse. * @param {boolean} dontParse - If falsy, some transformations (for parsing the * value as JSON) will be executed. * @param {string} source - If {@code 'search'}, the parameters will parsed out * of {@code url.search}; otherwise, out of {@code url.hash}. * @returns {Object} */ -export default function parseURLParams( +export function parseURLParams( url: URL, dontParse: boolean = false, source: string = 'hash'): Object { diff --git a/react/features/base/util/uri.js b/react/features/base/util/uri.js index 6917d1a94..142afe80f 100644 --- a/react/features/base/util/uri.js +++ b/react/features/base/util/uri.js @@ -1,5 +1,6 @@ // @flow +import { parseURLParams } from './parseURLParams'; import { normalizeNFKC } from './strings'; /** @@ -551,3 +552,24 @@ export function urlObjectToString(o: Object): ?string { return url.toString() || undefined; } + +/** + * Adds hash params to URL. + * + * @param {URL} url - The URL. + * @param {Object} hashParamsToAdd - A map with the parameters to be set. + * @returns {URL} - The new URL. + */ +export function addHashParamsToURL(url: URL, hashParamsToAdd: Object = {}) { + const params = parseURLParams(url); + const urlParamsArray = _objectToURLParamsArray({ + ...params, + ...hashParamsToAdd + }); + + if (urlParamsArray.length) { + url.hash = `#${urlParamsArray.join('&')}`; + } + + return url; +} diff --git a/react/features/calendar-sync/web/microsoftCalendar.js b/react/features/calendar-sync/web/microsoftCalendar.js index 9ab3eaefe..19804a692 100644 --- a/react/features/calendar-sync/web/microsoftCalendar.js +++ b/react/features/calendar-sync/web/microsoftCalendar.js @@ -6,8 +6,7 @@ import type { Dispatch } from 'redux'; import { createDeferred } from '../../../../modules/util/helpers'; -import parseURLParams from '../../base/config/parseURLParams'; -import { parseStandardURIString } from '../../base/util'; +import { parseStandardURIString, parseURLParams } from '../../base/util'; import { getShareInfoText } from '../../invite'; import { setCalendarAPIAuthState } from '../actions'; diff --git a/react/features/dropbox/functions.web.js b/react/features/dropbox/functions.web.js index 2fdfe7e9b..94e2c5052 100644 --- a/react/features/dropbox/functions.web.js +++ b/react/features/dropbox/functions.web.js @@ -2,10 +2,10 @@ import { Dropbox } from 'dropbox'; -import { parseURLParams } from '../base/config'; import { getJitsiMeetGlobalNS, - parseStandardURIString + parseStandardURIString, + parseURLParams } from '../base/util'; /** diff --git a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js index 7d2c90990..4c90b7a95 100644 --- a/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js +++ b/react/features/invite/components/dial-in-info-page/DialInInfoApp.web.js @@ -2,9 +2,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { I18nextProvider } from 'react-i18next'; -import parseURLParams from '../../../base/config/parseURLParams'; import { i18next } from '../../../base/i18n'; import { isMobileBrowser } from '../../../base/environment/utils'; +import { parseURLParams } from '../../../base/util/parseURLParams'; import { DialInSummary } from '../dial-in-summary'; import NoRoomError from './NoRoomError'; diff --git a/react/features/settings/components/web/DeviceSelectionPopup.js b/react/features/settings/components/web/DeviceSelectionPopup.js index 617e13a06..1c6ce2756 100644 --- a/react/features/settings/components/web/DeviceSelectionPopup.js +++ b/react/features/settings/components/web/DeviceSelectionPopup.js @@ -20,8 +20,8 @@ import { setVideoInputDevice } from '../../../../../modules/API/external/functions'; -import parseURLParams from '../../../base/config/parseURLParams'; import DialogWithTabs from '../../../base/dialog/components/web/DialogWithTabs'; +import { parseURLParams } from '../../../base/util/parseURLParams'; import DeviceSelection from '../../../device-selection/components/DeviceSelection'; /**