From eb19f94598488fd64cc2be322f97b4f63c43d5c2 Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Fri, 13 Apr 2018 19:00:40 -0500 Subject: [PATCH] Deeplinking (#2760) * feat(Deeplinking): Implement for web. * ref(unsupported_browser): Move the mobile version to deeplinking feature * feat(deeplinking_mobile): Redesign. * fix(deeplinking): Use interface.NATIVE_APP_NAME. * feat(dial_in_summary): Add the PIN to the number link. * fix(deep_linking): Handle use case when there isn't deep linking image. * fix(deep_linking): css * fix(deep_linking): deeplink -> "deep linking" * fix(deeplinking_css): Remove position: fixed * docs(deeplinking): Add comment for the openWebApp action. --- css/deep-linking/_desktop.scss | 75 ++++++++ css/deep-linking/_main.scss | 3 + .../_mobile.scss} | 42 ++++- .../_no-mobile-app.scss | 0 css/main.scss | 1 + css/unsupported-browser/_main.scss | 2 - images/logo-deep-linking.png | Bin 0 -> 18584 bytes interface_config.js | 4 +- lang/main.json | 14 +- react/features/app/functions.web.js | 80 +++------ react/features/app/middleware.js | 4 +- .../web}/HideNotificationBarStyle.js | 0 .../base/react/components/web/index.js | 2 + react/features/base/util/uri.js | 2 +- .../conference/components/Conference.web.js | 2 +- react/features/deep-linking/actionTypes.js | 20 +++ react/features/deep-linking/actions.js | 34 ++++ .../components/DeepLinkingDesktopPage.js | 165 ++++++++++++++++++ .../components/DeepLinkingMobilePage.js} | 91 +++++----- .../components/NoMobileApp.js | 6 +- .../features/deep-linking/components/index.js | 3 + react/features/deep-linking/constants.js | 6 + react/features/deep-linking/functions.js | 80 +++++++++ react/features/deep-linking/index.js | 4 + react/features/deep-linking/middleware.js | 23 +++ react/features/deep-linking/reducer.js | 18 ++ .../shouldShowDeepLinkingDesktopPage.js | 9 + .../dial-in-summary/DialInSummary.web.js | 1 + .../dial-in-summary/NumbersList.web.js | 7 +- .../unsupported-browser/actionTypes.js | 16 -- react/features/unsupported-browser/actions.js | 21 --- .../components/UnsupportedDesktopBrowser.js | 3 +- .../unsupported-browser/components/index.js | 5 - react/features/unsupported-browser/index.js | 2 - react/features/unsupported-browser/reducer.js | 27 --- .../welcome/components/WelcomePage.web.js | 3 +- 36 files changed, 571 insertions(+), 204 deletions(-) create mode 100644 css/deep-linking/_desktop.scss create mode 100644 css/deep-linking/_main.scss rename css/{unsupported-browser/_unsupported-mobile-browser.scss => deep-linking/_mobile.scss} (69%) rename css/{unsupported-browser => deep-linking}/_no-mobile-app.scss (100%) create mode 100644 images/logo-deep-linking.png rename react/features/{unsupported-browser/components => base/react/components/web}/HideNotificationBarStyle.js (100%) create mode 100644 react/features/deep-linking/actionTypes.js create mode 100644 react/features/deep-linking/actions.js create mode 100644 react/features/deep-linking/components/DeepLinkingDesktopPage.js rename react/features/{unsupported-browser/components/UnsupportedMobileBrowser.js => deep-linking/components/DeepLinkingMobilePage.js} (54%) rename react/features/{unsupported-browser => deep-linking}/components/NoMobileApp.js (80%) create mode 100644 react/features/deep-linking/components/index.js create mode 100644 react/features/deep-linking/constants.js create mode 100644 react/features/deep-linking/functions.js create mode 100644 react/features/deep-linking/index.js create mode 100644 react/features/deep-linking/middleware.js create mode 100644 react/features/deep-linking/reducer.js create mode 100644 react/features/deep-linking/shouldShowDeepLinkingDesktopPage.js delete mode 100644 react/features/unsupported-browser/actionTypes.js delete mode 100644 react/features/unsupported-browser/actions.js delete mode 100644 react/features/unsupported-browser/reducer.js diff --git a/css/deep-linking/_desktop.scss b/css/deep-linking/_desktop.scss new file mode 100644 index 000000000..9ece9999c --- /dev/null +++ b/css/deep-linking/_desktop.scss @@ -0,0 +1,75 @@ +.deep-linking-desktop { + background-color: #fff; + width: 100%; + height: 100%; + display: flex; + flex-flow: column; + .header { + width: 100%; + height: 55px; + background-color: #f1f2f5; + padding-top: 15px; + padding-left: 50px; + display: flex; + flex-flow: row; + flex: 0 0 55px; + .logo { + height: 40px; + } + } + .content { + padding-top: 40px; + padding-bottom: 40px; + left: 0px; + right: 0px; + display: flex; + width: 100%; + height: 100%; + flex-flow: row; + .leftColumn { + left: 0px; + width: 50%; + min-height: 156px; + display: flex; + flex-flow: column; + .leftColumnContent{ + padding: 20px; + display: flex; + flex-flow: column; + height: 100%; + .image { + background-image: url('../images/deep-linking-image.png'); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + height: 100%; + width: 100%; + } + } + + } + .rightColumn { + top: 0px; + width: 50%; + min-height: 156px; + display: flex; + flex-flow: row; + align-items: center; + .rightColumnContent { + display: flex; + flex-flow: column; + padding: 20px 20px 20px 60px; + .title { + color: #1c2946; + } + .description { + color: #606a80; + margin-top: 8px; + } + .buttons { + margin-top: 16px; + } + } + } + } +} diff --git a/css/deep-linking/_main.scss b/css/deep-linking/_main.scss new file mode 100644 index 000000000..262088ecf --- /dev/null +++ b/css/deep-linking/_main.scss @@ -0,0 +1,3 @@ +@import 'desktop'; +@import 'mobile'; +@import 'no-mobile-app'; diff --git a/css/unsupported-browser/_unsupported-mobile-browser.scss b/css/deep-linking/_mobile.scss similarity index 69% rename from css/unsupported-browser/_unsupported-mobile-browser.scss rename to css/deep-linking/_mobile.scss index 0ab5dbda1..d8ccd4d68 100644 --- a/css/unsupported-browser/_unsupported-mobile-browser.scss +++ b/css/deep-linking/_mobile.scss @@ -1,10 +1,23 @@ -.unsupported-mobile-browser { +.deep-linking-mobile { background-color: #fff; height: 100vh; overflow: auto; position: relative; width: 100vw; + .header { + width: 100%; + height: 70px; + background-color: #f1f2f5; + text-align: center; + .logo { + margin-top: 15px; + margin-left: auto; + margin-right: auto; + height: 40px; + } + } + a { text-decoration: none } @@ -20,10 +33,19 @@ a:active { text-decoration: none; } + + .image { + max-width: 80%; + } + } + + &__text { + font-weight: bolder; + padding: 10px 10px 0px 10px; } &__text, - .unsupported-dial-in { + .deep-linking-dial-in { font-size: 1.2em; line-height: em(29px, 21px); margin-bottom: 0.65em; @@ -39,21 +61,27 @@ } } - &__logo { - height: 108px; - width: 77px; + &__href { + height: 2.2857142857142856em; + line-height: 2.2857142857142856em; + margin: 18px auto 20px; + max-width: 300px; + width: auto; + font-weight: bolder; } &__button { border: 0; height: 2.2857142857142856em; line-height: 2.2857142857142856em; - margin: 18px auto 20px; + margin: 18px auto 10px; + padding: 0px 10px 0px 10px; max-width: 300px; width: auto; @include border-radius(3px); background-color: $unsupportedBrowserButtonBgColor; color: #505F79; + font-weight: bold; &:active { background-color: $unsupportedBrowserButtonBgColor; @@ -69,7 +97,7 @@ } } - .unsupported-dial-in { + .deep-linking-dial-in { display: none; &.has-numbers { diff --git a/css/unsupported-browser/_no-mobile-app.scss b/css/deep-linking/_no-mobile-app.scss similarity index 100% rename from css/unsupported-browser/_no-mobile-app.scss rename to css/deep-linking/_no-mobile-app.scss diff --git a/css/main.scss b/css/main.scss index 4e31c7108..125e34773 100644 --- a/css/main.scss +++ b/css/main.scss @@ -71,5 +71,6 @@ @import 'unsupported-browser/main'; @import 'modals/invite/add-people'; @import 'vertical_filmstrip_overrides'; +@import 'deep-linking/main'; /* Modules END */ diff --git a/css/unsupported-browser/_main.scss b/css/unsupported-browser/_main.scss index 726706763..d40c011d4 100644 --- a/css/unsupported-browser/_main.scss +++ b/css/unsupported-browser/_main.scss @@ -1,3 +1 @@ -@import 'no-mobile-app'; @import 'unsupported-desktop-browser'; -@import 'unsupported-mobile-browser'; diff --git a/images/logo-deep-linking.png b/images/logo-deep-linking.png new file mode 100644 index 0000000000000000000000000000000000000000..b11108f8b19667b5adfea5b3229015c126657c0b GIT binary patch literal 18584 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 * @type {Function[]} */ const _INTERCEPT_COMPONENT_RULES = [ - - /** - * This rule describes case when user opens application using mobile - * browser and is attempting to join a conference. In order to promote the - * app, we choose to suggest the mobile app even if the browser supports the - * app (e.g. Google Chrome with WebRTC support on Android). - * - * @param {Object} state - The redux state of the app. - * @returns {UnsupportedMobileBrowser|void} If the rule is satisfied then - * we should intercept existing component by UnsupportedMobileBrowser. - */ - // eslint-disable-next-line no-unused-vars - state => { - const OS = Platform.OS; - const { room } = state['features/base/conference']; - const isUsingMobileBrowser = OS === 'android' || OS === 'ios'; - - /** - * Checking for presence of a room is done so that interception only - * occurs when trying to enter a meeting but pages outside of meeting, - * like WelcomePage, can still display. - */ - if (room && isUsingMobileBrowser) { - const mobileAppPromo - = typeof interfaceConfig === 'object' - && interfaceConfig.MOBILE_APP_PROMO; - - return ( - typeof mobileAppPromo === 'undefined' || Boolean(mobileAppPromo) - ? UnsupportedMobileBrowser - : NoMobileApp); - } - }, + getDeepLinkingPage, state => { const { webRTCReady } = state['features/base/lib-jitsi-meet']; switch (typeof webRTCReady) { case 'boolean': if (webRTCReady === false) { - return UnsupportedDesktopBrowser; + return Promise.resolve(UnsupportedDesktopBrowser); } break; @@ -76,8 +42,10 @@ const _INTERCEPT_COMPONENT_RULES = [ break; default: - return PluginRequiredBrowser; + return Promise.resolve(PluginRequiredBrowser); } + + return Promise.resolve(); } ]; @@ -87,16 +55,19 @@ const _INTERCEPT_COMPONENT_RULES = [ * * @param {(Object|Function)} stateOrGetState - The redux state or * {@link getState} function. - * @returns {Route} + * @returns {Promise} */ -export function _getRouteToRender(stateOrGetState: Object | Function) { +export function _getRouteToRender(stateOrGetState: Object | Function): Object { const route = _super_getRouteToRender(stateOrGetState); // Intercepts route components if any of component interceptor rules is // satisfied. - route.component = _interceptComponent(stateOrGetState, route.component); + return _interceptComponent(stateOrGetState, route.component).then( + (component: React$Element<*>) => { + route.component = component; - return route; + return route; + }, () => Promise.resolve(route)); } /** @@ -106,23 +77,24 @@ export function _getRouteToRender(stateOrGetState: Object | Function) { * {@link getState} function. * @param {ReactElement} component - Current route component to render. * @private - * @returns {ReactElement} If any of the pre-defined rules is satisfied, returns - * intercepted component. + * @returns {Promise} If any of the pre-defined rules is + * satisfied, returns intercepted component. */ function _interceptComponent( stateOrGetState: Object | Function, component: React$Element<*>) { - let result; const state = toState(stateOrGetState); - for (const rule of _INTERCEPT_COMPONENT_RULES) { - result = rule(state); - if (result) { - break; - } - } + const promises = []; - return result || component; + _INTERCEPT_COMPONENT_RULES.forEach(rule => { + promises.push(rule(state)); + }); + + return Promise.all(promises).then( + results => + results.find(result => typeof result !== 'undefined') || component, + () => Promise.resolve(component)); } /** diff --git a/react/features/app/middleware.js b/react/features/app/middleware.js index 15159352a..a7d0cfd8c 100644 --- a/react/features/app/middleware.js +++ b/react/features/app/middleware.js @@ -77,9 +77,9 @@ function _connectionEstablished(store, next, action) { function _navigate({ getState }) { const state = getState(); const { app } = state['features/app']; - const routeToRender = _getRouteToRender(state); - return app._navigate(routeToRender); + _getRouteToRender(state) + .then(routeToRender => app._navigate(routeToRender)); } /** diff --git a/react/features/unsupported-browser/components/HideNotificationBarStyle.js b/react/features/base/react/components/web/HideNotificationBarStyle.js similarity index 100% rename from react/features/unsupported-browser/components/HideNotificationBarStyle.js rename to react/features/base/react/components/web/HideNotificationBarStyle.js diff --git a/react/features/base/react/components/web/index.js b/react/features/base/react/components/web/index.js index 21916624a..ed07f774d 100644 --- a/react/features/base/react/components/web/index.js +++ b/react/features/base/react/components/web/index.js @@ -1,4 +1,6 @@ export { default as Container } from './Container'; +export { default as HideNotificationBarStyle } + from './HideNotificationBarStyle'; export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete'; export { default as Text } from './Text'; export { default as Watermarks } from './Watermarks'; diff --git a/react/features/base/util/uri.js b/react/features/base/util/uri.js index b742dd347..855bdd44d 100644 --- a/react/features/base/util/uri.js +++ b/react/features/base/util/uri.js @@ -27,7 +27,7 @@ const _URI_PATH_PATTERN = '([^?#]*)'; * * FIXME: The URL class exposed by JavaScript will not include the colon in * the protocol field. Also in other places (at the time of this writing: - * the UnsupportedMobileBrowser.js) the APP_LINK_SCHEME does not include + * the DeepLinkingMobilePage.js) the APP_LINK_SCHEME does not include * the double dots, so things are inconsistent. * * @type {string} diff --git a/react/features/conference/components/Conference.web.js b/react/features/conference/components/Conference.web.js index 5805a4539..1d8acf611 100644 --- a/react/features/conference/components/Conference.web.js +++ b/react/features/conference/components/Conference.web.js @@ -8,6 +8,7 @@ import { connect, disconnect } from '../../base/connection'; import { DialogContainer } from '../../base/dialog'; import { translate } from '../../base/i18n'; import { CalleeInfoContainer } from '../../base/jwt'; +import { HideNotificationBarStyle } from '../../base/react'; import { Filmstrip } from '../../filmstrip'; import { LargeVideo } from '../../large-video'; import { NotificationsContainer } from '../../notifications'; @@ -18,7 +19,6 @@ import { setToolboxAlwaysVisible, showToolbox } from '../../toolbox'; -import { HideNotificationBarStyle } from '../../unsupported-browser'; import { maybeShowSuboptimalExperienceNotification } from '../functions'; diff --git a/react/features/deep-linking/actionTypes.js b/react/features/deep-linking/actionTypes.js new file mode 100644 index 000000000..ece30cac7 --- /dev/null +++ b/react/features/deep-linking/actionTypes.js @@ -0,0 +1,20 @@ +/* @flow */ + +/** + * The type of the action which signals to open the conference in the desktop + * app. + * + * { + * type: OPEN_DESKTOP + * } + */ +export const OPEN_DESKTOP_APP = Symbol('OPEN_DESKTOP_APP'); + +/** + * The type of the action which signals to open the conference in the web app. + * + * { + * type: OPEN_WEB_APP + * } + */ +export const OPEN_WEB_APP = Symbol('OPEN_WEB_APP'); diff --git a/react/features/deep-linking/actions.js b/react/features/deep-linking/actions.js new file mode 100644 index 000000000..005690b38 --- /dev/null +++ b/react/features/deep-linking/actions.js @@ -0,0 +1,34 @@ +/* @flow */ + +import { appNavigate } from '../app'; + +import { OPEN_DESKTOP_APP, OPEN_WEB_APP } from './actionTypes'; + +/** + * Continue to the conference page. + * + * @returns {Function} + */ +export function openWebApp() { + return (dispatch: Dispatch<*>) => { + // In order to go to the web app we need to skip the deep linking + // interceptor. OPEN_WEB_APP action should set launchInWeb to true in + // the redux store. After this when appNavigate() is called the + // deep linking interceptor will be skipped (will return undefined). + dispatch({ type: OPEN_WEB_APP }); + dispatch(appNavigate()); + }; +} + +/** + * Opens the desktop app. + * + * @returns {{ + * type: OPEN_DESKTOP_APP + * }} + */ +export function openDesktopApp() { + return { + type: OPEN_DESKTOP_APP + }; +} diff --git a/react/features/deep-linking/components/DeepLinkingDesktopPage.js b/react/features/deep-linking/components/DeepLinkingDesktopPage.js new file mode 100644 index 000000000..8dd022d40 --- /dev/null +++ b/react/features/deep-linking/components/DeepLinkingDesktopPage.js @@ -0,0 +1,165 @@ +/* @flow */ + +import Button, { ButtonGroup } from '@atlaskit/button'; +import { AtlasKitThemeProvider } from '@atlaskit/theme'; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; + +import { translate } from '../../base/i18n'; + +import { + openWebApp, + openDesktopApp +} from '../actions'; +import { _TNS } from '../constants'; + +declare var interfaceConfig: Object; + +/** + * The type of the React {@code Component} props of + * {@link DeepLinkingDesktopPage}. + */ + type Props = { + + /** + * Used to dispatch actions from the buttons. + */ + dispatch: Dispatch<*>, + + /** + * Used to obtain translations. + */ + t: Function +}; + +/** + * React component representing the deep linking page. + * + * @class DeepLinkingDesktopPage + */ +class DeepLinkingDesktopPage

extends Component

{ + /** + * Initializes a new {@code DeepLinkingDesktopPage} instance. + * + * @param {Object} props - The read-only React {@code Component} props with + * which the new instance is to be initialized. + */ + constructor(props: P) { + super(props); + + // Bind event handlers so they are only bound once per instance. + this._openDesktopApp = this._openDesktopApp.bind(this); + this._onLaunchWeb = this._onLaunchWeb.bind(this); + this._onTryAgain = this._onTryAgain.bind(this); + } + + /** + * Implements the Component's componentDidMount method. + * + * @inheritdoc + */ + componentDidMount() { + this._openDesktopApp(); + } + + /** + * Renders the component. + * + * @returns {ReactElement} + */ + render() { + const { t } = this.props; + const { NATIVE_APP_NAME, SHOW_DEEP_LINKING_IMAGE } = interfaceConfig; + const rightColumnStyle + = SHOW_DEEP_LINKING_IMAGE ? null : { width: '100%' }; + + return ( + + // Enabling light theme because of the color of the buttons. + +

+
+ +
+
+ { + SHOW_DEEP_LINKING_IMAGE + ?
+
+
+
+
: null + } +
+
+

+ { + t(`${_TNS}.title`, + { app: NATIVE_APP_NAME }) + } +

+

+ { + t(`${_TNS}.description`, + { app: NATIVE_APP_NAME }) + } +

+
+ + + + +
+
+
+
+
+ + ); + } + + _openDesktopApp: () => {} + + /** + * Dispatches the openDesktopApp action. + * + * @returns {void} + */ + _openDesktopApp() { + this.props.dispatch(openDesktopApp()); + } + + _onTryAgain: () => {} + + /** + * Handles try again button clicks. + * + * @returns {void} + */ + _onTryAgain() { + this._openDesktopApp(); + } + + _onLaunchWeb: () => {} + + /** + * Handles launch web button clicks. + * + * @returns {void} + */ + _onLaunchWeb() { + this.props.dispatch(openWebApp()); + } +} + +export default translate(connect()(DeepLinkingDesktopPage)); diff --git a/react/features/unsupported-browser/components/UnsupportedMobileBrowser.js b/react/features/deep-linking/components/DeepLinkingMobilePage.js similarity index 54% rename from react/features/unsupported-browser/components/UnsupportedMobileBrowser.js rename to react/features/deep-linking/components/DeepLinkingMobilePage.js index 7d0381a39..ffbea5172 100644 --- a/react/features/unsupported-browser/components/UnsupportedMobileBrowser.js +++ b/react/features/deep-linking/components/DeepLinkingMobilePage.js @@ -5,28 +5,21 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { translate, translateToHTML } from '../../base/i18n'; -import { Platform } from '../../base/react'; -import { URI_PROTOCOL_PATTERN } from '../../base/util'; +import { HideNotificationBarStyle, Platform } from '../../base/react'; import { DialInSummary } from '../../invite'; -import HideNotificationBarStyle from './HideNotificationBarStyle'; + +import { _TNS } from '../constants'; +import { generateDeepLinkingURL } from '../functions'; declare var interfaceConfig: Object; /** - * The namespace of the CSS styles of UnsupportedMobileBrowser. + * The namespace of the CSS styles of DeepLinkingMobilePage. * * @private * @type {string} */ -const _SNS = 'unsupported-mobile-browser'; - -/** - * The namespace of the i18n/translation keys of UnsupportedMobileBrowser. - * - * @private - * @type {string} - */ -const _TNS = 'unsupportedBrowser'; +const _SNS = 'deep-linking-mobile'; /** * The map of platforms to URLs at which the mobile app for the associated @@ -45,13 +38,13 @@ const _URLS = { /** * React component representing mobile browser page. * - * @class UnsupportedMobileBrowser + * @class DeepLinkingMobilePage */ -class UnsupportedMobileBrowser extends Component<*, *> { +class DeepLinkingMobilePage extends Component<*, *> { state: Object; /** - * UnsupportedMobileBrowser component's property types. + * DeepLinkingMobilePage component's property types. * * @static */ @@ -77,20 +70,8 @@ class UnsupportedMobileBrowser extends Component<*, *> { * @inheritdoc */ componentWillMount() { - // If the user installed the app while this Component was displayed - // (e.g. the user clicked the Download the App button), then we would - // like to open the current URL in the mobile app. The only way to do it - // appears to be a link with an app-specific scheme, not a Universal - // Link. - const appScheme = interfaceConfig.MOBILE_APP_SCHEME || 'org.jitsi.meet'; - - // Replace the protocol part with the app scheme. - const joinURL - = window.location.href.replace( - new RegExp(`^${URI_PROTOCOL_PATTERN}`), `${appScheme}:`); - this.setState({ - joinURL + joinURL: generateDeepLinkingURL() }); } @@ -102,43 +83,49 @@ class UnsupportedMobileBrowser extends Component<*, *> { */ render() { const { _room, t } = this.props; - - const openAppButtonClassName + const { NATIVE_APP_NAME, SHOW_DEEP_LINKING_IMAGE } = interfaceConfig; + const downloadButtonClassName = `${_SNS}__button ${_SNS}__button_primary`; - const appName - = interfaceConfig.ADD_PEOPLE_APP_NAME || interfaceConfig.APP_NAME; return (
-
+
+ className = 'logo' + src = '../images/logo-deep-linking.png' /> +
+
+ { + SHOW_DEEP_LINKING_IMAGE + ? + : null + }

{ translateToHTML( t, `${_TNS}.appNotInstalled`, - { app: appName }) + { app: NATIVE_APP_NAME }) }

- - - - - { _room - ? - : null } + + {/* */} + +
@@ -148,7 +135,7 @@ class UnsupportedMobileBrowser extends Component<*, *> { /** * Maps (parts of) the Redux state to the associated props for the - * {@code UnsupportedMobileBrowser} component. + * {@code DeepLinkingMobilePage} component. * * @param {Object} state - The Redux state. * @private @@ -162,4 +149,4 @@ function _mapStateToProps(state) { }; } -export default translate(connect(_mapStateToProps)(UnsupportedMobileBrowser)); +export default translate(connect(_mapStateToProps)(DeepLinkingMobilePage)); diff --git a/react/features/unsupported-browser/components/NoMobileApp.js b/react/features/deep-linking/components/NoMobileApp.js similarity index 80% rename from react/features/unsupported-browser/components/NoMobileApp.js rename to react/features/deep-linking/components/NoMobileApp.js index 029ad81cf..f5e7f223a 100644 --- a/react/features/unsupported-browser/components/NoMobileApp.js +++ b/react/features/deep-linking/components/NoMobileApp.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; -import HideNotificationBarStyle from './HideNotificationBarStyle'; +import { HideNotificationBarStyle } from '../../base/react'; declare var interfaceConfig: Object; @@ -26,8 +26,8 @@ export default class NoMobileApp extends Component<*> { Video chat isn't available on mobile.

- Please use { interfaceConfig.APP_NAME } on desktop to join - calls. + Please use { interfaceConfig.NATIVE_APP_NAME } on desktop to + join calls.

diff --git a/react/features/deep-linking/components/index.js b/react/features/deep-linking/components/index.js new file mode 100644 index 000000000..45fcc430f --- /dev/null +++ b/react/features/deep-linking/components/index.js @@ -0,0 +1,3 @@ +export { default as DeepLinkingDesktopPage } from './DeepLinkingDesktopPage'; +export { default as DeepLinkingMobilePage } from './DeepLinkingMobilePage'; +export { default as NoMobileApp } from './NoMobileApp'; diff --git a/react/features/deep-linking/constants.js b/react/features/deep-linking/constants.js new file mode 100644 index 000000000..b41e8e7f7 --- /dev/null +++ b/react/features/deep-linking/constants.js @@ -0,0 +1,6 @@ +/** + * The namespace of the i18n/translation keys. + * + * @type {string} + */ +export const _TNS = 'deepLinking'; diff --git a/react/features/deep-linking/functions.js b/react/features/deep-linking/functions.js new file mode 100644 index 000000000..5f4f32abd --- /dev/null +++ b/react/features/deep-linking/functions.js @@ -0,0 +1,80 @@ +/* global interfaceConfig */ + +import { URI_PROTOCOL_PATTERN } from '../base/util'; +import { Platform } from '../base/react'; + +import { + DeepLinkingDesktopPage, + DeepLinkingMobilePage, + NoMobileApp +} from './components'; +import { _shouldShowDeepLinkingDesktopPage } + from './shouldShowDeepLinkingDesktopPage'; + +/** + * Generates a deep linking URL based on the current window URL. + * + * @returns {string} - The generated URL. + */ +export function generateDeepLinkingURL() { + // If the user installed the app while this Component was displayed + // (e.g. the user clicked the Download the App button), then we would + // like to open the current URL in the mobile app. The only way to do it + // appears to be a link with an app-specific scheme, not a Universal + // Link. + const appScheme = interfaceConfig.APP_SCHEME || 'org.jitsi.meet'; + + // Replace the protocol part with the app scheme. + + return window.location.href.replace( + new RegExp(`^${URI_PROTOCOL_PATTERN}`), `${appScheme}:`); +} + +/** + * Resolves with the component that should be displayed if the deep linking page + * should be shown and with undefined otherwise. + * + * @param {Object} state - Object containing current redux state. + * @returns {Promise} + */ +export function getDeepLinkingPage(state) { + const { room } = state['features/base/conference']; + + // Show only if we are about to join a conference. + if (!room) { + return Promise.resolve(); + } + + const OS = Platform.OS; + const isUsingMobileBrowser = OS === 'android' || OS === 'ios'; + + if (isUsingMobileBrowser) { // mobile + const mobileAppPromo + = typeof interfaceConfig === 'object' + && interfaceConfig.MOBILE_APP_PROMO; + + return Promise.resolve( + typeof mobileAppPromo === 'undefined' || Boolean(mobileAppPromo) + ? DeepLinkingMobilePage : NoMobileApp); + } + + // desktop + const { launchInWeb } = state['features/deep-linking']; + + if (launchInWeb) { + return Promise.resolve(); + } + + return _shouldShowDeepLinkingDesktopPage().then( + // eslint-disable-next-line no-confusing-arrow + show => show ? DeepLinkingDesktopPage : undefined); +} + +/** + * Opens the desktop app. + * + * @returns {void} + */ +export function openDesktopApp() { + window.location.href = generateDeepLinkingURL(); +} diff --git a/react/features/deep-linking/index.js b/react/features/deep-linking/index.js new file mode 100644 index 000000000..c19d5eb7b --- /dev/null +++ b/react/features/deep-linking/index.js @@ -0,0 +1,4 @@ +export * from './functions'; + +import './middleware'; +import './reducer'; diff --git a/react/features/deep-linking/middleware.js b/react/features/deep-linking/middleware.js new file mode 100644 index 000000000..6facca567 --- /dev/null +++ b/react/features/deep-linking/middleware.js @@ -0,0 +1,23 @@ +// @flow + +import { MiddlewareRegistry } from '../base/redux'; + +import { OPEN_DESKTOP_APP } from './actionTypes'; +import { openDesktopApp } from './functions'; + +/** + * Implements the middleware of the deep linking feature. + * + * @param {Store} store - The redux store. + * @returns {Function} + */ +// eslint-disable-next-line no-unused-vars +MiddlewareRegistry.register(store => next => action => { + switch (action.type) { + case OPEN_DESKTOP_APP: + openDesktopApp(); + break; + } + + return next(action); +}); diff --git a/react/features/deep-linking/reducer.js b/react/features/deep-linking/reducer.js new file mode 100644 index 000000000..2ef34a041 --- /dev/null +++ b/react/features/deep-linking/reducer.js @@ -0,0 +1,18 @@ +/* @flow */ + +import { ReducerRegistry } from '../base/redux'; + +import { OPEN_WEB_APP } from './actionTypes'; + +ReducerRegistry.register('features/deep-linking', (state = {}, action) => { + switch (action.type) { + case OPEN_WEB_APP: { + return { + ...state, + launchInWeb: true + }; + } + } + + return state; +}); diff --git a/react/features/deep-linking/shouldShowDeepLinkingDesktopPage.js b/react/features/deep-linking/shouldShowDeepLinkingDesktopPage.js new file mode 100644 index 000000000..2f04305e5 --- /dev/null +++ b/react/features/deep-linking/shouldShowDeepLinkingDesktopPage.js @@ -0,0 +1,9 @@ +/** + * Resolves with true if the deep linking page should be shown and with + * false otherwise. + * + * @returns {Promise} + */ +export function _shouldShowDeepLinkingDesktopPage() { + return Promise.resolve(false); +} diff --git a/react/features/invite/components/dial-in-summary/DialInSummary.web.js b/react/features/invite/components/dial-in-summary/DialInSummary.web.js index 95bd9cae0..4cb565496 100644 --- a/react/features/invite/components/dial-in-summary/DialInSummary.web.js +++ b/react/features/invite/components/dial-in-summary/DialInSummary.web.js @@ -129,6 +129,7 @@ class DialInSummary extends Component { : null, ]; diff --git a/react/features/invite/components/dial-in-summary/NumbersList.web.js b/react/features/invite/components/dial-in-summary/NumbersList.web.js index 490aae7a3..f8556c316 100644 --- a/react/features/invite/components/dial-in-summary/NumbersList.web.js +++ b/react/features/invite/components/dial-in-summary/NumbersList.web.js @@ -21,6 +21,11 @@ class NumbersList extends Component { */ clickableNumbers: PropTypes.bool, + /** + * The conference ID for dialing in. + */ + conferenceID: PropTypes.number, + /** * The phone numbers to display. Can be an array of numbers * or an object with countries as keys and an array of numbers @@ -136,7 +141,7 @@ class NumbersList extends Component { if (this.props.clickableNumbers) { return ( { number } diff --git a/react/features/unsupported-browser/actionTypes.js b/react/features/unsupported-browser/actionTypes.js deleted file mode 100644 index 95d9af9a8..000000000 --- a/react/features/unsupported-browser/actionTypes.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * The type of the Redux action which signals that the React Component - * UnsupportedMobileBrowser which was rendered as a promotion of the mobile app - * on a browser was dismissed by the user. For example, the Web app may possibly - * run in Google Chrome on Android but we choose to promote the mobile app - * anyway claiming the user experience provided by the Web app is inferior to - * that of the mobile app. Eventually, the user may choose to dismiss the - * promotion of the mobile app and take their chances with the Web app instead. - * If unused, then we have chosen to force the mobile app and not allow the Web - * app in mobile browsers. - * - * { - * type: DISMISS_MOBILE_APP_PROMO - * } - */ -export const DISMISS_MOBILE_APP_PROMO = Symbol('DISMISS_MOBILE_APP_PROMO'); diff --git a/react/features/unsupported-browser/actions.js b/react/features/unsupported-browser/actions.js deleted file mode 100644 index c07c8a7d5..000000000 --- a/react/features/unsupported-browser/actions.js +++ /dev/null @@ -1,21 +0,0 @@ -import { DISMISS_MOBILE_APP_PROMO } from './actionTypes'; - -/** - * Returns a Redux action which signals that the UnsupportedMobileBrowser which - * was rendered as a promotion of the mobile app on a browser was dismissed by - * the user. For example, the Web app may possibly run in Google Chrome - * on Android but we choose to promote the mobile app anyway claiming the user - * experience provided by the Web app is inferior to that of the mobile app. - * Eventually, the user may choose to dismiss the promotion of the mobile app - * and take their chances with the Web app instead. If unused, then we have - * chosen to force the mobile app and not allow the Web app in mobile browsers. - * - * @returns {{ - * type: DISMISS_MOBILE_APP_PROMO - * }} - */ -export function dismissMobileAppPromo() { - return { - type: DISMISS_MOBILE_APP_PROMO - }; -} diff --git a/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js b/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js index 8473bc6e4..e395106c1 100644 --- a/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js +++ b/react/features/unsupported-browser/components/UnsupportedDesktopBrowser.js @@ -4,10 +4,9 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { translate } from '../../base/i18n'; -import { Platform } from '../../base/react'; +import { HideNotificationBarStyle, Platform } from '../../base/react'; import { CHROME, FIREFOX, IE, SAFARI } from './browserLinks'; -import HideNotificationBarStyle from './HideNotificationBarStyle'; /** * The namespace of the CSS styles of UnsupportedDesktopBrowser. diff --git a/react/features/unsupported-browser/components/index.js b/react/features/unsupported-browser/components/index.js index 34be115bf..142fbf841 100644 --- a/react/features/unsupported-browser/components/index.js +++ b/react/features/unsupported-browser/components/index.js @@ -1,8 +1,3 @@ -export { default as HideNotificationBarStyle } - from './HideNotificationBarStyle'; -export { default as NoMobileApp } from './NoMobileApp'; export { default as PluginRequiredBrowser } from './PluginRequiredBrowser'; export { default as UnsupportedDesktopBrowser } from './UnsupportedDesktopBrowser'; -export { default as UnsupportedMobileBrowser } - from './UnsupportedMobileBrowser'; diff --git a/react/features/unsupported-browser/index.js b/react/features/unsupported-browser/index.js index 7f0ef0251..7ce19e0e6 100644 --- a/react/features/unsupported-browser/index.js +++ b/react/features/unsupported-browser/index.js @@ -1,5 +1,3 @@ -export * from './actions'; export * from './components'; import './middleware'; -import './reducer'; diff --git a/react/features/unsupported-browser/reducer.js b/react/features/unsupported-browser/reducer.js deleted file mode 100644 index 72fc5393a..000000000 --- a/react/features/unsupported-browser/reducer.js +++ /dev/null @@ -1,27 +0,0 @@ -import { ReducerRegistry } from '../base/redux'; - -import { DISMISS_MOBILE_APP_PROMO } from './actionTypes'; - -ReducerRegistry.register( - 'features/unsupported-browser', - (state = {}, action) => { - switch (action.type) { - case DISMISS_MOBILE_APP_PROMO: - return { - ...state, - - /** - * The indicator which determines whether the React - * Component UnsupportedMobileBrowser which was rendered as - * a promotion of the mobile app on a browser was dismissed - * by the user. If unused, then we have chosen to force the - * mobile app and not allow the Web app in mobile browsers. - * - * @type {boolean} - */ - mobileAppPromoDismissed: true - }; - } - - return state; - }); diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index aeb3c981d..a84576c91 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -9,8 +9,7 @@ import { connect } from 'react-redux'; import { initAnalytics } from '../../analytics'; import { translate } from '../../base/i18n'; import { isAnalyticsEnabled } from '../../base/lib-jitsi-meet'; -import { Watermarks } from '../../base/react'; -import { HideNotificationBarStyle } from '../../unsupported-browser'; +import { HideNotificationBarStyle, Watermarks } from '../../base/react'; import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';