diff --git a/Makefile b/Makefile index 040f8c9774..32d14a9d7e 100644 --- a/Makefile +++ b/Makefile @@ -48,8 +48,6 @@ deploy-appbundle: $(BUILD_DIR)/do_external_connect.min.js.map \ $(BUILD_DIR)/external_api.min.js \ $(BUILD_DIR)/external_api.min.js.map \ - $(BUILD_DIR)/dial_in_info_bundle.min.js \ - $(BUILD_DIR)/dial_in_info_bundle.min.js.map \ $(BUILD_DIR)/alwaysontop.min.js \ $(BUILD_DIR)/alwaysontop.min.js.map \ $(OUTPUT_DIR)/analytics-ga.js \ diff --git a/config.js b/config.js index 422d2f1b01..02647606ba 100644 --- a/config.js +++ b/config.js @@ -1146,6 +1146,13 @@ var config = { // } // }, + // // The terms, privacy and help centre URL's. + // legalUrls: { + // helpCentre: 'https://web-cdn.jitsi.net/faq/meet-faq.html', + // privacy: 'https://jitsi.org/meet/privacy', + // terms: 'https://jitsi.org/meet/terms' + // }, + // 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/css/_variables.scss b/css/_variables.scss index dfe48669b0..40fdebf487 100644 --- a/css/_variables.scss +++ b/css/_variables.scss @@ -201,11 +201,6 @@ $deepLinkingDialInConferenceIdPadding: inherit; $deepLinkingDialInConferenceIdBackgroundColor: inherit; $deepLinkingDialInConferenceIdBorderRadius: inherit; -$deepLinkingDialInConferenceNameFontSize: inherit; -$deepLinkingDialInConferenceNameLineHeight: inherit; -$deepLinkingDialInConferenceNameMarginBottom: none; -$deepLinkingDialInConferenceNameFontWeight: inherit; - $deepLinkingDialInConferenceDescriptionFontSize: 0.8em; $deepLinkingDialInConferenceDescriptionLineHeight: inherit; $deepLinkingDialInConferenceDescriptionMarginBottom: none; diff --git a/css/deep-linking/_mobile.scss b/css/deep-linking/_mobile.scss index bab9f9b8f6..0b93e45e89 100644 --- a/css/deep-linking/_mobile.scss +++ b/css/deep-linking/_mobile.scss @@ -67,6 +67,13 @@ font-size: 1em; } + + .dial-in-conference-id { + text-align: center; + min-width: 200px; + margin-top: 40px; + } + .dial-in-conference-id { margin: $deepLinkingDialInConferenceIdMargin; padding: $deepLinkingDialInConferenceIdPadding; @@ -74,24 +81,12 @@ border-radius: $deepLinkingDialInConferenceIdBorderRadius; } - .dial-in-conference-name { - font-size: $deepLinkingDialInConferenceNameFontSize; - line-height: $deepLinkingDialInConferenceNameLineHeight; - margin-bottom: $deepLinkingDialInConferenceNameMarginBottom; - font-weight: $deepLinkingDialInConferenceNameFontWeight; - } - .dial-in-conference-description { font-size: $deepLinkingDialInConferenceDescriptionFontSize; line-height: $deepLinkingDialInConferenceDescriptionLineHeight; margin-bottom: $deepLinkingDialInConferenceDescriptionMarginBottom; } - .dial-in-conference-pin { - font-size: $deepLinkingDialInConferencePinFontSize; - line-height: $deepLinkingDialInConferencePinLineHeight; - } - .toll-free-list { min-width: 80px; } diff --git a/css/modals/invite/_info.scss b/css/modals/invite/_info.scss index 7992206c8c..cf233f9b0b 100644 --- a/css/modals/invite/_info.scss +++ b/css/modals/invite/_info.scss @@ -50,6 +50,8 @@ } .dial-in-numbers-list { + max-width: 334px; + width: 100%; margin-top: 20px; font-size: 12px; line-height: 24px; @@ -59,10 +61,6 @@ text-align: left; } - tr { - border-bottom: 1px solid #d1dbe8; - } - .flag-cell { vertical-align: top; width: 30px; @@ -91,6 +89,7 @@ font-weight: bold; list-style: none; vertical-align: top; + text-align: right; } li.toll-free:empty:before { @@ -119,11 +118,6 @@ margin-top: 40px; } - .dial-in-conference-name, - .dial-in-conference-pin { - font-size: 18px; - } - .dial-in-conference-description { margin: 12px; } diff --git a/images/logo-deep-linking-mobile.png b/images/logo-deep-linking-mobile.png new file mode 100644 index 0000000000..4225c83d2c Binary files /dev/null and b/images/logo-deep-linking-mobile.png differ diff --git a/images/logo-deep-linking.png b/images/logo-deep-linking.png index b11108f8b1..69ca9c5bad 100644 Binary files a/images/logo-deep-linking.png and b/images/logo-deep-linking.png differ diff --git a/lang/main.json b/lang/main.json index b1d082913e..8a8d8abefc 100644 --- a/lang/main.json +++ b/lang/main.json @@ -184,13 +184,21 @@ "deepLinking": { "appNotInstalled": "You need the {{app}} mobile app to join this meeting on your phone.", "description": "Nothing happened? We tried launching your meeting in the {{app}} desktop app. Try again or launch it in the {{app}} web app.", + "descriptionNew": "Nothing happened? We tried launching your meeting in the {{app}} desktop app.

You can try again or launch it on web.", "descriptionWithoutWeb": "Nothing happened? We tried launching your meeting in the {{app}} desktop app.", "downloadApp": "Download the app", + "downloadMobileApp": "Download from App Store", "ifDoNotHaveApp": "If you don't have the app yet:", "ifHaveApp": "If you already have the app:", "joinInApp": "Join this meeting using the app", + "joinInAppNew": "Join in app", + "joinInBrowser": "Join in browser", + "launchMeetingLabel": "How do you want to join this meeting?", "launchWebButton": "Launch in web", + "noMobileApp": "You don’t have the app?", + "termsAndConditions": "By continuing you agree to our terms & conditions.", "title": "Launching your meeting in {{app}}...", + "titleNew": "Launching your meeting ...", "tryAgainButton": "Try again in desktop", "unsupportedBrowser": "It looks like you're using a browser we don't support." }, diff --git a/react/features/base/config/configType.ts b/react/features/base/config/configType.ts index 63dbbfbe8d..e2bbf9109e 100644 --- a/react/features/base/config/configType.ts +++ b/react/features/base/config/configType.ts @@ -388,6 +388,11 @@ export interface IConfig { lastNLimits?: { [key: number]: number; }; + legalUrls?: { + helpCentre: string; + privacy: string; + terms: string; + }; liveStreaming?: { dataPrivacyLink?: string; enabled?: boolean; diff --git a/react/features/base/config/constants.ts b/react/features/base/config/constants.ts index 34bd5f51e7..6358c1ecf2 100644 --- a/react/features/base/config/constants.ts +++ b/react/features/base/config/constants.ts @@ -70,3 +70,18 @@ export const THIRD_PARTY_PREJOIN_BUTTONS = [ 'microphone', 'camera', 'select-bac export const FEATURE_FLAGS = { SSRC_REWRITING: 'ssrcRewritingEnabled' }; + +/** + * The URL at which the terms (of service/use) are available to the user. + */ +export const DEFAULT_TERMS_URL = 'https://jitsi.org/meet/terms'; + +/** + * The URL at which the privacy policy is available to the user. + */ +export const DEFAULT_PRIVACY_URL = 'https://jitsi.org/meet/privacy'; + +/** + * The URL at which the help centre is available to the user. + */ +export const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html'; diff --git a/react/features/base/config/functions.any.ts b/react/features/base/config/functions.any.ts index 0d71bd2cd8..3f8049f3b1 100644 --- a/react/features/base/config/functions.any.ts +++ b/react/features/base/config/functions.any.ts @@ -11,7 +11,13 @@ import { parseURLParams } from '../util/parseURLParams'; import { IConfig } from './configType'; import CONFIG_WHITELIST from './configWhitelist'; -import { FEATURE_FLAGS, _CONFIG_STORE_PREFIX } from './constants'; +import { + DEFAULT_HELP_CENTRE_URL, + DEFAULT_PRIVACY_URL, + DEFAULT_TERMS_URL, + FEATURE_FLAGS, + _CONFIG_STORE_PREFIX +} from './constants'; import INTERFACE_CONFIG_WHITELIST from './interfaceConfigWhitelist'; import logger from './logger'; @@ -326,3 +332,24 @@ export function getDialOutUrl(state: IReduxState) { export function getSecurityUiConfig(state: IReduxState) { return state['features/base/config']?.securityUi || {}; } + +/** + * Returns the terms, privacy and help centre URL's. + * + * @param {IReduxState} state - The state of the application. + * @returns {{ + * privacy: string, + * helpCentre: string, + * terms: string + * }} + */ +export function getLegalUrls(state: IReduxState) { + const helpCentreURL = state['features/base/config']?.helpCentreURL; + const configLegalUrls = state['features/base/config']?.legalUrls; + + return { + privacy: configLegalUrls?.privacy || DEFAULT_PRIVACY_URL, + helpCentre: helpCentreURL || configLegalUrls?.helpCentre || DEFAULT_HELP_CENTRE_URL, + terms: configLegalUrls?.terms || DEFAULT_TERMS_URL + }; +} diff --git a/react/features/deep-linking/actions.js b/react/features/deep-linking/actions.ts similarity index 98% rename from react/features/deep-linking/actions.js rename to react/features/deep-linking/actions.ts index 54f5f249b3..f686e8270c 100644 --- a/react/features/deep-linking/actions.js +++ b/react/features/deep-linking/actions.ts @@ -1,5 +1,3 @@ -// @flow - import type { Dispatch } from 'redux'; import { appNavigate } from '../app/actions'; diff --git a/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js b/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js deleted file mode 100644 index aba9ab559f..0000000000 --- a/react/features/deep-linking/components/DeepLinkingDesktopPage.web.js +++ /dev/null @@ -1,192 +0,0 @@ -// @flow - -import { AtlasKitThemeProvider } from '@atlaskit/theme'; -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'; -import Button from '../../base/ui/components/web/Button'; -import { BUTTON_TYPES } from '../../base/ui/constants.web'; -import { - openDesktopApp, - openWebApp -} from '../actions'; -import { _TNS } from '../constants'; - -/** - * 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. - */ - 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._onLaunchWeb = this._onLaunchWeb.bind(this); - this._onTryAgain = this._onTryAgain.bind(this); - } - - /** - * Implements the Component's componentDidMount method. - * - * @inheritdoc - */ - componentDidMount() { - sendAnalytics( - createDeepLinkingPageEvent( - 'displayed', 'DeepLinkingDesktop', { isMobileBrowser: false })); - } - - /** - * Renders the component. - * - * @returns {ReactElement} - */ - render() { - const { t, _deeplinkingCfg: { desktop = {}, hideLogo, showImage } } = this.props; - const { appName } = desktop; - const rightColumnStyle - = showImage ? null : { width: '100%' }; - - return ( - - // Enabling light theme because of the color of the buttons. - -

-
- { - hideLogo - ? null - : { - } -
-
- { - showImage - ?
-
-
-
-
: null - } -
-
-

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

-

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

-
-
-
-
-
-
- - ); - } - - _onTryAgain: () => void; - - /** - * Handles try again button clicks. - * - * @returns {void} - */ - _onTryAgain() { - sendAnalytics( - createDeepLinkingPageEvent( - 'clicked', 'tryAgainButton', { isMobileBrowser: false })); - this.props.dispatch(openDesktopApp()); - } - - _onLaunchWeb: () => void; - - /** - * Handles launch web button clicks. - * - * @returns {void} - */ - _onLaunchWeb() { - sendAnalytics( - createDeepLinkingPageEvent( - 'clicked', 'launchWebButton', { isMobileBrowser: false })); - this.props.dispatch(openWebApp()); - } -} - -/** - * 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/DeepLinkingDesktopPage.web.tsx b/react/features/deep-linking/components/DeepLinkingDesktopPage.web.tsx new file mode 100644 index 0000000000..31d50650cd --- /dev/null +++ b/react/features/deep-linking/components/DeepLinkingDesktopPage.web.tsx @@ -0,0 +1,159 @@ +import { Theme } from '@mui/material'; +import React, { useCallback, useEffect } from 'react'; +import { WithTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { makeStyles } from 'tss-react/mui'; + +import { createDeepLinkingPageEvent } from '../../analytics/AnalyticsEvents'; +import { sendAnalytics } from '../../analytics/functions'; +import { IReduxState } from '../../app/types'; +import { IDeeplinkingConfig } from '../../base/config/configType'; +import { getLegalUrls } from '../../base/config/functions.any'; +import { isSupportedBrowser } from '../../base/environment/environment'; +import { translate, translateToHTML } from '../../base/i18n/functions'; +import { withPixelLineHeight } from '../../base/styles/functions.web'; +import Button from '../../base/ui/components/web/Button'; +import { BUTTON_TYPES } from '../../base/ui/constants.any'; +import { + openDesktopApp, + openWebApp +} from '../actions'; +import { _TNS } from '../constants'; + +const useStyles = makeStyles()((theme: Theme) => { + return { + container: { + background: '#1E1E1E', + alignItems: 'center', + justifyContent: 'center', + width: '100%', + height: '100%', + display: 'flex' + }, + contentPane: { + display: 'flex', + flexDirection: 'column', + background: theme.palette.ui01, + border: `1px solid ${theme.palette.ui03}`, + padding: 40, + borderRadius: 16, + maxWidth: 410, + color: theme.palette.text01 + }, + logo: { + marginBottom: 32 + }, + launchingMeetingLabel: { + marginBottom: 16, + ...withPixelLineHeight(theme.typography.heading4) + }, + roomName: { + marginBottom: 32, + ...withPixelLineHeight(theme.typography.heading5) + }, + descriptionLabel: { + marginBottom: 32, + ...withPixelLineHeight(theme.typography.bodyLongRegular) + }, + buttonsContainer: { + display: 'flex', + justifyContent: 'flex-start', + '& > *:not(:last-child)': { + marginRight: 16 + } + }, + separator: { + marginTop: 40, + height: 1, + maxWidth: 390, + background: theme.palette.ui03 + }, + label: { + marginTop: 40, + ...withPixelLineHeight(theme.typography.labelRegular), + color: theme.palette.text02, + '& a': { + color: theme.palette.link01 + } + } + }; +}); + +const DeepLinkingDesktopPage: React.FC = ({ t }) => { + const dispatch = useDispatch(); + const room = useSelector((state: IReduxState) => decodeURIComponent(state['features/base/conference'].room || '')); + const deeplinkingCfg = useSelector((state: IReduxState) => + state['features/base/config']?.deeplinking || {} as IDeeplinkingConfig); + + const legalUrls = useSelector(getLegalUrls); + + const { hideLogo, desktop } = deeplinkingCfg; + + const { classes: styles } = useStyles(); + const onLaunchWeb = useCallback(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'clicked', 'launchWebButton', { isMobileBrowser: false })); + dispatch(openWebApp()); + }, []); + const onTryAgain = useCallback(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'clicked', 'tryAgainButton', { isMobileBrowser: false })); + dispatch(openDesktopApp()); + }, []); + + useEffect(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'displayed', 'DeepLinkingDesktop', { isMobileBrowser: false })); + }, []); + + return ( +
+
+
+ { + !hideLogo + && { + } +
+
+ { + t(`${_TNS}.titleNew`) + } +
+
{ room }
+
+ { + isSupportedBrowser() + ? translateToHTML(t, `${_TNS}.descriptionNew`, { app: desktop?.appName }) + : t(`${_TNS}.descriptionWithoutWeb`, { app: desktop?.appName }) + } +
+
+
+
+
{translateToHTML(t, 'deepLinking.termsAndConditions', { + termsAndConditionsLink: legalUrls.terms + })} +
+
+
+ ); +}; + +export default translate(DeepLinkingDesktopPage); diff --git a/react/features/deep-linking/components/DeepLinkingMobilePage.web.js b/react/features/deep-linking/components/DeepLinkingMobilePage.web.js deleted file mode 100644 index e4a810238c..0000000000 --- a/react/features/deep-linking/components/DeepLinkingMobilePage.web.js +++ /dev/null @@ -1,299 +0,0 @@ -// @flow - -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'; -import { connect } from '../../base/redux'; -import { DialInSummary } from '../../invite'; -import { openWebApp } from '../actions'; -import { _TNS } from '../constants'; -import { generateDeepLinkingURL } from '../functions'; -import { renderPromotionalFooter } from '../renderPromotionalFooter'; - -/** - * The namespace of the CSS styles of DeepLinkingMobilePage. - * - * @private - * @type {string} - */ -const _SNS = 'deep-linking-mobile'; - -/** - * The type of the React {@code Component} props of - * {@link DeepLinkingMobilePage}. - */ -type Props = { - - /** - * The deeplinking config. - */ - _deeplinkingCfg: IDeeplinkingConfig, - - /** - * Application mobile deeplinking config. - */ - _mobileConfig: IDeeplinkingMobileConfig, - - /** - * The deeplinking url. - */ - _deepLinkingUrl: string, - - /** - * The name of the conference attempting to being joined. - */ - _room: string, - - /** - * The page current url. - */ - _url: URL, - - /** - * Used to dispatch actions from the buttons. - */ - dispatch: Dispatch, - - /** - * The function to translate human-readable text. - */ - t: Function -}; - -/** - * React component representing mobile browser page. - * - * @class DeepLinkingMobilePage - */ -class DeepLinkingMobilePage extends Component { - /** - * Initializes a new {@code DeepLinkingMobilePage} instance. - * - * @param {Object} props - The read-only React {@code Component} props with - * which the new instance is to be initialized. - */ - constructor(props: Props) { - super(props); - - // Bind event handlers so they are only bound once per instance. - this._onDownloadApp = this._onDownloadApp.bind(this); - this._onLaunchWeb = this._onLaunchWeb.bind(this); - this._onOpenApp = this._onOpenApp.bind(this); - } - - /** - * Implements the Component's componentDidMount method. - * - * @inheritdoc - */ - componentDidMount() { - sendAnalytics( - createDeepLinkingPageEvent( - 'displayed', 'DeepLinkingMobile', { isMobileBrowser: true })); - } - - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const { - _deeplinkingCfg: { hideLogo }, - _mobileConfig: { downloadLink, appName }, - _room, - t, - _url, - _deepLinkingUrl - } = this.props; - const downloadButtonClassName - = `${_SNS}__button ${_SNS}__button_primary`; - - - const onOpenLinkProperties = downloadLink - ? { - // When opening a link to the download page, we want to let the - // OS itself handle intercepting and opening the appropriate - // app store. This avoids potential issues with browsers, such - // as iOS Chrome, not opening the store properly. - } - : { - // When falling back to another URL (Firebase) let the page be - // opened in a new window. This helps prevent the user getting - // trapped in an app-open-cycle where going back to the mobile - // browser re-triggers the app-open behavior. - target: '_blank', - rel: 'noopener noreferrer' - }; - - return ( -
-
- { - hideLogo - ? null - : { - } -
-
-

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

-

- { t(`${_TNS}.ifHaveApp`) } -

- - - -

- { t(`${_TNS}.ifDoNotHaveApp`) } -

- - - - { - isSupportedMobileBrowser() - ? ( - - - - ) : ( - - { t(`${_TNS}.unsupportedBrowser`) } - - ) - } - { renderPromotionalFooter() } - -
-
- ); - } - - /** - * Generates the URL for downloading the app. - * - * @private - * @returns {string} - The URL for downloading the app. - */ - _generateDownloadURL() { - const { _mobileConfig: { downloadLink, dynamicLink, appScheme } } = this.props; - - if (downloadLink && typeof dynamicLink === 'undefined') { - return downloadLink; - } - - const { - apn, - appCode, - customDomain, - ibi, - isi - } = dynamicLink || {}; - - const domain = customDomain ?? `https://${appCode}.app.goo.gl`; - - return `${domain}/?link=${ - encodeURIComponent(window.location.href)}&apn=${ - apn}&ibi=${ - ibi}&isi=${ - isi}&ius=${ - appScheme}&efr=1`; - } - - _onDownloadApp: () => void; - - /** - * Handles download app button clicks. - * - * @private - * @returns {void} - */ - _onDownloadApp() { - sendAnalytics( - createDeepLinkingPageEvent( - 'clicked', 'downloadAppButton', { isMobileBrowser: true })); - } - - _onLaunchWeb: () => void; - - /** - * Handles launch web button clicks. - * - * @returns {void} - */ - _onLaunchWeb() { - sendAnalytics( - createDeepLinkingPageEvent( - 'clicked', 'launchWebButton', { isMobileBrowser: true })); - this.props.dispatch(openWebApp()); - } - - _onOpenApp: () => void; - - /** - * Handles open app button clicks. - * - * @private - * @returns {void} - */ - _onOpenApp() { - sendAnalytics( - createDeepLinkingPageEvent( - 'clicked', 'openAppButton', { isMobileBrowser: true })); - } -} - -/** - * Maps (parts of) the Redux state to the associated props for the - * {@code DeepLinkingMobilePage} component. - * - * @param {Object} state - The Redux state. - * @private - * @returns {Props} - */ -function _mapStateToProps(state) { - const { locationURL = {} } = state['features/base/connection']; - const { deeplinking } = state['features/base/config']; - const mobileConfig = deeplinking?.[Platform.OS] || {}; - - return { - _deeplinkingCfg: deeplinking || {}, - _mobileConfig: mobileConfig, - _room: decodeURIComponent(state['features/base/conference'].room), - _url: locationURL, - _deepLinkingUrl: generateDeepLinkingURL(state) - }; -} - -export default translate(connect(_mapStateToProps)(DeepLinkingMobilePage)); diff --git a/react/features/deep-linking/components/DeepLinkingMobilePage.web.tsx b/react/features/deep-linking/components/DeepLinkingMobilePage.web.tsx new file mode 100644 index 0000000000..5c00bb2b42 --- /dev/null +++ b/react/features/deep-linking/components/DeepLinkingMobilePage.web.tsx @@ -0,0 +1,241 @@ +/* eslint-disable lines-around-comment */ +import { Theme } from '@mui/material'; +import React, { useCallback, useEffect, useMemo } from 'react'; +import { WithTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { makeStyles } from 'tss-react/mui'; + +import { createDeepLinkingPageEvent } from '../../analytics/AnalyticsEvents'; +import { sendAnalytics } from '../../analytics/functions'; +import { IReduxState } from '../../app/types'; +import { IDeeplinkingConfig, IDeeplinkingMobileConfig } from '../../base/config/configType'; +import { isSupportedMobileBrowser } from '../../base/environment/environment'; +import { translate } from '../../base/i18n/functions'; +import Platform from '../../base/react/Platform.web'; +import { withPixelLineHeight } from '../../base/styles/functions.web'; +import Button from '../../base/ui/components/web/Button'; +// @ts-ignore +import DialInSummary from '../../invite/components/dial-in-summary/web/DialInSummary'; +import { openWebApp } from '../actions'; +// @ts-ignore +import { _TNS } from '../constants'; +// @ts-ignore +import { generateDeepLinkingURL } from '../functions'; + + +const PADDINGS = { + topBottom: 24, + leftRight: 40 +}; + +const useStyles = makeStyles()((theme: Theme) => { + return { + container: { + background: '#1E1E1E', + width: '100vw', + height: '100vh', + overflowX: 'hidden', + overflowY: 'auto', + justifyContent: 'center', + display: 'flex', + '& a': { + textDecoration: 'none' + } + }, + contentPane: { + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + padding: `${PADDINGS.topBottom}px ${PADDINGS.leftRight}px`, + maxWidth: 410, + color: theme.palette.text01 + }, + launchingMeetingLabel: { + marginTop: 24, + textAlign: 'center', + marginBottom: 32, + ...withPixelLineHeight(theme.typography.heading5) + }, + roomNameLabel: { + ...withPixelLineHeight(theme.typography.bodyLongRegularLarge) + }, + joinMeetWrapper: { + marginTop: 24, + width: '100%' + }, + labelDescription: { + textAlign: 'center', + marginTop: 16, + ...withPixelLineHeight(theme.typography.bodyShortRegularLarge) + }, + linkWrapper: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + marginTop: 8, + width: '100%' + }, + linkLabel: { + color: theme.palette.link01, + ...withPixelLineHeight(theme.typography.bodyLongBoldLarge) + }, + supportedBrowserContent: { + marginTop: 16, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center' + }, + labelOr: { + ...withPixelLineHeight(theme.typography.bodyShortRegularLarge) + }, + separator: { + marginTop: '32px', + height: 1, + width: `calc(100% + ${2 * PADDINGS.leftRight}px)`, + background: theme.palette.ui03 + } + }; +}); + +const DeepLinkingMobilePage: React.FC = ({ t }) => { + const deeplinkingCfg = useSelector((state: IReduxState) => + state['features/base/config']?.deeplinking || {} as IDeeplinkingConfig); + const { hideLogo } = deeplinkingCfg; + const deepLinkingUrl: string = useSelector(generateDeepLinkingURL); + const room = useSelector((state: IReduxState) => decodeURIComponent(state['features/base/conference'].room || '')); + const url = useSelector((state: IReduxState) => state['features/base/connection'] || {}); + const dispatch = useDispatch(); + const { classes: styles } = useStyles(); + + const generateDownloadURL = useCallback(() => { + const { downloadLink, dynamicLink, appScheme } + = (deeplinkingCfg?.[Platform.OS as keyof typeof deeplinkingCfg] || {}) as IDeeplinkingMobileConfig; + + if (downloadLink && typeof dynamicLink === 'undefined') { + return downloadLink; + } + + const { + apn, + appCode, + customDomain, + ibi, + isi + } = dynamicLink || {}; + + const domain = customDomain ?? `https://${appCode}.app.goo.gl`; + + return `${domain}/?link=${ + encodeURIComponent(window.location.href)}&apn=${ + apn}&ibi=${ + ibi}&isi=${ + isi}&ius=${ + appScheme}&efr=1`; + }, [ deeplinkingCfg ]); + + const onDownloadApp = useCallback(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'clicked', 'downloadAppButton', { isMobileBrowser: true })); + }, []); + + const onLaunchWeb = useCallback(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'clicked', 'launchWebButton', { isMobileBrowser: true })); + dispatch(openWebApp()); + }, []); + + const onOpenApp = useCallback(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'clicked', 'openAppButton', { isMobileBrowser: true })); + }, []); + + const onOpenLinkProperties = useMemo(() => { + const { downloadLink } + = (deeplinkingCfg?.[Platform.OS as keyof typeof deeplinkingCfg] || {}) as IDeeplinkingMobileConfig; + + if (downloadLink) { + return { + // When opening a link to the download page, we want to let the + // OS itself handle intercepting and opening the appropriate + // app store. This avoids potential issues with browsers, such + // as iOS Chrome, not opening the store properly. + }; + } + + return { + // When falling back to another URL (Firebase) let the page be + // opened in a new window. This helps prevent the user getting + // trapped in an app-open-cycle where going back to the mobile + // browser re-triggers the app-open behavior. + target: '_blank', + rel: 'noopener noreferrer' + }; + }, [ deeplinkingCfg ]); + + useEffect(() => { + sendAnalytics( + createDeepLinkingPageEvent( + 'displayed', 'DeepLinkingMobile', { isMobileBrowser: true })); + }, []); + + + return ( +
+
+ {!hideLogo && ({ + )} + +
{ t(`${_TNS}.launchMeetingLabel`) }
+
{room}
+ +
+ ); +}; + +export default translate(DeepLinkingMobilePage); diff --git a/react/features/deep-linking/renderPromotionalFooter.js b/react/features/deep-linking/renderPromotionalFooter.js deleted file mode 100644 index 733de78f4d..0000000000 --- a/react/features/deep-linking/renderPromotionalFooter.js +++ /dev/null @@ -1,9 +0,0 @@ -// @flow -/** - * Method used in order to render a custom promotional footer. - * - * @returns {HTMLElement} - */ -export function renderPromotionalFooter() { - return null; -} diff --git a/react/features/invite/components/dial-in-summary/web/ConferenceID.js b/react/features/invite/components/dial-in-summary/web/ConferenceID.js deleted file mode 100644 index 328957919f..0000000000 --- a/react/features/invite/components/dial-in-summary/web/ConferenceID.js +++ /dev/null @@ -1,60 +0,0 @@ -// @flow - -import React, { Component } from 'react'; - -import { translate } from '../../../../base/i18n'; -import { _formatConferenceIDPin } from '../../../_utils'; - -/** - * The type of the React {@code Component} props of {@link ConferenceID}. - */ -type Props = { - - /** - * The conference ID for dialing in. - */ - conferenceID: number, - - /** - * The name of the conference. - */ - conferenceName: ?string, - - /** - * Invoked to obtain translated strings. - */ - t: Function -}; - -/** - * Displays a conference ID used as a pin for dialing into a conference. - * - * @augments Component - */ -class ConferenceID extends Component { - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const { conferenceID, conferenceName, t } = this.props; - - return ( -
-
- { conferenceName } -
-
- { t('info.dialANumber') } -
-
- { `${t('info.dialInConferenceID')} ${_formatConferenceIDPin(conferenceID)}` } -
-
- ); - } -} - -export default translate(ConferenceID); diff --git a/react/features/invite/components/dial-in-summary/web/ConferenceID.tsx b/react/features/invite/components/dial-in-summary/web/ConferenceID.tsx new file mode 100644 index 0000000000..136fddd705 --- /dev/null +++ b/react/features/invite/components/dial-in-summary/web/ConferenceID.tsx @@ -0,0 +1,77 @@ +/* eslint-disable lines-around-comment */ +import { Theme } from '@mui/material'; +import React from 'react'; +import { WithTranslation } from 'react-i18next'; +import { makeStyles } from 'tss-react/mui'; + +import { translate } from '../../../../base/i18n/functions'; +import { withPixelLineHeight } from '../../../../base/styles/functions.web'; +// @ts-ignore +import { _formatConferenceIDPin } from '../../../_utils'; + + +interface IProps extends WithTranslation { + + /** + * The conference id. + */ + conferenceID?: string | number; + + /** + * The conference name. + */ + conferenceName: string; +} + +const useStyles = makeStyles()((theme: Theme) => { + return { + container: { + marginTop: 32, + maxWidth: 310, + padding: '16px 12px', + background: theme.palette.ui02, + textAlign: 'center', + display: 'flex', + flexDirection: 'column', + borderRadius: 6 + }, + confNameLabel: { + ...withPixelLineHeight(theme.typography.heading6), + marginBottom: 18, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis' + }, + descriptionLabel: { + ...withPixelLineHeight(theme.typography.bodyShortRegularLarge), + marginBottom: 18 + }, + separator: { + width: '100%', + height: 1, + background: theme.palette.ui04, + marginBottom: 18 + }, + pinLabel: { + ...withPixelLineHeight(theme.typography.heading6) + } + }; +}); + +const ConferenceID: React.FC = ({ conferenceID, t }) => { + const { classes: styles } = useStyles(); + + return ( +
+
+ To join the meeting via phone, dial one of these numbers and then enter the pin +
+
+
+ { `${t('info.dialInConferenceID')} ${_formatConferenceIDPin(conferenceID ?? '')}` } +
+
+ ); +}; + +export default translate(ConferenceID); diff --git a/react/features/invite/components/dial-in-summary/web/DialInSummary.js b/react/features/invite/components/dial-in-summary/web/DialInSummary.js index 64865af466..5549ace25c 100644 --- a/react/features/invite/components/dial-in-summary/web/DialInSummary.js +++ b/react/features/invite/components/dial-in-summary/web/DialInSummary.js @@ -1,8 +1,13 @@ // @flow + +import { Theme } from '@mui/material'; +import { withStyles } from '@mui/styles'; +import clsx from 'clsx'; import React, { Component } from 'react'; import { translate } from '../../../../base/i18n'; +import { withPixelLineHeight } from '../../../../base/styles/functions.web'; import { getDialInConferenceID, getDialInNumbers } from '../../../_utils'; import ConferenceID from './ConferenceID'; @@ -20,6 +25,11 @@ type Props = { */ className: string, + /** + * An object containing the CSS classes. + */ + classes: any; + /** * Whether or not numbers should include links with the telephone protocol. */ @@ -30,6 +40,16 @@ type Props = { */ room: string, + /** + * Whether the dial in summary container is scrollable. + */ + scrollable: Boolean, + + /** + * Whether the room name should show as title. + */ + showTitle?: boolean, + /** * The url where we were loaded. */ @@ -72,6 +92,26 @@ type State = { numbersEnabled: ?boolean } +const styles = (theme: Theme) => { + return { + hasNumbers: { + alignItems: 'center', + display: 'flex', + flexDirection: 'column', + background: '#1E1E1E', + color: theme.palette.text01 + }, + scrollable: { + height: '100vh', + overflowY: 'scroll' + }, + roomName: { + margin: '40px auto 8px', + ...withPixelLineHeight(theme.typography.heading5) + } + }; +}; + /** * Displays a page listing numbers for dialing into a conference and pin to * the a specific conference. @@ -136,24 +176,27 @@ class DialInSummary extends Component { let contents; const { conferenceID, error, loading, numbersEnabled } = this.state; + const { classes, showTitle, room, clickableNumbers, scrollable, t } = this.props; if (loading) { contents = ''; } else if (numbersEnabled === false) { - contents = this.props.t('info.dialInNotSupported'); + contents = t('info.dialInNotSupported'); } else if (error) { contents = error; } else { - className = 'has-numbers'; + className = clsx(classes.hasNumbers, scrollable && classes.scrollable); contents = [ conferenceID - ? - : null, + ? <> + { showTitle &&
{ room }
} + + : null, @@ -161,7 +204,7 @@ class DialInSummary extends Component { } return ( -
+
{ contents }
); @@ -272,4 +315,4 @@ class DialInSummary extends Component { } } -export default translate(DialInSummary); +export default translate(withStyles(styles)(DialInSummary)); diff --git a/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js b/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js new file mode 100644 index 0000000000..2339059de0 --- /dev/null +++ b/react/features/invite/components/dial-in-summary/web/DialInSummaryApp.js @@ -0,0 +1,80 @@ +// @flow + +import { AtlasKitThemeProvider } from '@atlaskit/theme'; +import React from 'react'; + +import { BaseApp } from '../../../../base/app'; +import { isMobileBrowser } from '../../../../base/environment/utils'; +import GlobalStyles from '../../../../base/ui/components/GlobalStyles.web'; +import JitsiThemeProvider from '../../../../base/ui/components/JitsiThemeProvider.web'; +import { parseURLParams } from '../../../../base/util'; +import { DIAL_IN_INFO_PAGE_PATH_NAME } from '../../../constants'; +import NoRoomError from '../../dial-in-info-page/NoRoomError.web'; + +import DialInSummary from './DialInSummary'; + +/** + * Wrapper application for prejoin. + * + * @augments BaseApp + */ +export default class DialInSummaryApp extends BaseApp { + /** + * The deferred for the initialisation {{promise, resolve, reject}}. + */ + _init: Object; + + /** + * Navigates to {@link Prejoin} upon mount. + * + * @returns {void} + */ + async componentDidMount() { + await super.componentDidMount(); + + const { room } = parseURLParams(window.location, true, 'search'); + const { href } = window.location; + const ix = href.indexOf(DIAL_IN_INFO_PAGE_PATH_NAME); + const url = (ix > 0 ? href.substring(0, ix) : href) + room; + + super._navigate({ + component: () => (<> + {room + ? + : } + ) + }); + } + + /** + * Overrides the parent method to inject {@link AtlasKitThemeProvider} as + * the top most component. + * + * @override + */ + _createMainElement(component, props) { + return ( + + + + {super._createMainElement(component, props)} + + + ); + } + + /** + * Renders the platform specific dialog container. + * + * @returns {React$Element} + */ + _renderDialogContainer() { + return null; + } +} diff --git a/react/features/invite/components/dial-in-summary/web/NumbersList.js b/react/features/invite/components/dial-in-summary/web/NumbersList.js deleted file mode 100644 index c87e2fc914..0000000000 --- a/react/features/invite/components/dial-in-summary/web/NumbersList.js +++ /dev/null @@ -1,244 +0,0 @@ -// @flow - -import React, { Component } from 'react'; - -import { translate } from '../../../../base/i18n'; -import { Icon, IconPhoneRinging } from '../../../../base/icons'; - -type Props = { - - /** - * Whether or not numbers should include links with the telephone protocol. - */ - clickableNumbers: boolean, - - /** - * The conference ID for dialing in. - */ - conferenceID: number, - - /** - * The phone numbers to display. Can be an array of number Objects or an - * object with countries as keys and an array of numbers as values. - */ - numbers: { [string]: Array } | Array, - - /** - * Invoked to obtain translated strings. - */ - t: Function -} - -/** - * Displays a table with phone numbers to dial in to a conference. - * - * @augments Component - */ -class NumbersList extends Component { - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - const { numbers } = this.props; - - return this._renderWithCountries(numbers); - } - - /** - * Renders rows of countries and associated phone numbers. - * - * @param {Object|Array} numbersMapping - An object with country - * names as keys and values as arrays of phone numbers. - * @private - * @returns {ReactElement[]} - */ - _renderWithCountries( - numbersMapping: { numbers: Array } | Array) { - const { t } = this.props; - let hasFlags = false, numbers; - - if (Array.isArray(numbersMapping)) { - hasFlags = true; - numbers = numbersMapping.reduce( - (resultNumbers, number) => { - // The i18n-iso-countries package insists on upper case. - const countryCode = number.countryCode.toUpperCase(); - - let countryName; - - if (countryCode === 'SIP') { - countryName = t('info.sip'); - } else { - countryName = t(`countries:countries.${countryCode}`); - - // Some countries have multiple names as US ['United States of America', 'USA'] - // choose the first one if that is the case - if (!countryName) { - countryName = t(`countries:countries.${countryCode}.0`); - } - } - - if (resultNumbers[countryName]) { - resultNumbers[countryName].push(number); - } else { - resultNumbers[countryName] = [ number ]; - } - - return resultNumbers; - }, {}); - } else { - numbers = {}; - - for (const [ country, numbersArray ] - of Object.entries(numbersMapping.numbers)) { - - if (Array.isArray(numbersArray)) { - /* eslint-disable arrow-body-style */ - const formattedNumbers = numbersArray.map(number => ({ - formattedNumber: number - })); - /* eslint-enable arrow-body-style */ - - numbers[country] = formattedNumbers; - } - } - } - - const rows = []; - - Object.keys(numbers).forEach((countryName: string) => { - const numbersArray = numbers[countryName]; - - rows.push( - - { this._renderFlag(numbersArray[0].countryCode) } - { countryName } - - { this._renderNumbersList(numbersArray) } - - - { this._renderNumbersTollFreeList(numbersArray) } - - - ); - }); - - return ( - - - - { hasFlags ? - - - - - { rows } - -
: null} - { t('info.country') }{ t('info.numbers') } -
- ); - } - - /** - * Renders a div container for a flag for the country of the phone number. - * - * @param {string} countryCode - The country code flag to display. - * @private - * @returns {ReactElement} - */ - _renderFlag(countryCode) { - if (countryCode) { - return ( - - {countryCode === 'SIP' - ? - : - } - ); - } - - return null; - } - - /** - * Renders a div container for a phone number. - * - * @param {Array} numbers - The phone number to display. - * @private - * @returns {ReactElement[]} - */ - _renderNumbersList(numbers) { - const numbersListItems = numbers.map(number => - (
  • - { this._renderNumberLink(number.formattedNumber) } -
  • )); - - return ( -
      - { numbersListItems } -
    - ); - } - - /** - * Renders list with a toll free text on the position where there is a - * number marked as toll free. - * - * @param {Array} numbers - The phone number that are displayed. - * @private - * @returns {ReactElement[]} - */ - _renderNumbersTollFreeList(numbers) { - const { t } = this.props; - - const tollNumbersListItems = numbers.map(number => - (
  • - { number.tollFree ? t('info.dialInTollFree') : '' } -
  • )); - - return ( -
      - { tollNumbersListItems } -
    - ); - } - - /** - * Renders a ReactElement for displaying a telephone number. If the - * component prop {@code clickableNumbers} is true, then the number will - * have a link with the telephone protocol. - * - * @param {string} number - The phone number to display. - * @private - * @returns {ReactElement} - */ - _renderNumberLink(number) { - if (this.props.clickableNumbers) { - // Url encode # to %23, Android phone was cutting the # after - // clicking it. - // Seems that using ',' and '%23' works on iOS and Android. - return ( - - { number } - - ); - } - - return number; - } - -} - -export default translate(NumbersList); diff --git a/react/features/invite/components/dial-in-summary/web/NumbersList.tsx b/react/features/invite/components/dial-in-summary/web/NumbersList.tsx new file mode 100644 index 0000000000..9f86e26456 --- /dev/null +++ b/react/features/invite/components/dial-in-summary/web/NumbersList.tsx @@ -0,0 +1,209 @@ +/* eslint-disable lines-around-comment */ +import countries from 'i18n-iso-countries'; +import en from 'i18n-iso-countries/langs/en.json'; +import React, { useCallback, useMemo } from 'react'; +import { WithTranslation } from 'react-i18next'; + +import { translate } from '../../../../base/i18n/functions'; +// @ts-ignore +import { Icon, IconSip } from '../../../../base/icons'; + +countries.registerLocale(en); + +interface INormalizedNumber { + + /** + * The country code. + */ + countryCode?: string; + + /** + * The formatted number. + */ + formattedNumber: string; + + /** + * Whether the number is toll-free. + */ + tollFree?: boolean; +} + +interface INumbersMapping { + [countryName: string]: Array; +} + +interface IProps extends WithTranslation { + + /** + * Whether or not numbers should include links with the telephone protocol. + */ + clickableNumbers: boolean; + + /** + * The conference ID for dialing in. + */ + conferenceID: number; + + /** + * The phone numbers to display. Can be an array of number Objects or an + * object with countries as keys and an array of numbers as values. + */ + numbers: INumbersMapping; + +} + +const NumbersList: React.FC = ({ t, conferenceID, clickableNumbers, numbers: numbersMapping }) => { + const renderFlag = useCallback((countryCode: string) => { + if (countryCode) { + return ( + + {countryCode === 'SIP' + ? + : + } + ); + } + + return null; + }, []); + + const renderNumberLink = useCallback((number: string) => { + if (clickableNumbers) { + // Url encode # to %23, Android phone was cutting the # after + // clicking it. + // Seems that using ',' and '%23' works on iOS and Android. + return ( + + {number} + + ); + } + + return number; + }, [ conferenceID, clickableNumbers ]); + + const renderNumbersList = useCallback((numbers: Array) => { + const numbersListItems = numbers.map(number => + (
  • + {renderNumberLink(number.formattedNumber)} +
  • )); + + return ( +
      + {numbersListItems} +
    + ); + }, []); + + const renderNumbersTollFreeList = useCallback((numbers: Array) => { + const tollNumbersListItems = numbers.map(number => + (
  • + {number.tollFree ? t('info.dialInTollFree') : ''} +
  • )); + + return ( +
      + {tollNumbersListItems} +
    + ); + }, []); + + const renderNumbers = useMemo(() => { + let numbers: INumbersMapping; + + if (!numbersMapping) { + return; + } + + if (Array.isArray(numbersMapping)) { + numbers = numbersMapping.reduce( + (resultNumbers: any, number: any) => { + // The i18n-iso-countries package insists on upper case. + const countryCode = number.countryCode.toUpperCase(); + let countryName; + + if (countryCode === 'SIP') { + countryName = t('info.sip'); + } else { + countryName = t(`countries:countries.${countryCode}`); + + // Some countries have multiple names as US ['United States of America', 'USA'] + // choose the first one if that is the case + if (!countryName) { + countryName = t(`countries:countries.${countryCode}.0`); + } + } + + if (resultNumbers[countryName]) { + resultNumbers[countryName].push(number); + } else { + resultNumbers[countryName] = [ number ]; + } + + return resultNumbers; + }, {}); + } else { + numbers = {}; + + for (const [ country, numbersArray ] + of Object.entries(numbersMapping.numbers)) { + + if (Array.isArray(numbersArray)) { + /* eslint-disable arrow-body-style */ + const formattedNumbers = numbersArray.map(number => ({ + formattedNumber: number + })); + /* eslint-enable arrow-body-style */ + + numbers[country] = formattedNumbers; + } + } + } + + const rows: [JSX.Element] = [] as unknown as [JSX.Element]; + + Object.keys(numbers).forEach((countryName: string) => { + const numbersArray: Array = numbers[countryName]; + const countryCode = numbersArray[0].countryCode + || countries.getAlpha2Code(countryName, 'en')?.toUpperCase() + || countryName; + + rows.push( + <> + + {renderFlag(countryCode)} + {countryName} + + + + + {renderNumbersList(numbersArray)} + + + {renderNumbersTollFreeList(numbersArray)} + + + + ); + }); + + return rows; + }, [ numbersMapping ]); + + return ( + + + {renderNumbers} + +
    + ); +}; + +export default translate(NumbersList); diff --git a/react/features/settings/components/native/SettingsView.tsx b/react/features/settings/components/native/SettingsView.tsx index b11c3a3998..7e6a01e9c7 100644 --- a/react/features/settings/components/native/SettingsView.tsx +++ b/react/features/settings/components/native/SettingsView.tsx @@ -19,6 +19,7 @@ import { getDefaultURL } from '../../../app/functions.native'; import { IReduxState } from '../../../app/types'; // @ts-ignore import { Avatar } from '../../../base/avatar'; +import { getLegalUrls } from '../../../base/config/functions.native'; import { translate } from '../../../base/i18n/functions'; // @ts-ignore import JitsiScreen from '../../../base/modal/components/JitsiScreen'; @@ -46,19 +47,6 @@ import styles from './styles'; */ const { AppInfo } = NativeModules; -/** - * The URL at which the terms (of service/use) are available to the user. - */ -const TERMS_URL = 'https://jitsi.org/meet/terms'; - -/** - * The URL at which the privacy policy is available to the user. - */ -const PRIVACY_URL = 'https://jitsi.org/meet/privacy'; - - -const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html'; - interface IState { /** @@ -119,11 +107,13 @@ interface IState { interface IProps extends WithTranslation { /** - * The URL for when the help link. - * - * @protected + * The legal URL's. */ - _helpCentreUrl: string; + _legalUrls: { + helpCentre: string; + privacy: string; + terms: string; + }; /** * The ID of the local participant. @@ -710,7 +700,7 @@ class SettingsView extends Component { * @returns {void} */ _onShowHelpPressed() { - Linking.openURL(this.props._helpCentreUrl); + Linking.openURL(this.props._legalUrls.helpCentre); } /** @@ -719,7 +709,7 @@ class SettingsView extends Component { * @returns {void} */ _onShowPrivacyPressed() { - Linking.openURL(PRIVACY_URL); + Linking.openURL(this.props._legalUrls.privacy); } /** @@ -728,7 +718,7 @@ class SettingsView extends Component { * @returns {void} */ _onShowTermsPressed() { - Linking.openURL(TERMS_URL); + Linking.openURL(this.props._legalUrls.terms); } /** @@ -795,7 +785,7 @@ function _mapStateToProps(state: IReduxState) { const localParticipant = getLocalParticipant(state); return { - _helpCentreUrl: state['features/base/config'].helpCentreURL || DEFAULT_HELP_CENTRE_URL, + _legalUrls: getLegalUrls(state), _localParticipantId: localParticipant?.id, _serverURL: getDefaultURL(state), _serverURLChangeEnabled: isServerURLChangeEnabled(state), diff --git a/react/index.web.js b/react/index.web.js index 0efd861754..c3ba869520 100644 --- a/react/index.web.js +++ b/react/index.web.js @@ -7,6 +7,7 @@ import { App } from './features/app/components'; import { getLogger } from './features/base/logging/functions'; import { Platform } from './features/base/react'; import { getJitsiMeetGlobalNS } from './features/base/util'; +import DialInSummaryApp from './features/invite/components/dial-in-summary/web/DialInSummaryApp'; import PrejoinApp from './features/prejoin/components/web/PrejoinApp'; const logger = getLogger('index.web'); @@ -43,7 +44,8 @@ const globalNS = getJitsiMeetGlobalNS(); globalNS.entryPoints = { APP: App, - PREJOIN: PrejoinApp + PREJOIN: PrejoinApp, + DIALIN: DialInSummaryApp }; globalNS.renderEntryPoint = ({ diff --git a/static/dialInInfo.html b/static/dialInInfo.html index 4fa19553b5..a0c08ff2f0 100644 --- a/static/dialInInfo.html +++ b/static/dialInInfo.html @@ -1,17 +1,29 @@ - + - - - - -
    + + - + + + + +
    diff --git a/webpack.config.js b/webpack.config.js index e5ee9aeddc..2938fd4d79 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -322,20 +322,6 @@ module.exports = (_env, argv) => { ], performance: getPerformanceHints(perfHintOptions, 800 * 1024) }), - Object.assign({}, config, { - entry: { - 'dial_in_info_bundle': './react/features/invite/components/dial-in-info-page' - }, - plugins: [ - ...config.plugins, - ...getBundleAnalyzerPlugin(analyzeBundle, 'dial_in_info'), - new webpack.IgnorePlugin({ - resourceRegExp: /^\.\/locale$/, - contextRegExp: /moment$/ - }) - ], - performance: getPerformanceHints(perfHintOptions, 500 * 1024) - }), Object.assign({}, config, { entry: { 'do_external_connect': './connection_optimization/do_external_connect.js'