diff --git a/config.js b/config.js index aba126dff6..1d124aa258 100644 --- a/config.js +++ b/config.js @@ -1091,10 +1091,67 @@ var config = { // use only. // _desktopSharingSourceDevice: 'sample-id-or-label', + // DEPRECATED! Use deeplinking.disabled instead. // If true, any checks to handoff to another application will be prevented // and instead the app will continue to display in the current browser. // disableDeepLinking: false, + // The deeplinking config. + // For information about the properties of + // deeplinking.[ios/android].dynamicLink check: + // https://firebase.google.com/docs/dynamic-links/create-manually + // deeplinking: { + // + // // The desktop deeplinking config. + // desktop: { + // appName: 'Jitsi Meet' + // }, + // // If true, any checks to handoff to another application will be prevented + // // and instead the app will continue to display in the current browser. + // disabled: false, + + // // whether to hide the logo on the deep linking pages. + // hideLogo: false, + + // // whether to show deeplinking image. + // showImage: false, + + // // The ios deeplinking config. + // ios: { + // appName: 'Jitsi Meet', + // // Specify mobile app scheme for opening the app from the mobile browser. + // appScheme: 'org.jitsi.meet', + // // Custom URL for downloading ios mobile app. + // downloadLink: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905', + // dynamicLink: { + // apn: 'org.jitsi.meet', + // appCode: 'w2atb', + // customDomain: undefined, + // ibi: 'com.atlassian.JitsiMeet.ios', + // isi: '1165103905' + // } + // }, + + // // The android deeplinking config. + // android: { + // appName: 'Jitsi Meet', + // // Specify mobile app scheme for opening the app from the mobile browser. + // appScheme: 'org.jitsi.meet', + // // Custom URL for downloading android mobile app. + // downloadLink: 'https://play.google.com/store/apps/details?id=org.jitsi.meet', + // // Android app package name. + // appPackage: 'org.jitsi.meet', + // fDroidUrl: 'https://f-droid.org/en/packages/org.jitsi.meet/', + // dynamicLink: { + // apn: 'org.jitsi.meet', + // appCode: 'w2atb', + // customDomain: undefined, + // ibi: 'com.atlassian.JitsiMeet.ios', + // isi: '1165103905' + // } + // } + // }, + // A property to disable the right click context menu for localVideo // the menu has option to flip the locally seen video for local presentations // disableLocalVideoFlip: false, diff --git a/interface_config.js b/interface_config.js index 9d0e357f63..82da16bdd6 100644 --- a/interface_config.js +++ b/interface_config.js @@ -76,11 +76,6 @@ var interfaceConfig = { GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true, - /** - * Hide the logo on the deep linking pages. - */ - HIDE_DEEP_LINKING_LOGO: false, - /** * Hide the invite prompt in the header when alone in the meeting. */ @@ -108,23 +103,6 @@ var interfaceConfig = { */ MOBILE_APP_PROMO: true, - /** - * Specify custom URL for downloading android mobile app. - */ - MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet', - - /** - * Specify custom URL for downloading f droid app. - */ - MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/', - - /** - * Specify URL for downloading ios mobile app. - */ - MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905', - - NATIVE_APP_NAME: 'Jitsi Meet', - // Names of browsers which should show a warning stating the current browser // has a suboptimal experience. Browsers which are not listed as optimal or // unsupported are considered suboptimal. Valid values are: @@ -159,7 +137,6 @@ var interfaceConfig = { */ SHOW_CHROME_EXTENSION_BANNER: false, - SHOW_DEEP_LINKING_IMAGE: false, SHOW_JITSI_WATERMARK: true, SHOW_POWERED_BY: false, SHOW_PROMOTIONAL_CLOSE_PAGE: false, @@ -200,6 +177,33 @@ var interfaceConfig = { */ // TILE_VIEW_MAX_COLUMNS: 5, + // List of undocumented settings + /** + INDICATOR_FONT_SIZES + PHONE_NUMBER_REGEX + */ + + // -----------------DEPRECATED CONFIGS BELOW THIS LINE----------------------------- + + /** + * Specify URL for downloading ios mobile app. + */ + // MOBILE_DOWNLOAD_LINK_IOS: 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905', + + /** + * Specify custom URL for downloading android mobile app. + */ + // MOBILE_DOWNLOAD_LINK_ANDROID: 'https://play.google.com/store/apps/details?id=org.jitsi.meet', + + // SHOW_DEEP_LINKING_IMAGE: false, + + /** + * Specify mobile app scheme for opening the app from the mobile browser. + */ + // APP_SCHEME: 'org.jitsi.meet', + + // NATIVE_APP_NAME: 'Jitsi Meet', + /** * Specify Firebase dynamic link properties for the mobile apps. */ @@ -212,22 +216,19 @@ var interfaceConfig = { // }, /** - * Specify mobile app scheme for opening the app from the mobile browser. + * Hide the logo on the deep linking pages. */ - // APP_SCHEME: 'org.jitsi.meet', + // HIDE_DEEP_LINKING_LOGO: false, /** * Specify the Android app package name. */ // ANDROID_APP_PACKAGE: 'org.jitsi.meet', - // List of undocumented settings /** - INDICATOR_FONT_SIZES - PHONE_NUMBER_REGEX - */ - - // -----------------DEPRECATED CONFIGS BELOW THIS LINE----------------------------- + * Specify custom URL for downloading f droid app. + */ + // MOBILE_DOWNLOAD_LINK_F_DROID: 'https://f-droid.org/en/packages/org.jitsi.meet/', // Connection indicators ( // CONNECTION_INDICATOR_AUTO_HIDE_ENABLED, diff --git a/react/features/base/config/configType.ts b/react/features/base/config/configType.ts index 8e8323a0be..df96d09fb6 100644 --- a/react/features/base/config/configType.ts +++ b/react/features/base/config/configType.ts @@ -88,6 +88,36 @@ export type Sounds = 'ASKED_TO_UNMUTE_SOUND' | 'RECORDING_ON_SOUND' | 'TALK_WHILE_MUTED_SOUND'; + +export interface IMobileDynamicLink { + apn: string; + appCode: string; + customDomain?: string; + ibi: string; + isi: string; +} + +export interface IDeeplinkingPlatformConfig { + appName: string; +} + +export interface IDeeplinkingMobileConfig extends IDeeplinkingPlatformConfig { + appPackage?: string; + appScheme: string; + downloadLink: string; + dynamicLink?: IMobileDynamicLink; + fDroidUrl?: string; +} + +export interface IDeeplinkingConfig { + android?: IDeeplinkingMobileConfig; + desktop?: IDeeplinkingPlatformConfig; + disabled: boolean; + hideLogo: boolean; + ios?: IDeeplinkingMobileConfig; + showImage: boolean; +} + export interface IConfig { _desktopSharingSourceDevice?: string; analytics?: { @@ -176,6 +206,7 @@ export interface IConfig { }; }; corsAvatarURLs?: Array; + deeplinking?: IDeeplinkingConfig; defaultLanguage?: string; defaultLocalDisplayName?: string; defaultLogoUrl?: string; diff --git a/react/features/base/config/configWhitelist.ts b/react/features/base/config/configWhitelist.ts index be27a9c41e..a4cd334f2b 100644 --- a/react/features/base/config/configWhitelist.ts +++ b/react/features/base/config/configWhitelist.ts @@ -81,6 +81,8 @@ export default [ 'brandingRoomAlias', 'debug', 'debugAudioLevels', + 'deeplinking.disabled', + 'deeplinking.showImage', 'defaultLocalDisplayName', 'defaultRemoteDisplayName', 'deploymentUrls', diff --git a/react/features/base/config/functions.native.ts b/react/features/base/config/functions.native.ts index 584fe1cd8b..c99c30d8f9 100644 --- a/react/features/base/config/functions.native.ts +++ b/react/features/base/config/functions.native.ts @@ -4,7 +4,7 @@ import { IReduxState } from '../../app/types'; import { REPLACE_PARTICIPANT } from '../flags/constants'; import { getFeatureFlag } from '../flags/functions'; -import { IConfig } from './configType'; +import { IConfig, IDeeplinkingConfig } from './configType'; export * from './functions.any'; @@ -42,3 +42,14 @@ export function _cleanupConfig(config: IConfig) { export function getReplaceParticipant(state: IReduxState): string { return getFeatureFlag(state, REPLACE_PARTICIPANT, false); } + +/** + * Sets the defaults for deeplinking. + * + * @param {IDeeplinkingConfig} _deeplinking - The deeplinking config. + * @returns {void} + */ +export function _setDeeplinkingDefaults(_deeplinking: IDeeplinkingConfig) { + return; +} + diff --git a/react/features/base/config/functions.web.ts b/react/features/base/config/functions.web.ts index 21d0cae4bd..7ac3791e80 100644 --- a/react/features/base/config/functions.web.ts +++ b/react/features/base/config/functions.web.ts @@ -1,6 +1,6 @@ import { IReduxState } from '../../app/types'; -import { IConfig } from './configType'; +import { IConfig, IDeeplinkingConfig, IDeeplinkingMobileConfig, IDeeplinkingPlatformConfig } from './configType'; import { TOOLBAR_BUTTONS } from './constants'; export * from './functions.any'; @@ -8,10 +8,11 @@ export * from './functions.any'; /** * Removes all analytics related options from the given configuration, in case of a libre build. * - * @param {*} config - The configuration which needs to be cleaned up. + * @param {*} _config - The configuration which needs to be cleaned up. * @returns {void} */ -export function _cleanupConfig(config: IConfig) { // eslint-disable-line @typescript-eslint/no-unused-vars +export function _cleanupConfig(_config: IConfig) { + return; } /** @@ -60,3 +61,43 @@ export function areAudioLevelsEnabled(state: IReduxState): boolean { // Default to false for React Native as audio levels are of no interest to the mobile app. return navigator.product !== 'ReactNative' && !state['features/base/config'].disableAudioLevels; } + +/** + * Sets the defaults for deeplinking. + * + * @param {IDeeplinkingConfig} deeplinking - The deeplinking config. + * @returns {void} + */ +export function _setDeeplinkingDefaults(deeplinking: IDeeplinkingConfig) { + const { + desktop = {} as IDeeplinkingPlatformConfig, + android = {} as IDeeplinkingMobileConfig, + ios = {} as IDeeplinkingMobileConfig + } = deeplinking; + + desktop.appName = desktop.appName || 'Jitsi Meet'; + + ios.appName = ios.appName || 'Jitsi Meet'; + ios.appScheme = ios.appScheme || 'org.jitsi.meet'; + ios.downloadLink = ios.downloadLink + || 'https://itunes.apple.com/us/app/jitsi-meet/id1165103905'; + if (ios.dynamicLink) { + ios.dynamicLink.apn = ios.dynamicLink.apn || 'org.jitsi.meet'; + ios.dynamicLink.appCode = ios.dynamicLink.appCode || 'w2atb'; + ios.dynamicLink.ibi = ios.dynamicLink.ibi || 'com.atlassian.JitsiMeet.ios'; + ios.dynamicLink.isi = ios.dynamicLink.isi || '1165103905'; + } + + android.appName = android.appName || 'Jitsi Meet'; + android.appScheme = android.appScheme || 'org.jitsi.meet'; + android.downloadLink = android.downloadLink + || 'https://play.google.com/store/apps/details?id=org.jitsi.meet'; + android.appPackage = android.appPackage || 'org.jitsi.meet'; + android.fDroidUrl = android.fDroidUrl || 'https://f-droid.org/en/packages/org.jitsi.meet/'; + if (android.dynamicLink) { + android.dynamicLink.apn = android.dynamicLink.apn || 'org.jitsi.meet'; + android.dynamicLink.appCode = android.dynamicLink.appCode || 'w2atb'; + android.dynamicLink.ibi = android.dynamicLink.ibi || 'com.atlassian.JitsiMeet.ios'; + android.dynamicLink.isi = android.dynamicLink.isi || '1165103905'; + } +} diff --git a/react/features/base/config/reducer.ts b/react/features/base/config/reducer.ts index 02dd914eae..81d875ebb7 100644 --- a/react/features/base/config/reducer.ts +++ b/react/features/base/config/reducer.ts @@ -11,8 +11,14 @@ import { SET_CONFIG, UPDATE_CONFIG } from './actionTypes'; -import { IConfig } from './configType'; -import { _cleanupConfig } from './functions'; +import { + IConfig, + IDeeplinkingConfig, + IDeeplinkingMobileConfig, + IDeeplinkingPlatformConfig, + IMobileDynamicLink +} from './configType'; +import { _cleanupConfig, _setDeeplinkingDefaults } from './functions'; /** * The initial state of the feature base/config when executing in a @@ -292,6 +298,52 @@ function _translateInterfaceConfig(oldValue: IConfig) { } } + // if we have `deeplinking` defined, ignore deprecated values. Otherwise, compose the config. + if (!oldValue.deeplinking) { + const disabled = Boolean(oldValue.disableDeepLinking); + const deeplinking: IDeeplinkingConfig = { + desktop: {} as IDeeplinkingPlatformConfig, + hideLogo: false, + disabled, + showImage: false, + android: {} as IDeeplinkingMobileConfig, + ios: {} as IDeeplinkingMobileConfig + }; + + if (typeof interfaceConfig === 'object') { + const mobileDynamicLink = interfaceConfig.MOBILE_DYNAMIC_LINK; + const dynamicLink: IMobileDynamicLink | undefined = mobileDynamicLink ? { + apn: mobileDynamicLink.APN, + appCode: mobileDynamicLink.APP_CODE, + ibi: mobileDynamicLink.IBI, + isi: mobileDynamicLink.ISI, + customDomain: mobileDynamicLink.CUSTOM_DOMAIN + } : undefined; + + if (deeplinking.desktop) { + deeplinking.desktop.appName = interfaceConfig.NATIVE_APP_NAME; + } + + deeplinking.hideLogo = Boolean(interfaceConfig.HIDE_DEEP_LINKING_LOGO); + deeplinking.showImage = interfaceConfig.SHOW_DEEP_LINKING_IMAGE; + deeplinking.android = { + appName: interfaceConfig.NATIVE_APP_NAME, + appScheme: interfaceConfig.APP_SCHEME, + downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_ANDROID, + appPackage: interfaceConfig.ANDROID_APP_PACKAGE, + fDroidUrl: interfaceConfig.MOBILE_DOWNLOAD_LINK_F_DROID, + dynamicLink + }; + deeplinking.ios = { + appName: interfaceConfig.NATIVE_APP_NAME, + appScheme: interfaceConfig.APP_SCHEME, + downloadLink: interfaceConfig.MOBILE_DOWNLOAD_LINK_IOS, + dynamicLink + }; + } + newValue.deeplinking = deeplinking; + } + return newValue; } @@ -480,6 +532,8 @@ function _translateLegacyConfig(oldValue: IConfig) { }; } + _setDeeplinkingDefaults(newValue.deeplinking as IDeeplinkingConfig); + return newValue; } diff --git a/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js b/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js index a54a58eae3..aba9ab559f 100644 --- a/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js +++ b/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js @@ -5,6 +5,7 @@ import React, { Component } from 'react'; import type { Dispatch } from 'redux'; import { createDeepLinkingPageEvent, sendAnalytics } from '../../analytics'; +import { IDeeplinkingConfig } from '../../base/config/configType'; import { isSupportedBrowser } from '../../base/environment'; import { translate } from '../../base/i18n'; import { connect } from '../../base/redux'; @@ -16,14 +17,17 @@ import { } from '../actions'; import { _TNS } from '../constants'; -declare var interfaceConfig: Object; - /** * The type of the React {@code Component} props of * {@link DeepLinkingDesktopPage}. */ type Props = { + /** + * The deeplinking config. + */ + _deeplinkingCfg: IDeeplinkingConfig, + /** * Used to dispatch actions from the buttons. */ @@ -72,10 +76,10 @@ class DeepLinkingDesktopPage

extends Component

{ * @returns {ReactElement} */ render() { - const { t } = this.props; - const { HIDE_DEEP_LINKING_LOGO, NATIVE_APP_NAME, SHOW_DEEP_LINKING_IMAGE } = interfaceConfig; + const { t, _deeplinkingCfg: { desktop = {}, hideLogo, showImage } } = this.props; + const { appName } = desktop; const rightColumnStyle - = SHOW_DEEP_LINKING_IMAGE ? null : { width: '100%' }; + = showImage ? null : { width: '100%' }; return ( @@ -84,7 +88,7 @@ class DeepLinkingDesktopPage

extends Component

{

{ - HIDE_DEEP_LINKING_LOGO + hideLogo ? null : { extends Component

{

{ - SHOW_DEEP_LINKING_IMAGE + showImage ?
@@ -108,7 +112,7 @@ class DeepLinkingDesktopPage

extends Component

{

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

@@ -117,7 +121,7 @@ class DeepLinkingDesktopPage

extends Component

{ `${_TNS}.${isSupportedBrowser() ? 'description' : 'descriptionWithoutWeb'}`, - { app: NATIVE_APP_NAME } + { app: appName } ) }

@@ -171,4 +175,18 @@ class DeepLinkingDesktopPage

extends Component

{ } } -export default translate(connect()(DeepLinkingDesktopPage)); +/** + * Maps (parts of) the Redux state to the associated props for the + * {@code DeepLinkingDesktopPage} component. + * + * @param {Object} state - The Redux state. + * @private + * @returns {Props} + */ +function _mapStateToProps(state) { + return { + _deeplinkingCfg: state['features/base/config'].deeplinking || {} + }; +} + +export default translate(connect(_mapStateToProps)(DeepLinkingDesktopPage)); diff --git a/react/features/deep-linking/components/DeepLinkingMobilePage.web.js b/react/features/deep-linking/components/DeepLinkingMobilePage.web.js index ec239d1d03..98cf1f7142 100644 --- a/react/features/deep-linking/components/DeepLinkingMobilePage.web.js +++ b/react/features/deep-linking/components/DeepLinkingMobilePage.web.js @@ -4,6 +4,7 @@ import React, { Component } from 'react'; import type { Dispatch } from 'redux'; import { createDeepLinkingPageEvent, sendAnalytics } from '../../analytics'; +import { IDeeplinkingConfig, IDeeplinkingMobileConfig } from '../../base/config/configType'; import { isSupportedMobileBrowser } from '../../base/environment'; import { translate } from '../../base/i18n'; import { Platform } from '../../base/react'; @@ -14,8 +15,6 @@ import { _TNS } from '../constants'; import { generateDeepLinkingURL } from '../functions'; import { renderPromotionalFooter } from '../renderPromotionalFooter'; -declare var interfaceConfig: Object; - /** * The namespace of the CSS styles of DeepLinkingMobilePage. * @@ -31,9 +30,19 @@ const _SNS = 'deep-linking-mobile'; type Props = { /** - * Application download URL. + * The deeplinking config. */ - _downloadUrl: ?string, + _deeplinkingCfg: IDeeplinkingConfig, + + /** + * Application mobile deeplinking config. + */ + _mobileConfig: IDeeplinkingMobileConfig, + + /** + * The deeplinking url. + */ + _deepLinkingUrl: string, /** * The name of the conference attempting to being joined. @@ -95,13 +104,19 @@ class DeepLinkingMobilePage extends Component { * @returns {ReactElement} */ render() { - const { _downloadUrl, _room, t, _url } = this.props; - const { HIDE_DEEP_LINKING_LOGO, NATIVE_APP_NAME, SHOW_DEEP_LINKING_IMAGE } = interfaceConfig; + const { + _deeplinkingCfg: { hideLogo, showImage }, + _mobileConfig: { downloadLink, appName }, + _room, + t, + _url, + _deepLinkingUrl + } = this.props; const downloadButtonClassName = `${_SNS}__button ${_SNS}__button_primary`; - const onOpenLinkProperties = _downloadUrl + const onOpenLinkProperties = downloadLink ? { // When opening a link to the download page, we want to let the // OS itself handle intercepting and opening the appropriate @@ -121,7 +136,7 @@ class DeepLinkingMobilePage extends Component {

{ - HIDE_DEEP_LINKING_LOGO + hideLogo ? null : { {
{ - SHOW_DEEP_LINKING_IMAGE + showImage ? { { : null }

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

{ t(`${_TNS}.ifHaveApp`) } @@ -147,7 +162,7 @@ class DeepLinkingMobilePage extends Component {