init
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
2025-09-02 14:49:16 +08:00
commit 38ba663466
2885 changed files with 391107 additions and 0 deletions

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1,177 @@
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 Platform from '../../base/react/Platform.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,
...theme.typography.heading4
},
roomName: {
marginBottom: 32,
...theme.typography.heading5
},
descriptionLabel: {
marginBottom: 32,
...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,
...theme.typography.labelRegular,
color: theme.palette.text02,
'& a': {
color: theme.palette.link01
}
}
};
});
const DeepLinkingDesktopPage: React.FC<WithTranslation> = ({ 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 generateDownloadURL = useCallback(() => {
const downloadCfg = deeplinkingCfg.desktop?.download;
if (downloadCfg) {
return downloadCfg[Platform.OS as keyof typeof downloadCfg];
}
}, [ deeplinkingCfg ]);
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 (
<div className = { styles.container }>
<div className = { styles.contentPane }>
<div className = 'header'>
{
!hideLogo
&& <img
alt = { t('welcomepage.logo.logoDeepLinking') }
className = { styles.logo }
src = 'images/logo-deep-linking.png' />
}
</div>
<div className = { styles.launchingMeetingLabel }>
{
t(`${_TNS}.titleNew`)
}
</div>
<div className = { styles.roomName }>{ room }</div>
<div className = { styles.descriptionLabel }>
{
isSupportedBrowser()
? translateToHTML(t, `${_TNS}.descriptionNew`, { app: desktop?.appName })
: t(`${_TNS}.descriptionWithoutWeb`, { app: desktop?.appName })
}
</div>
<div className = { styles.descriptionLabel }>
{
t(`${_TNS}.noDesktopApp`)
} &nbsp;
<a href = { generateDownloadURL() }>
{
t(`${_TNS}.downloadApp`)
}
</a>
</div>
<div className = { styles.buttonsContainer }>
<Button
label = { t(`${_TNS}.tryAgainButton`) }
onClick = { onTryAgain } />
{ isSupportedBrowser() && (
<Button
label = { t(`${_TNS}.launchWebButton`) }
onClick = { onLaunchWeb }
type = { BUTTON_TYPES.SECONDARY } />
)}
</div>
<div className = { styles.separator } />
<div className = { styles.label }> {translateToHTML(t, 'deepLinking.termsAndConditions', {
termsAndConditionsLink: legalUrls.terms
})}
</div>
</div>
</div>
);
};
export default translate(DeepLinkingDesktopPage);

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1,219 @@
/* 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 Button from '../../base/ui/components/web/Button';
import DialInSummary from '../../invite/components/dial-in-summary/web/DialInSummary';
import { openWebApp } from '../actions';
import { _TNS } from '../constants';
import { generateDeepLinkingURL } from '../functions';
const PADDINGS = {
topBottom: 24,
leftRight: 40
};
const useStyles = makeStyles()((theme: Theme) => {
return {
container: {
background: '#1E1E1E',
width: '100vw',
height: '100dvh',
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,
...theme.typography.heading5
},
roomNameLabel: {
...theme.typography.bodyLongRegularLarge
},
joinMeetWrapper: {
marginTop: 24,
width: '100%'
},
labelDescription: {
textAlign: 'center',
marginTop: 16,
...theme.typography.bodyShortRegularLarge
},
linkWrapper: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginTop: 8,
width: '100%'
},
linkLabel: {
color: theme.palette.link01,
...theme.typography.bodyLongBoldLarge
},
supportedBrowserContent: {
marginTop: 16,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center'
},
labelOr: {
...theme.typography.bodyShortRegularLarge
},
separator: {
marginTop: '32px',
height: 1,
width: `calc(100% + ${2 * PADDINGS.leftRight}px)`,
background: theme.palette.ui03
}
};
});
const DeepLinkingMobilePage: React.FC<WithTranslation> = ({ 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 }
= (deeplinkingCfg?.[Platform.OS as keyof typeof deeplinkingCfg] || {}) as IDeeplinkingMobileConfig;
return downloadLink;
}, [ 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 (
<div className = { styles.container }>
<div className = { styles.contentPane }>
{!hideLogo && (<img
alt = { t('welcomepage.logo.logoDeepLinking') }
src = 'images/logo-deep-linking-mobile.png' />
)}
<div className = { styles.launchingMeetingLabel }>{ t(`${_TNS}.launchMeetingLabel`) }</div>
<div className = ''>{room}</div>
<a
{ ...onOpenLinkProperties }
className = { styles.joinMeetWrapper }
href = { deepLinkingUrl }
onClick = { onOpenApp }
target = '_top'>
<Button
fullWidth = { true }
label = { t(`${_TNS}.joinInAppNew`) } />
</a>
<div className = { styles.labelDescription }>{ t(`${_TNS}.noMobileApp`) }</div>
<a
{ ...onOpenLinkProperties }
className = { styles.linkWrapper }
href = { generateDownloadURL() }
onClick = { onDownloadApp }
target = '_top'>
<div className = { styles.linkLabel }>{ t(`${_TNS}.downloadMobileApp`) }</div>
</a>
{isSupportedMobileBrowser() ? (
<div className = { styles.supportedBrowserContent }>
<div className = { styles.labelOr }>{ t(`${_TNS}.or`) }</div>
<a
className = { styles.linkWrapper }
onClick = { onLaunchWeb }
target = '_top'>
<div className = { styles.linkLabel }>{ t(`${_TNS}.joinInBrowser`) }</div>
</a>
</div>
) : (
<div className = { styles.labelDescription }>
{t(`${_TNS}.unsupportedBrowser`)}
</div>
)}
<div className = { styles.separator } />
<DialInSummary
className = 'deep-linking-dial-in'
clickableNumbers = { true }
hideError = { true }
room = { room }
url = { url } />
</div>
</div>
);
};
export default translate(DeepLinkingMobilePage);

View File

@@ -0,0 +1 @@
export default {};

View File

@@ -0,0 +1,77 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createDeepLinkingPageEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState } from '../../app/types';
import { IDeeplinkingConfig } from '../../base/config/configType';
/**
* The type of the React {@code Component} props of
* {@link NoMobileApp}.
*/
interface IProps {
/**
* The deeplinking config.
*/
_deeplinkingCfg: IDeeplinkingConfig;
}
/**
* React component representing no mobile app page.
*
* @class NoMobileApp
*/
class NoMobileApp extends Component<IProps> {
/**
* Implements the Component's componentDidMount method.
*
* @inheritdoc
*/
override componentDidMount() {
sendAnalytics(
createDeepLinkingPageEvent(
'displayed', 'noMobileApp', { isMobileBrowser: true }));
}
/**
* Renders the component.
*
* @returns {ReactElement}
*/
override render() {
const ns = 'no-mobile-app';
const { desktop } = this.props._deeplinkingCfg;
const { appName } = desktop ?? {};
return (
<div className = { ns }>
<h2 className = { `${ns}__title` }>
Video chat isn't available on mobile.
</h2>
<p className = { `${ns}__description` }>
Please use { appName } on desktop to
join calls.
</p>
</div>
);
}
}
/**
* Maps (parts of) the Redux state to the associated props for the
* {@code NoMobileApp} component.
*
* @param {Object} state - The Redux state.
* @private
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState) {
return {
_deeplinkingCfg: state['features/base/config'].deeplinking || {}
};
}
export default connect(_mapStateToProps)(NoMobileApp);