feat(rn,welcome) React Navigation drawer
This commit is contained in:
parent
f5cdd5fca1
commit
4e2fea1e12
|
@ -1140,6 +1140,12 @@
|
|||
"button": "Invite others",
|
||||
"youAreAlone": "You are the only one in the meeting"
|
||||
},
|
||||
"termsView": {
|
||||
"header": "Terms"
|
||||
},
|
||||
"privacyView": {
|
||||
"header": "Privacy"
|
||||
},
|
||||
"helpView": {
|
||||
"header": "Help center"
|
||||
},
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"@react-native-community/netinfo": "4.1.5",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-native-masked-view/masked-view": "0.2.6",
|
||||
"@react-navigation/drawer": "5.12.9",
|
||||
"@react-navigation/material-top-tabs": "5.3.19",
|
||||
"@react-navigation/native": "5.9.8",
|
||||
"@react-navigation/stack": "5.14.9",
|
||||
|
@ -4255,6 +4256,24 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/drawer": {
|
||||
"version": "5.12.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.12.9.tgz",
|
||||
"integrity": "sha512-SYb2BCEAn+BiEwC6WBfCzs1VlWD+ZdQbxmsim6vo1o+ndPW2e+kiq7FXKRs0vUXhQRZVl2oOB3vBn0c3YCllQg==",
|
||||
"dependencies": {
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@react-navigation/native": "^5.0.5",
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-gesture-handler": ">= 1.0.0",
|
||||
"react-native-reanimated": ">= 1.0.0",
|
||||
"react-native-safe-area-context": ">= 0.6.0",
|
||||
"react-native-screens": ">= 2.0.0-alpha.0 || >= 2.0.0-beta.0 || >= 2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-navigation/material-top-tabs": {
|
||||
"version": "5.3.19",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
|
||||
|
@ -23379,6 +23398,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@react-navigation/drawer": {
|
||||
"version": "5.12.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.12.9.tgz",
|
||||
"integrity": "sha512-SYb2BCEAn+BiEwC6WBfCzs1VlWD+ZdQbxmsim6vo1o+ndPW2e+kiq7FXKRs0vUXhQRZVl2oOB3vBn0c3YCllQg==",
|
||||
"requires": {
|
||||
"color": "^3.1.3",
|
||||
"react-native-iphone-x-helper": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"@react-navigation/material-top-tabs": {
|
||||
"version": "5.3.19",
|
||||
"resolved": "https://registry.npmjs.org/@react-navigation/material-top-tabs/-/material-top-tabs-5.3.19.tgz",
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
"@react-native-community/netinfo": "4.1.5",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-native-masked-view/masked-view": "0.2.6",
|
||||
"@react-navigation/drawer": "5.12.9",
|
||||
"@react-navigation/material-top-tabs": "5.3.19",
|
||||
"@react-navigation/native": "5.9.8",
|
||||
"@react-navigation/stack": "5.14.9",
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
import { isRoomValid } from '../base/conference';
|
||||
import { toState } from '../base/redux';
|
||||
import { ConferenceNavigationContainer } from '../conference';
|
||||
import { isWelcomePageAppEnabled } from '../welcome';
|
||||
import { BlankPage, WelcomePage } from '../welcome/components';
|
||||
import RootNavigationContainer from '../welcome/components/RootNavigationContainer';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
|
@ -26,27 +25,17 @@ export function _getRouteToRender(stateful) {
|
|||
* @returns {Promise}
|
||||
*/
|
||||
function _getMobileRoute(state) {
|
||||
const route = _getEmptyRoute();
|
||||
const route = {
|
||||
component: null,
|
||||
href: undefined
|
||||
};
|
||||
|
||||
if (isRoomValid(state['features/base/conference'].room)) {
|
||||
route.component = ConferenceNavigationContainer;
|
||||
} else if (isWelcomePageAppEnabled(state)) {
|
||||
route.component = WelcomePage;
|
||||
} else {
|
||||
route.component = BlankPage;
|
||||
route.component = RootNavigationContainer;
|
||||
}
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default {@code Route}.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _getEmptyRoute() {
|
||||
return {
|
||||
component: BlankPage,
|
||||
href: undefined
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ import { toState } from '../base/redux';
|
|||
import { Conference } from '../conference';
|
||||
import { getDeepLinkingPage } from '../deep-linking';
|
||||
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
|
||||
import { isWelcomePageUserEnabled } from '../welcome';
|
||||
import { BlankPage, WelcomePage } from '../welcome/components';
|
||||
|
||||
import { BlankPage, isWelcomePageUserEnabled, WelcomePage } from '../welcome';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="30px" height="30px">
|
||||
<path d="M 15 2 A 1 1 0 0 0 14.300781 2.2851562 L 3.3925781 11.207031 A 1 1 0 0 0 3.3554688 11.236328 L 3.3183594 11.267578 L 3.3183594 11.269531 A 1 1 0 0 0 3 12 A 1 1 0 0 0 4 13 L 5 13 L 5 24 C 5 25.105 5.895 26 7 26 L 23 26 C 24.105 26 25 25.105 25 24 L 25 13 L 26 13 A 1 1 0 0 0 27 12 A 1 1 0 0 0 26.681641 11.267578 L 26.666016 11.255859 A 1 1 0 0 0 26.597656 11.199219 L 25 9.8925781 L 25 6 C 25 5.448 24.552 5 24 5 L 23 5 C 22.448 5 22 5.448 22 6 L 22 7.4394531 L 15.677734 2.2675781 A 1 1 0 0 0 15 2 z M 18 15 L 22 15 L 22 23 L 18 23 L 18 15 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 677 B |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="30px" height="30px">
|
||||
<path d="M 15 2 A 1 1 0 0 0 14.300781 2.2851562 L 3.3925781 11.207031 A 1 1 0 0 0 3.3554688 11.236328 L 3.3183594 11.267578 L 3.3183594 11.269531 A 1 1 0 0 0 3 12 A 1 1 0 0 0 4 13 L 5 13 L 5 24 C 5 25.105 5.895 26 7 26 L 23 26 C 24.105 26 25 25.105 25 24 L 25 13 L 26 13 A 1 1 0 0 0 27 12 A 1 1 0 0 0 26.681641 11.267578 L 26.666016 11.255859 A 1 1 0 0 0 26.597656 11.199219 L 25 9.8925781 L 25 6 C 25 5.448 24.552 5 24 5 L 23 5 C 22.448 5 22 5.448 22 6 L 22 7.4394531 L 15.677734 2.2675781 A 1 1 0 0 0 15 2 z M 18 15 L 22 15 L 22 23 L 18 23 L 18 15 z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 692 B |
|
@ -60,6 +60,7 @@ export { default as IconFullScreen } from './full-screen.svg';
|
|||
export { default as IconGoogle } from './google.svg';
|
||||
export { default as IconHangup } from './hangup.svg';
|
||||
export { default as IconHelp } from './help.svg';
|
||||
export { default as IconHome } from './home.svg';
|
||||
export { default as IconHorizontalPoints } from './horizontal-points.svg';
|
||||
export { default as IconInfo } from './info.svg';
|
||||
export { default as IconInviteMore } from './user-plus.svg';
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { KeyboardAvoidingView, Platform, SafeAreaView } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../color-scheme';
|
||||
import { HeaderWithNavigation, SlidingView } from '../../react';
|
||||
import { connect } from '../../redux';
|
||||
import { StyleType } from '../../styles';
|
||||
import { setActiveModalId } from '../actions';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The color schemed style of the common header component.
|
||||
*/
|
||||
_headerStyles: StyleType,
|
||||
|
||||
/**
|
||||
* True if the modal should be shown, false otherwise.
|
||||
*/
|
||||
_show: boolean,
|
||||
|
||||
/**
|
||||
* The color schemed style of the modal.
|
||||
*/
|
||||
_styles: StyleType,
|
||||
|
||||
/**
|
||||
* The children component(s) of the Modal, to be rendered.
|
||||
*/
|
||||
children: React$Node,
|
||||
|
||||
/**
|
||||
* The Redux Dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Optional function that renders a footer component, if needed.
|
||||
*/
|
||||
footerComponent?: Function,
|
||||
|
||||
/**
|
||||
* Props to be passed over to the header.
|
||||
*
|
||||
* See {@code HeaderWithNavigation} for more details.
|
||||
*/
|
||||
headerProps: Object,
|
||||
|
||||
/**
|
||||
* True if the header with navigation should be hidden, false otherwise.
|
||||
*/
|
||||
hideHeaderWithNavigation?: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the modal that is being rendered. This is used to show/hide the modal.
|
||||
*/
|
||||
modalId: string,
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the modal closes.
|
||||
*/
|
||||
onClose?: Function,
|
||||
|
||||
/**
|
||||
* The position from where the modal should be opened. This is derived from the
|
||||
* props of the {@code SlidingView} with the same name.
|
||||
*/
|
||||
position?: string,
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the View containing the content of the modal.
|
||||
*/
|
||||
style?: StyleType
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a custom Jitsi Modal that doesn't use the built in native
|
||||
* Modal component of React Native.
|
||||
*/
|
||||
class JitsiModal extends PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
position: 'bottom',
|
||||
hideHeaderWithNavigation: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiates a new component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._onRequestClose = this._onRequestClose.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code PureComponent#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_headerStyles,
|
||||
_show,
|
||||
_styles,
|
||||
children,
|
||||
footerComponent,
|
||||
headerProps,
|
||||
position,
|
||||
hideHeaderWithNavigation,
|
||||
style
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView
|
||||
onHide = { this._onRequestClose }
|
||||
position = { position }
|
||||
show = { _show }>
|
||||
<KeyboardAvoidingView
|
||||
behavior =
|
||||
{
|
||||
Platform.OS === 'ios'
|
||||
? 'padding' : 'height'
|
||||
}
|
||||
enabled = { true }
|
||||
style = { [
|
||||
_headerStyles.page,
|
||||
_styles.page,
|
||||
style
|
||||
] }>
|
||||
<HeaderWithNavigation
|
||||
{ ...headerProps }
|
||||
hideHeaderWithNavigation = { hideHeaderWithNavigation }
|
||||
onPressBack = { this._onRequestClose } />
|
||||
<SafeAreaView style = { styles.safeArea }>
|
||||
{ children }
|
||||
</SafeAreaView>
|
||||
{ footerComponent && footerComponent() }
|
||||
</KeyboardAvoidingView>
|
||||
</SlidingView>
|
||||
);
|
||||
}
|
||||
|
||||
_onRequestClose: () => boolean;
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the SlidingView requests closing.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_onRequestClose() {
|
||||
const { _show, dispatch, onClose } = this.props;
|
||||
let shouldCloseModal = true;
|
||||
|
||||
if (_show) {
|
||||
if (typeof onClose === 'function') {
|
||||
shouldCloseModal = onClose();
|
||||
}
|
||||
shouldCloseModal && dispatch(setActiveModalId());
|
||||
|
||||
return shouldCloseModal;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redix state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Props} ownProps - The own props of the component.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): $Shape<Props> {
|
||||
return {
|
||||
_headerStyles: ColorSchemeRegistry.get(state, 'Header'),
|
||||
_show: state['features/base/modal'].activeModalId === ownProps.modalId,
|
||||
_styles: ColorSchemeRegistry.get(state, 'Modal')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(JitsiModal);
|
|
@ -30,7 +30,7 @@ type Props = {
|
|||
/**
|
||||
* Is the screen rendering a tab navigator?
|
||||
*/
|
||||
hasTabNavigator: boolean,
|
||||
hasTabNavigator?: boolean,
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView containing the content of the modal.
|
||||
|
@ -42,7 +42,7 @@ const JitsiScreen = ({
|
|||
contentContainerStyle,
|
||||
children,
|
||||
footerComponent,
|
||||
hasTabNavigator,
|
||||
hasTabNavigator = false,
|
||||
style
|
||||
}: Props) => (
|
||||
<View
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import WebView from 'react-native-webview';
|
||||
|
||||
import JitsiScreen from './JitsiScreen';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to display.
|
||||
*/
|
||||
source: string,
|
||||
|
||||
/**
|
||||
* The component's external style.
|
||||
*/
|
||||
style: Object
|
||||
}
|
||||
|
||||
const JitsiScreenWebView = ({ source, style }: Props) => (
|
||||
<JitsiScreen
|
||||
style = { style }>
|
||||
<WebView source = {{ uri: source }} />
|
||||
</JitsiScreen>
|
||||
);
|
||||
|
||||
export default JitsiScreenWebView;
|
|
@ -0,0 +1,79 @@
|
|||
// @flow
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import { StatusBar } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../color-scheme';
|
||||
import { connect } from '../../redux';
|
||||
import { isDarkColor } from '../../styles';
|
||||
|
||||
// Register style
|
||||
import '../../react/components/native/headerstyles';
|
||||
|
||||
/**
|
||||
* Constants for the (currently) supported statusbar colors.
|
||||
*/
|
||||
const STATUSBAR_DARK = 'dark-content';
|
||||
const STATUSBAR_LIGHT = 'light-content';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The color schemed style of the component.
|
||||
*/
|
||||
_styles: Object
|
||||
}
|
||||
|
||||
const JitsiStatusBar = ({ _styles }: Props) => {
|
||||
|
||||
const getStatusBarContentColor = useCallback(() => {
|
||||
const { statusBarContent } = _styles;
|
||||
|
||||
if (statusBarContent) {
|
||||
// We have the possibility to define the statusbar color in the
|
||||
// color scheme feature, but since mobile devices (at the moment)
|
||||
// only support two colors (light and dark) we need to normalize
|
||||
// the value.
|
||||
|
||||
if (isDarkColor(statusBarContent)) {
|
||||
return STATUSBAR_DARK;
|
||||
}
|
||||
|
||||
return STATUSBAR_LIGHT;
|
||||
}
|
||||
|
||||
// The statusbar color is not defined, so we need to base our choice
|
||||
// on the header colors
|
||||
const { statusBar, screenHeader } = _styles;
|
||||
|
||||
if (isDarkColor(statusBar || screenHeader.backgroundColor)) {
|
||||
return STATUSBAR_LIGHT;
|
||||
}
|
||||
|
||||
return STATUSBAR_DARK;
|
||||
}, [ _styles ]);
|
||||
|
||||
return (
|
||||
<StatusBar
|
||||
backgroundColor = { _styles.statusBar }
|
||||
barStyle = { getStatusBarContentColor() }
|
||||
translucent = { false } />
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of the component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {{
|
||||
* _styles: Object
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_styles: ColorSchemeRegistry.get(state, 'Header')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(JitsiStatusBar);
|
|
@ -1,34 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Keyboard } from 'react-native';
|
||||
|
||||
import { toState } from '../../redux';
|
||||
|
||||
export const useKeyboardHeight = () => {
|
||||
const [ keyboardHeight, setKeyboardHeight ] = useState(0);
|
||||
|
||||
const onKeyboardDidShow = e => {
|
||||
setKeyboardHeight(e.endCoordinates.height);
|
||||
};
|
||||
|
||||
const onKeyboardDidHide = () => {
|
||||
setKeyboardHeight(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const keyboardShow = Keyboard.addListener('keyboardDidShow', onKeyboardDidShow);
|
||||
const keyboardHide = Keyboard.addListener('keyboardDidHide', onKeyboardDidHide);
|
||||
|
||||
return () => {
|
||||
keyboardShow.remove();
|
||||
keyboardHide.remove();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return keyboardHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the client width.
|
||||
|
@ -39,7 +12,7 @@ export const useKeyboardHeight = () => {
|
|||
* @returns {number}.
|
||||
*/
|
||||
export function getClientWidth(stateful: Object) {
|
||||
const state = toState(stateful['features/base/responsive-ui']);
|
||||
const state = toState(stateful)['features/base/responsive-ui'];
|
||||
|
||||
return state.clientWidth;
|
||||
}
|
||||
|
@ -54,7 +27,7 @@ export function getClientWidth(stateful: Object) {
|
|||
* @returns {number}.
|
||||
*/
|
||||
export function getClientHeight(stateful: Object) {
|
||||
const state = toState(stateful['features/base/responsive-ui']);
|
||||
const state = toState(stateful)['features/base/responsive-ui'];
|
||||
|
||||
return state.clientHeight;
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export { default as JitsiModal } from './JitsiModal';
|
|
@ -1,6 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import { Component } from 'react';
|
||||
|
||||
export const JitsiModal = Component;
|
||||
|
|
@ -2,4 +2,3 @@
|
|||
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './components';
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent, type Node } from 'react';
|
||||
import { SafeAreaView, StatusBar, View } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../../color-scheme';
|
||||
import { connect } from '../../../redux';
|
||||
import { isDarkColor } from '../../../styles';
|
||||
|
||||
// Register style
|
||||
import './headerstyles';
|
||||
|
||||
/**
|
||||
* Constanst for the (currently) supported statusbar colors.
|
||||
*/
|
||||
const STATUSBAR_DARK = 'dark-content';
|
||||
const STATUSBAR_LIGHT = 'light-content';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Header}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Children component(s).
|
||||
*/
|
||||
children: Node,
|
||||
|
||||
/**
|
||||
* The component's external style.
|
||||
*/
|
||||
style: Object,
|
||||
|
||||
/**
|
||||
* The color schemed style of the component.
|
||||
*/
|
||||
_styles: Object
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic screen header component.
|
||||
*/
|
||||
class Header extends PureComponent<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _styles } = this.props;
|
||||
|
||||
return (
|
||||
<View style = { _styles.headerOverlay }>
|
||||
<StatusBar
|
||||
backgroundColor = { _styles.statusBar }
|
||||
barStyle = { this._getStatusBarContentColor() }
|
||||
translucent = { false } />
|
||||
<SafeAreaView>
|
||||
<View
|
||||
style = { [
|
||||
_styles.screenHeader,
|
||||
this.props.style
|
||||
] }>
|
||||
{
|
||||
this.props.children
|
||||
}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the color of the statusbar content (light or dark) based on
|
||||
* certain criteria.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
_getStatusBarContentColor() {
|
||||
const { _styles } = this.props;
|
||||
const { statusBarContent } = _styles;
|
||||
|
||||
if (statusBarContent) {
|
||||
// We have the possibility to define the statusbar color in the
|
||||
// color scheme feature, but since mobile devices (at the moment)
|
||||
// only support two colors (light and dark) we need to normalize
|
||||
// the value.
|
||||
|
||||
if (isDarkColor(statusBarContent)) {
|
||||
return STATUSBAR_DARK;
|
||||
}
|
||||
|
||||
return STATUSBAR_LIGHT;
|
||||
}
|
||||
|
||||
// The statusbar color is not defined, so we need to base our choice
|
||||
// on the header colors
|
||||
const { statusBar, screenHeader } = _styles;
|
||||
|
||||
if (isDarkColor(statusBar || screenHeader.backgroundColor)) {
|
||||
return STATUSBAR_LIGHT;
|
||||
}
|
||||
|
||||
return STATUSBAR_DARK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of the component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {{
|
||||
* _styles: Object
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_styles: ColorSchemeRegistry.get(state, 'Header')
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Header);
|
|
@ -1,72 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { translate } from '../../../i18n';
|
||||
|
||||
import BackButton from './BackButton';
|
||||
import ForwardButton from './ForwardButton';
|
||||
import Header from './Header';
|
||||
import HeaderLabel from './HeaderLabel';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Boolean to set the forward button disabled.
|
||||
*/
|
||||
forwardDisabled: boolean,
|
||||
|
||||
/**
|
||||
* The i18n key of the the forward button label.
|
||||
*/
|
||||
forwardLabelKey: ?string,
|
||||
|
||||
/**
|
||||
* The i18n key of the header label (title).
|
||||
*/
|
||||
headerLabelKey: ?string,
|
||||
|
||||
/**
|
||||
* True if the header with navigation should be hidden, false otherwise.
|
||||
*/
|
||||
hideHeaderWithNavigation?: boolean,
|
||||
|
||||
/**
|
||||
* Callback to be invoked on pressing the back button.
|
||||
*/
|
||||
onPressBack: ?Function,
|
||||
|
||||
/**
|
||||
* Callback to be invoked on pressing the forward button.
|
||||
*/
|
||||
onPressForward: ?Function,
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a header with the standard navigation content.
|
||||
*/
|
||||
class HeaderWithNavigation extends Component<Props> {
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { hideHeaderWithNavigation, onPressBack, onPressForward } = this.props;
|
||||
|
||||
return (
|
||||
!hideHeaderWithNavigation
|
||||
&& <Header>
|
||||
{ onPressBack && <BackButton onPress = { onPressBack } /> }
|
||||
<HeaderLabel labelKey = { this.props.headerLabelKey } />
|
||||
{ onPressForward && <ForwardButton
|
||||
disabled = { this.props.forwardDisabled }
|
||||
labelKey = { this.props.forwardLabelKey }
|
||||
onPress = { onPressForward } /> }
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(HeaderWithNavigation);
|
|
@ -6,9 +6,7 @@ export { default as BaseIndicator } from './BaseIndicator';
|
|||
export { default as Button } from './Button';
|
||||
export { default as Container } from './Container';
|
||||
export { default as ForwardButton } from './ForwardButton';
|
||||
export { default as Header } from './Header';
|
||||
export { default as HeaderLabel } from './HeaderLabel';
|
||||
export { default as HeaderWithNavigation } from './HeaderWithNavigation';
|
||||
export { default as Image } from './Image';
|
||||
export { default as Link } from './Link';
|
||||
export { default as Linkify } from './Linkify';
|
||||
|
|
|
@ -18,6 +18,7 @@ export const colors = {
|
|||
primary08: '#99BBF3',
|
||||
primary09: '#CCDDF9',
|
||||
primary10: '#17A0DB',
|
||||
primary11: '#1081B2',
|
||||
|
||||
surface00: '#111111',
|
||||
surface01: '#040404',
|
||||
|
@ -32,12 +33,14 @@ export const colors = {
|
|||
surface10: '#E0E0E0',
|
||||
surface11: '#FFF',
|
||||
surface12: '#AAAAAA',
|
||||
surface13: '#495258',
|
||||
|
||||
success04: '#189B55',
|
||||
success05: '#1EC26A',
|
||||
|
||||
warning05: '#F8AE1A',
|
||||
warning06: '#ED9E1B'
|
||||
warning06: '#ED9E1B',
|
||||
warning07: '#D77976'
|
||||
};
|
||||
|
||||
// Mapping between the token used and the color
|
||||
|
@ -51,13 +54,18 @@ export const colorMap = {
|
|||
ui03: 'surface04',
|
||||
ui04: 'surface05',
|
||||
ui05: 'surface06',
|
||||
ui12: 'surface11',
|
||||
|
||||
// Primary buttons
|
||||
action01: 'primary05',
|
||||
action04: 'primary11',
|
||||
|
||||
// Screen header
|
||||
screen01Header: 'primary10',
|
||||
|
||||
// Status bar
|
||||
status01Bar: 'primary11',
|
||||
|
||||
// Hover state for primary buttons
|
||||
action01Hover: 'primary06',
|
||||
|
||||
|
@ -115,6 +123,9 @@ export const colorMap = {
|
|||
// Disabled state for danger buttons
|
||||
actionDangerDisabled: 'error03',
|
||||
|
||||
// Underlay color for buttons
|
||||
underlay01: 'surface13',
|
||||
|
||||
// Bottom sheet background
|
||||
bottomSheet: 'surface00',
|
||||
|
||||
|
@ -130,6 +141,9 @@ export const colorMap = {
|
|||
// Text for bottom sheet items
|
||||
text04: 'surface12',
|
||||
|
||||
// Text for drawer menu displayed name
|
||||
text05: 'surface06',
|
||||
|
||||
// error messages
|
||||
textError: 'error06',
|
||||
|
||||
|
@ -216,7 +230,10 @@ export const colorMap = {
|
|||
warning01: 'warning05',
|
||||
|
||||
// Color for indicating a raised hand
|
||||
warning02: 'warning06'
|
||||
warning02: 'warning06',
|
||||
|
||||
// Color for insecure room
|
||||
warning03: 'warning07'
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import React, { useEffect } from 'react';
|
|||
import { translate } from '../../../base/i18n';
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { screen } from '../../../conference/components/native/routes';
|
||||
import { closeChat, openChat } from '../../actions.native';
|
||||
import AbstractChat, {
|
||||
_mapStateToProps,
|
||||
|
@ -71,7 +70,8 @@ export default translate(connect(_mapStateToProps)(props => {
|
|||
_nbUnreadMessages,
|
||||
dispatch,
|
||||
navigation,
|
||||
route
|
||||
route,
|
||||
t
|
||||
} = props;
|
||||
const isChatScreenFocused = useIsFocused();
|
||||
const privateMessageRecipient = route.params?.privateMessageRecipient;
|
||||
|
@ -84,7 +84,7 @@ export default translate(connect(_mapStateToProps)(props => {
|
|||
dispatch(openChat(privateMessageRecipient));
|
||||
|
||||
navigation.setOptions({
|
||||
tabBarLabel: `${screen.conference.chatandpolls.tab.chat} ${nrUnreadMessages}`
|
||||
tabBarLabel: `${t('chat.tabs.chat')} ${nrUnreadMessages}`
|
||||
});
|
||||
|
||||
return () => dispatch(closeChat());
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
|
@ -23,6 +24,7 @@ import {
|
|||
conferenceScreenOptions,
|
||||
inviteScreenOptions,
|
||||
lobbyScreenOptions,
|
||||
navigationContainerTheme,
|
||||
participantsScreenOptions,
|
||||
sharedDocumentScreenOptions
|
||||
} from './ConferenceNavigatorScreenOptions';
|
||||
|
@ -30,6 +32,7 @@ import { screen } from './routes';
|
|||
|
||||
const ConferenceStack = createStackNavigator();
|
||||
|
||||
|
||||
const ConferenceNavigationContainer = () => {
|
||||
const isPollsDisabled = useSelector(getDisablePolls);
|
||||
const ChatScreen
|
||||
|
@ -40,56 +43,52 @@ const ConferenceNavigationContainer = () => {
|
|||
= isPollsDisabled
|
||||
? screen.conference.chat
|
||||
: screen.conference.chatandpolls.main;
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<NavigationContainer
|
||||
independent = { true }
|
||||
ref = { conferenceNavigationRef }
|
||||
theme = {{
|
||||
colors: {
|
||||
background: '#fff'
|
||||
}
|
||||
}}>
|
||||
theme = { navigationContainerTheme }>
|
||||
<ConferenceStack.Navigator
|
||||
initialRouteName = { screen.conference.main }
|
||||
mode = 'modal'>
|
||||
<ConferenceStack.Screen
|
||||
component = { Conference }
|
||||
name = { screen.conference.main }
|
||||
options = {{
|
||||
...conferenceScreenOptions
|
||||
}} />
|
||||
options = { conferenceScreenOptions } />
|
||||
<ConferenceStack.Screen
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
component = { ChatScreen }
|
||||
name = { chatScreenName }
|
||||
options = {{
|
||||
...chatScreenOptions
|
||||
...chatScreenOptions,
|
||||
title: t('chat.title')
|
||||
}} />
|
||||
<ConferenceStack.Screen
|
||||
component = { ParticipantsPane }
|
||||
name = { screen.conference.participants }
|
||||
options = {{
|
||||
...participantsScreenOptions
|
||||
...participantsScreenOptions,
|
||||
title: t('participantsPane.header')
|
||||
}} />
|
||||
<ConferenceStack.Screen
|
||||
component = { LobbyScreen }
|
||||
name = { screen.lobby }
|
||||
options = {{
|
||||
...lobbyScreenOptions
|
||||
}} />
|
||||
options = { lobbyScreenOptions } />
|
||||
<ConferenceStack.Screen
|
||||
component = { AddPeopleDialog }
|
||||
name = { screen.conference.invite }
|
||||
options = {{
|
||||
...inviteScreenOptions
|
||||
...inviteScreenOptions,
|
||||
title: t('addPeople.add')
|
||||
}} />
|
||||
<ConferenceStack.Screen
|
||||
component = { SharedDocument }
|
||||
name = { screen.conference.sharedDocument }
|
||||
options = {{
|
||||
...sharedDocumentScreenOptions
|
||||
...sharedDocumentScreenOptions,
|
||||
title: t('documentSharing.title')
|
||||
}} />
|
||||
</ConferenceStack.Navigator>
|
||||
</NavigationContainer>
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
// @flow
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
import { TransitionPresets } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import { IconClose } from '../../../base/icons';
|
||||
import {
|
||||
Icon,
|
||||
IconClose,
|
||||
IconHelp,
|
||||
IconHome,
|
||||
IconInfo,
|
||||
IconSettings
|
||||
} from '../../../base/icons';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme';
|
||||
|
||||
import { goBack } from './ConferenceNavigationContainerRef';
|
||||
import HeaderNavigationButton from './HeaderNavigationButton';
|
||||
|
||||
/**
|
||||
* Navigation container theme.
|
||||
*/
|
||||
export const navigationContainerTheme = {
|
||||
colors: {
|
||||
background: BaseTheme.palette.ui12
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default modal transition for the current platform.
|
||||
|
@ -20,24 +38,126 @@ export const conferenceModalPresentation = Platform.select({
|
|||
/**
|
||||
* Screen options and transition types.
|
||||
*/
|
||||
export const screenOptions = {
|
||||
export const fullScreenOptions = {
|
||||
...TransitionPresets.ModalTransition,
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dial-IN Info screen options and transition types.
|
||||
*/
|
||||
export const dialInSummaryScreenOptions = {
|
||||
...TransitionPresets.ModalTransition,
|
||||
gestureEnabled: true,
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: BaseTheme.palette.screen01Header
|
||||
},
|
||||
headerTitleStyle: {
|
||||
color: BaseTheme.palette.text01
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drawer navigator screens options and transition types.
|
||||
*/
|
||||
export const drawerNavigatorScreenOptions = {
|
||||
...TransitionPresets.ModalTransition,
|
||||
gestureEnabled: true,
|
||||
headerShown: false
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Drawer screen options and transition types.
|
||||
*/
|
||||
export const drawerScreenOptions = {
|
||||
...TransitionPresets.ModalTransition,
|
||||
gestureEnabled: true,
|
||||
headerShown: true,
|
||||
headerStyle: {
|
||||
backgroundColor: BaseTheme.palette.screen01Header
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for welcome page.
|
||||
*/
|
||||
export const welcomeScreenOptions = {
|
||||
...drawerScreenOptions,
|
||||
drawerIcon: ({ focused }) => (
|
||||
<Icon
|
||||
color = { focused ? BaseTheme.palette.screen01Header : BaseTheme.palette.field01Disabled }
|
||||
size = { 20 }
|
||||
src = { IconHome } />
|
||||
),
|
||||
headerTitleStyle: {
|
||||
color: BaseTheme.palette.screen01Header
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for settings screen.
|
||||
*/
|
||||
export const settingsScreenOptions = {
|
||||
...drawerScreenOptions,
|
||||
drawerIcon: ({ focused }) => (
|
||||
<Icon
|
||||
color = { focused ? BaseTheme.palette.screen01Header : BaseTheme.palette.field01Disabled }
|
||||
size = { 20 }
|
||||
src = { IconSettings } />
|
||||
),
|
||||
headerTitleStyle: {
|
||||
color: BaseTheme.palette.text01
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for terms/privacy screens.
|
||||
*/
|
||||
export const termsAndPrivacyScreenOptions = {
|
||||
...drawerScreenOptions,
|
||||
drawerIcon: ({ focused }) => (
|
||||
<Icon
|
||||
color = { focused ? BaseTheme.palette.screen01Header : BaseTheme.palette.field01Disabled }
|
||||
size = { 20 }
|
||||
src = { IconInfo } />
|
||||
),
|
||||
headerTitleStyle: {
|
||||
color: BaseTheme.palette.text01
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for help screen.
|
||||
*/
|
||||
export const helpScreenOptions = {
|
||||
...drawerScreenOptions,
|
||||
drawerIcon: ({ focused }) => (
|
||||
<Icon
|
||||
color = { focused ? BaseTheme.palette.screen01Header : BaseTheme.palette.field01Disabled }
|
||||
size = { 20 }
|
||||
src = { IconHelp } />
|
||||
),
|
||||
headerTitleStyle: {
|
||||
color: BaseTheme.palette.text01
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for conference.
|
||||
*/
|
||||
export const conferenceScreenOptions = {
|
||||
...screenOptions
|
||||
...fullScreenOptions
|
||||
};
|
||||
|
||||
/**
|
||||
* Screen options for lobby modal.
|
||||
*/
|
||||
export const lobbyScreenOptions = {
|
||||
...screenOptions
|
||||
...fullScreenOptions
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { TouchableWithoutFeedback } from 'react-native';
|
||||
import { TouchableOpacity } from 'react-native-gesture-handler';
|
||||
|
||||
import { Icon } from '../../../base/icons';
|
||||
|
||||
|
@ -22,17 +22,18 @@ type Props = {
|
|||
/**
|
||||
* The component's external style.
|
||||
*/
|
||||
style: Object
|
||||
style?: Object
|
||||
}
|
||||
|
||||
const HeaderNavigationButton = ({ onPress, src, style }: Props) => (
|
||||
<TouchableWithoutFeedback
|
||||
onPress = { onPress } >
|
||||
<TouchableOpacity
|
||||
onPress = { onPress }
|
||||
style = { styles.headerNavigationButton } >
|
||||
<Icon
|
||||
size = { 20 }
|
||||
src = { src }
|
||||
style = { [ styles.headerNavigationButton, style ] } />
|
||||
</TouchableWithoutFeedback>
|
||||
style = { [ styles.headerNavigationIcon, style ] } />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
export const screen = {
|
||||
welcome: {
|
||||
main: 'Home',
|
||||
settings: 'Settings',
|
||||
terms: 'Terms',
|
||||
privacy: 'Privacy',
|
||||
help: 'Help'
|
||||
},
|
||||
dialInSummary: 'Dial-In Info',
|
||||
conference: {
|
||||
main: 'Conference',
|
||||
chat: 'Chat',
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
|
||||
import { BoxModel, ColorPalette, fixAndroidViewClipping } from '../../../base/styles';
|
||||
import { BoxModel, fixAndroidViewClipping } from '../../../base/styles';
|
||||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
|
||||
export const INSECURE_ROOM_NAME_LABEL_COLOR = ColorPalette.warning;
|
||||
export const INSECURE_ROOM_NAME_LABEL_COLOR = BaseTheme.palette.warning03;
|
||||
|
||||
const NAVBAR_BUTTON_SIZE = 24;
|
||||
|
||||
|
@ -15,7 +16,7 @@ export default {
|
|||
*/
|
||||
conference: fixAndroidViewClipping({
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: '#040404',
|
||||
backgroundColor: BaseTheme.palette.uiBackground,
|
||||
flex: 1
|
||||
}),
|
||||
|
||||
|
@ -23,10 +24,16 @@ export default {
|
|||
margin: 10
|
||||
},
|
||||
|
||||
headerNavigationButton: {
|
||||
headerNavigationIcon: {
|
||||
marginLeft: 12
|
||||
},
|
||||
|
||||
headerNavigationButton: {
|
||||
height: BaseTheme.spacing[6],
|
||||
marginTop: BaseTheme.spacing[3],
|
||||
width: BaseTheme.spacing[6]
|
||||
},
|
||||
|
||||
/**
|
||||
* View that contains the indicators.
|
||||
*/
|
||||
|
@ -45,17 +52,17 @@ export default {
|
|||
inviteButton: {
|
||||
iconStyle: {
|
||||
padding: 10,
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.icon01,
|
||||
fontSize: NAVBAR_BUTTON_SIZE
|
||||
},
|
||||
underlayColor: ColorPalette.buttonUnderlay
|
||||
underlayColor: BaseTheme.spacing.underlay01
|
||||
},
|
||||
|
||||
lonelyButton: {
|
||||
alignItems: 'center',
|
||||
borderRadius: 24,
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
height: BaseTheme.spacing[6],
|
||||
justifyContent: 'space-around',
|
||||
paddingHorizontal: 12
|
||||
},
|
||||
|
@ -84,10 +91,10 @@ export default {
|
|||
pipButton: {
|
||||
iconStyle: {
|
||||
padding: 10,
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.icon01,
|
||||
fontSize: NAVBAR_BUTTON_SIZE
|
||||
},
|
||||
underlayColor: ColorPalette.buttonUnderlay
|
||||
underlayColor: BaseTheme.palette.underlay01
|
||||
},
|
||||
|
||||
navBarSafeView: {
|
||||
|
@ -107,7 +114,7 @@ export default {
|
|||
},
|
||||
|
||||
roomTimer: {
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.text01,
|
||||
fontSize: 12,
|
||||
fontWeight: '400',
|
||||
paddingHorizontal: 8
|
||||
|
@ -123,7 +130,7 @@ export default {
|
|||
},
|
||||
|
||||
roomName: {
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.text01,
|
||||
fontSize: 14,
|
||||
fontWeight: '400'
|
||||
},
|
||||
|
|
|
@ -15,7 +15,8 @@ export * from './functions.any';
|
|||
* @returns {boolean}.
|
||||
*/
|
||||
export function getDisablePolls(stateful: Object) {
|
||||
const state = toState(stateful['features/base/config']);
|
||||
const state = toState(stateful)['features/base/config'];
|
||||
|
||||
return state.disablePolls;
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,6 @@ class SharedDocument extends PureComponent<Props> {
|
|||
return (
|
||||
<JitsiScreen
|
||||
addHeaderHeightValue = { true }
|
||||
hasTabNavigator = { false }
|
||||
style = { styles.sharedDocContainer }>
|
||||
<WebView
|
||||
renderLoading = { this._renderLoading }
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import WebView from 'react-native-webview';
|
||||
|
||||
import { JitsiModal } from '../../base/modal';
|
||||
import { connect } from '../../base/redux';
|
||||
import { HELP_VIEW_MODAL_ID } from '../constants';
|
||||
|
||||
const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to display in the Help Centre.
|
||||
*/
|
||||
_url: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a page that renders the help content for the app.
|
||||
*/
|
||||
class HelpView extends PureComponent<Props> {
|
||||
/**
|
||||
* Implements {@code PureComponent#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<JitsiModal
|
||||
headerProps = {{
|
||||
headerLabelKey: 'helpView.header'
|
||||
}}
|
||||
modalId = { HELP_VIEW_MODAL_ID }>
|
||||
<WebView source = {{ uri: this.props._url }} />
|
||||
</JitsiModal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_url: state['features/base/config'].helpCentreURL || DEFAULT_HELP_CENTRE_URL
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(HelpView);
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export { default as HelpView } from './HelpView';
|
|
@ -1,3 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export const HELP_VIEW_MODAL_ID = 'helpView';
|
|
@ -1,4 +0,0 @@
|
|||
// @flow
|
||||
|
||||
export * from './components';
|
||||
export * from './constants';
|
|
@ -210,7 +210,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
|||
return (
|
||||
<JitsiScreen
|
||||
footerComponent = { this._renderShareMeetingButton }
|
||||
hasTabNavigator = { false }
|
||||
style = { styles.addPeopleContainer }>
|
||||
<ClearableInput
|
||||
autoFocus = { false }
|
||||
|
|
|
@ -7,10 +7,11 @@ import { type Dispatch } from 'redux';
|
|||
|
||||
import { openDialog } from '../../../../base/dialog';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { JitsiModal, setActiveModalId } from '../../../../base/modal';
|
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
||||
import { LoadingIndicator } from '../../../../base/react';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { DIAL_IN_SUMMARY_VIEW_ID } from '../../../constants';
|
||||
import { screen } from '../../../../conference/components/native/routes';
|
||||
import { renderArrowBackButton } from '../../../../welcome/functions.native';
|
||||
import { getDialInfoPageURLForURIString } from '../../../functions';
|
||||
|
||||
import DialInSummaryErrorDialog from './DialInSummaryErrorDialog';
|
||||
|
@ -18,12 +19,17 @@ import styles, { INDICATOR_COLOR } from './styles';
|
|||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to display the summary for.
|
||||
*/
|
||||
_summaryUrl: ?string,
|
||||
dispatch: Dispatch<any>,
|
||||
|
||||
dispatch: Dispatch<any>
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
route: Object
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -44,29 +50,46 @@ class DialInSummary extends Component<Props> {
|
|||
this._renderLoading = this._renderLoading.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}. Invoked
|
||||
* immediately after mounting occurs.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
navigation.setOptions({
|
||||
headerLeft: () =>
|
||||
renderArrowBackButton(() =>
|
||||
navigation.navigate(screen.welcome.main))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _summaryUrl } = this.props;
|
||||
const { route } = this.props;
|
||||
const summaryUrl = route.params?.summaryUrl;
|
||||
|
||||
return (
|
||||
<JitsiModal
|
||||
headerProps = {{
|
||||
headerLabelKey: 'info.label'
|
||||
}}
|
||||
modalId = { DIAL_IN_SUMMARY_VIEW_ID }
|
||||
<JitsiScreen
|
||||
style = { styles.backDrop }>
|
||||
<WebView
|
||||
onError = { this._onError }
|
||||
onShouldStartLoadWithRequest = { this._onNavigate }
|
||||
renderLoading = { this._renderLoading }
|
||||
source = {{ uri: getDialInfoPageURLForURIString(_summaryUrl) }}
|
||||
setSupportMultipleWindows = { false }
|
||||
source = {{ uri: getDialInfoPageURLForURIString(summaryUrl) }}
|
||||
startInLoadingState = { true }
|
||||
style = { styles.webView } />
|
||||
</JitsiModal>
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -78,7 +101,6 @@ class DialInSummary extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onError() {
|
||||
this.props.dispatch(setActiveModalId());
|
||||
this.props.dispatch(openDialog(DialInSummaryErrorDialog));
|
||||
}
|
||||
|
||||
|
@ -94,14 +116,14 @@ class DialInSummary extends Component<Props> {
|
|||
*/
|
||||
_onNavigate(request) {
|
||||
const { url } = request;
|
||||
const { route } = this.props;
|
||||
const summaryUrl = route.params?.summaryUrl;
|
||||
|
||||
if (url.startsWith('tel:')) {
|
||||
Linking.openURL(url);
|
||||
|
||||
this.props.dispatch(setActiveModalId());
|
||||
}
|
||||
|
||||
return url === getDialInfoPageURLForURIString(this.props._summaryUrl);
|
||||
return url === getDialInfoPageURLForURIString(summaryUrl);
|
||||
}
|
||||
|
||||
_renderLoading: () => React$Component<any>;
|
||||
|
@ -122,18 +144,4 @@ class DialInSummary extends Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {{
|
||||
* _summaryUrl: ?string
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_summaryUrl: (state['features/base/modal'].modalProps || {}).summaryUrl
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(DialInSummary));
|
||||
export default translate(connect()(DialInSummary));
|
||||
|
|
|
@ -9,7 +9,8 @@ const WV_BACKGROUND = 'rgb(71, 71, 71)';
|
|||
export default {
|
||||
|
||||
backDrop: {
|
||||
backgroundColor: WV_BACKGROUND
|
||||
backgroundColor: WV_BACKGROUND,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
indicatorWrapper: {
|
||||
|
|
|
@ -28,7 +28,6 @@ class LobbyScreen extends AbstractLobbyScreen {
|
|||
|
||||
return (
|
||||
<JitsiScreen
|
||||
hasTabNavigator = { false }
|
||||
style = { styles.contentWrapper }>
|
||||
<SafeAreaView>
|
||||
<Text style = { styles.dialogTitle }>
|
||||
|
|
|
@ -35,7 +35,6 @@ const ParticipantsPane = () => {
|
|||
|
||||
return (
|
||||
<JitsiScreen
|
||||
hasTabNavigator = { false }
|
||||
style = { styles.participantsPane }>
|
||||
<ScrollView bounces = { false }>
|
||||
<LobbyParticipantList />
|
||||
|
|
|
@ -8,7 +8,6 @@ import { useSelector } from 'react-redux';
|
|||
|
||||
import JitsiScreen from '../../../base/modal/components/JitsiScreen';
|
||||
import { BUTTON_MODES } from '../../../chat/constants';
|
||||
import { screen } from '../../../conference/components/native/routes';
|
||||
import { getUnreadPollCount } from '../../functions';
|
||||
import AbstractPollsPane from '../AbstractPollsPane';
|
||||
import type { AbstractProps } from '../AbstractPollsPane';
|
||||
|
@ -31,7 +30,7 @@ const PollsPane = (props: AbstractProps) => {
|
|||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
tabBarLabel: `${screen.conference.chatandpolls.tab.polls} ${nrUnreadPolls}`
|
||||
tabBarLabel: `${t('chat.tabs.polls')} ${nrUnreadPolls}`
|
||||
});
|
||||
}, [ nrUnreadPolls ]);
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconInfo } from '../../base/icons';
|
||||
import { setActiveModalId } from '../../base/modal';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { DIAL_IN_SUMMARY_VIEW_ID } from '../../invite/constants';
|
||||
import { screen } from '../../conference/components/native/routes';
|
||||
import { navigate } from '../../welcome/components/RootNavigationContainerRef';
|
||||
|
||||
export type Props = AbstractButtonProps & {
|
||||
|
||||
|
@ -40,9 +40,11 @@ class ShowDialInInfoButton extends AbstractButton<Props, *> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, itemId } = this.props;
|
||||
const { itemId } = this.props;
|
||||
|
||||
dispatch(setActiveModalId(DIAL_IN_SUMMARY_VIEW_ID, { summaryUrl: itemId.url }));
|
||||
navigate(screen.dialInSummary, {
|
||||
summaryUrl: itemId.url
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from './native';
|
|
@ -13,7 +13,7 @@ import { isRecentListEnabled } from '../../recent-list/functions';
|
|||
/**
|
||||
* {@code AbstractWelcomePage}'s React {@code Component} prop types.
|
||||
*/
|
||||
type Props = {
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Whether the calendar functionality is enabled or not.
|
||||
|
@ -56,7 +56,7 @@ type Props = {
|
|||
*
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractWelcomePage extends Component<Props, *> {
|
||||
export class AbstractWelcomePage<P: Props> extends Component<P, *> {
|
||||
_mounted: ?boolean;
|
||||
|
||||
/**
|
||||
|
@ -64,7 +64,7 @@ export class AbstractWelcomePage extends Component<Props, *> {
|
|||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
static getDerivedStateFromProps(props: Props, state: Object) {
|
||||
static getDerivedStateFromProps(props: P, state: Object) {
|
||||
return {
|
||||
room: props._room || state.room
|
||||
};
|
||||
|
@ -99,7 +99,7 @@ export class AbstractWelcomePage extends Component<Props, *> {
|
|||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the new {@code AbstractWelcomePage} instance with.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
constructor(props: P) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
// @flow
|
||||
|
||||
import { DrawerItemList } from '@react-navigation/drawer';
|
||||
import React from 'react';
|
||||
import { ScrollView, Text, View } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
import { Avatar } from '../../base/avatar';
|
||||
import {
|
||||
getLocalParticipant, getParticipantDisplayName
|
||||
} from '../../base/participants';
|
||||
import { connect } from '../../base/redux';
|
||||
|
||||
import styles, { DRAWER_AVATAR_SIZE } from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Local participant name to be displayed.
|
||||
*/
|
||||
displayName: string,
|
||||
|
||||
/**
|
||||
* The ID of the local participant.
|
||||
*/
|
||||
localParticipantId: string
|
||||
};
|
||||
|
||||
const CustomDrawerContent = (props: Props) => (
|
||||
<ScrollView bounces = { false }>
|
||||
<View style = { styles.drawerHeader }>
|
||||
<Avatar
|
||||
participantId = { props.localParticipantId }
|
||||
size = { DRAWER_AVATAR_SIZE } />
|
||||
<Text style = { styles.displayName }>
|
||||
{ props.displayName }
|
||||
</Text>
|
||||
</View>
|
||||
<SafeAreaView
|
||||
edges = { [
|
||||
'left',
|
||||
'right'
|
||||
] }>
|
||||
<DrawerItemList { ...props } />
|
||||
</SafeAreaView>
|
||||
</ScrollView>
|
||||
);
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state: Object) {
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const localParticipantId = localParticipant?.id;
|
||||
const displayName = localParticipant && getParticipantDisplayName(state, localParticipantId);
|
||||
|
||||
return {
|
||||
displayName,
|
||||
localParticipantId
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(CustomDrawerContent);
|
|
@ -0,0 +1,74 @@
|
|||
// @flow
|
||||
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
||||
import { connect } from '../../base/redux';
|
||||
import {
|
||||
dialInSummaryScreenOptions,
|
||||
drawerNavigatorScreenOptions,
|
||||
navigationContainerTheme
|
||||
} from '../../conference/components/native/ConferenceNavigatorScreenOptions';
|
||||
import { screen } from '../../conference/components/native/routes';
|
||||
import { DialInSummary } from '../../invite';
|
||||
import { isWelcomePageAppEnabled } from '../functions.native';
|
||||
|
||||
import BlankPage from './BlankPage';
|
||||
import { rootNavigationRef } from './RootNavigationContainerRef';
|
||||
import WelcomePageNavigationContainer from './WelcomePageNavigationContainer';
|
||||
|
||||
const RootStack = createStackNavigator();
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Is welcome page available?
|
||||
*/
|
||||
isWelcomePageAvailable: boolean
|
||||
}
|
||||
|
||||
|
||||
const RootNavigationContainer = ({ isWelcomePageAvailable }: Props) => (
|
||||
<SafeAreaProvider>
|
||||
<NavigationContainer
|
||||
independent = { true }
|
||||
ref = { rootNavigationRef }
|
||||
theme = { navigationContainerTheme }>
|
||||
<RootStack.Navigator
|
||||
initialRouteName = { screen.welcome.main }>
|
||||
{
|
||||
isWelcomePageAvailable
|
||||
? <RootStack.Screen
|
||||
component = { WelcomePageNavigationContainer }
|
||||
name = { screen.welcome.main }
|
||||
options = { drawerNavigatorScreenOptions } />
|
||||
: <RootStack.Screen
|
||||
component = { BlankPage }
|
||||
name = { screen.welcome.main } />
|
||||
}
|
||||
<RootStack.Screen
|
||||
component = { DialInSummary }
|
||||
name = { screen.dialInSummary }
|
||||
options = { dialInSummaryScreenOptions } />
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function mapStateToProps(state: Object) {
|
||||
return {
|
||||
isWelcomePageAvailable: isWelcomePageAppEnabled(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(RootNavigationContainer);
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// $FlowExpectedError
|
||||
export const rootNavigationRef = React.createRef();
|
||||
|
||||
/**
|
||||
* User defined navigation action included inside the reference to the container.
|
||||
*
|
||||
* @param {string} name - Destination name of the route that has been defined somewhere.
|
||||
* @param {Object} params - Params to pass to the destination route.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function navigate(name: string, params: Object) {
|
||||
// $FlowExpectedError
|
||||
return rootNavigationRef.current?.navigate(name, params);
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Linking, Text, TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Icon } from '../../base/icons';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The icon of the item.
|
||||
*/
|
||||
icon: Object,
|
||||
|
||||
/**
|
||||
* The i18n label of the item.
|
||||
*/
|
||||
label: string,
|
||||
|
||||
/**
|
||||
* The function to be invoked when the item is pressed
|
||||
* if the item is a button.
|
||||
*/
|
||||
onPress: Function,
|
||||
|
||||
/**
|
||||
* The translate function.
|
||||
*/
|
||||
t: Function,
|
||||
|
||||
/**
|
||||
* The URL of the link, if this item is a link.
|
||||
*/
|
||||
url: string
|
||||
};
|
||||
|
||||
/**
|
||||
* A component rendering an item in the system sidebar.
|
||||
*/
|
||||
class SideBarItem extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Initializes a new {@code SideBarItem} instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onOpenURL = this._onOpenURL.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the sidebar item.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { label, onPress, t } = this.props;
|
||||
const onPressCalculated
|
||||
= typeof onPress === 'function' ? onPress : this._onOpenURL;
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress = { onPressCalculated }
|
||||
style = { styles.sideBarItem }>
|
||||
<View style = { styles.sideBarItemButtonContainer }>
|
||||
<Icon
|
||||
src = { this.props.icon }
|
||||
style = { styles.sideBarItemIcon } />
|
||||
<Text style = { styles.sideBarItemText }>
|
||||
{ t(label) }
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
_onOpenURL: () => void;
|
||||
|
||||
/**
|
||||
* Opens the URL if one is provided.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOpenURL() {
|
||||
const { url } = this.props;
|
||||
|
||||
if (typeof url === 'string') {
|
||||
Linking.openURL(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(SideBarItem);
|
|
@ -1,7 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import { DrawerActions } from '@react-navigation/native';
|
||||
import React from 'react';
|
||||
import {
|
||||
Animated,
|
||||
Keyboard,
|
||||
SafeAreaView,
|
||||
TextInput,
|
||||
TouchableHighlight,
|
||||
|
@ -14,7 +16,8 @@ import { ColorSchemeRegistry } from '../../base/color-scheme';
|
|||
import { translate } from '../../base/i18n';
|
||||
import { Icon, IconMenu, IconWarning } from '../../base/icons';
|
||||
import { MEDIA_TYPE } from '../../base/media';
|
||||
import { Header, LoadingIndicator, Text } from '../../base/react';
|
||||
import JitsiStatusBar from '../../base/modal/components/JitsiStatusBar';
|
||||
import { LoadingIndicator, Text } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { ColorPalette } from '../../base/styles';
|
||||
import {
|
||||
|
@ -22,41 +25,63 @@ import {
|
|||
destroyLocalDesktopTrackIfExists,
|
||||
destroyLocalTracks
|
||||
} from '../../base/tracks';
|
||||
import { HelpView } from '../../help';
|
||||
import { DialInSummary } from '../../invite';
|
||||
import { SettingsView } from '../../settings/components';
|
||||
import { setSideBarVisible } from '../actions';
|
||||
|
||||
import {
|
||||
AbstractWelcomePage,
|
||||
_mapStateToProps as _abstractMapStateToProps
|
||||
_mapStateToProps as _abstractMapStateToProps,
|
||||
type Props as AbstractProps
|
||||
} from './AbstractWelcomePage';
|
||||
import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
|
||||
import VideoSwitch from './VideoSwitch';
|
||||
import WelcomePageLists from './WelcomePageLists';
|
||||
import WelcomePageSideBar from './WelcomePageSideBar';
|
||||
import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';
|
||||
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* The color schemed style of the Header component.
|
||||
*/
|
||||
_headerStyles: Object,
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
route: Object,
|
||||
|
||||
/**
|
||||
* The translate function.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* The native container rendering the welcome page.
|
||||
*
|
||||
* @augments AbstractWelcomePage
|
||||
*/
|
||||
class WelcomePage extends AbstractWelcomePage {
|
||||
class WelcomePage extends AbstractWelcomePage<*> {
|
||||
/**
|
||||
* Constructor of the Component.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// $FlowExpectedError
|
||||
this.state._fieldFocused = false;
|
||||
|
||||
// $FlowExpectedError
|
||||
this.state.hintBoxAnimation = new Animated.Value(0);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onFieldFocusChange = this._onFieldFocusChange.bind(this);
|
||||
this._onShowSideBar = this._onShowSideBar.bind(this);
|
||||
this._renderHintBox = this._renderHintBox.bind(this);
|
||||
|
||||
// Specially bind functions to avoid function definition on render.
|
||||
|
@ -64,6 +89,16 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
this._onFieldFocus = this._onFieldFocusChange.bind(this, true);
|
||||
}
|
||||
|
||||
_onFieldBlur: () => void;
|
||||
|
||||
_onFieldFocus: () => void;
|
||||
|
||||
_onJoin: () => void;
|
||||
|
||||
_onRoomChange: (string) => void;
|
||||
|
||||
_updateRoomname: () => void;
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}. Invoked
|
||||
* immediately after mounting occurs. Creates a local video track if none
|
||||
|
@ -77,7 +112,29 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
|
||||
this._updateRoomname();
|
||||
|
||||
const { dispatch } = this.props;
|
||||
const {
|
||||
_headerStyles,
|
||||
dispatch,
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (
|
||||
<TouchableOpacity
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
onPress = { () =>
|
||||
navigation.dispatch(DrawerActions.openDrawer()) }
|
||||
style = { styles.drawerNavigationIcon }>
|
||||
<Icon
|
||||
size = { 20 }
|
||||
src = { IconMenu }
|
||||
style = { _headerStyles.headerButtonIcon } />
|
||||
</TouchableOpacity>
|
||||
),
|
||||
// eslint-disable-next-line react/no-multi-comp
|
||||
headerRight: () =>
|
||||
<VideoSwitch />
|
||||
});
|
||||
|
||||
if (this.props._settings.startAudioOnly) {
|
||||
dispatch(destroyLocalTracks());
|
||||
|
@ -153,11 +210,14 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
styles.messageContainer,
|
||||
styles.hintContainer,
|
||||
{
|
||||
// $FlowExpectedError
|
||||
opacity: this.state.hintBoxAnimation
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
_onFieldFocusChange: (boolean) => void;
|
||||
|
||||
/**
|
||||
* Callback for when the room field's focus changes so the hint box
|
||||
* must be rendered or removed.
|
||||
|
@ -169,6 +229,7 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
_onFieldFocusChange(focused) {
|
||||
if (focused) {
|
||||
// Stop placeholder animation.
|
||||
// $FlowExpectedError
|
||||
this._clearTimeouts();
|
||||
this.setState({
|
||||
_fieldFocused: true,
|
||||
|
@ -180,29 +241,28 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
}
|
||||
|
||||
Animated.timing(
|
||||
|
||||
// $FlowExpectedError
|
||||
this.state.hintBoxAnimation,
|
||||
|
||||
// $FlowExpectedError
|
||||
{
|
||||
duration: 300,
|
||||
toValue: focused ? 1 : 0
|
||||
})
|
||||
.start(animationState =>
|
||||
|
||||
// $FlowExpectedError
|
||||
animationState.finished
|
||||
&& !focused
|
||||
|
||||
// $FlowExpectedError
|
||||
&& !focused
|
||||
&& this.setState({
|
||||
_fieldFocused: false
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the side bar.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onShowSideBar() {
|
||||
Keyboard.dismiss();
|
||||
this.props.dispatch(setSideBarVisible(true));
|
||||
}
|
||||
_renderHintBox: () => React$Element<any>;
|
||||
|
||||
/**
|
||||
* Renders the hint box if necessary.
|
||||
|
@ -211,9 +271,10 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
* @returns {React$Node}
|
||||
*/
|
||||
_renderHintBox() {
|
||||
if (this.state._fieldFocused) {
|
||||
const { t } = this.props;
|
||||
const { t } = this.props;
|
||||
|
||||
// $FlowExpectedError
|
||||
if (this.state._fieldFocused) {
|
||||
return (
|
||||
<Animated.View style = { this._getHintBoxStyle() }>
|
||||
<View style = { styles.hintTextContainer } >
|
||||
|
@ -283,51 +344,48 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
const { _headerStyles, t } = this.props;
|
||||
|
||||
return (
|
||||
<LocalVideoTrackUnderlay style = { styles.welcomePage }>
|
||||
<View style = { _headerStyles.page }>
|
||||
<Header style = { styles.header }>
|
||||
<TouchableOpacity onPress = { this._onShowSideBar } >
|
||||
<Icon
|
||||
src = { IconMenu }
|
||||
style = { _headerStyles.headerButtonIcon } />
|
||||
</TouchableOpacity>
|
||||
<VideoSwitch />
|
||||
</Header>
|
||||
<SafeAreaView style = { styles.roomContainer } >
|
||||
<View style = { styles.joinControls } >
|
||||
<Text style = { styles.enterRoomText }>
|
||||
{ t('welcomepage.roomname') }
|
||||
</Text>
|
||||
<TextInput
|
||||
accessibilityLabel = { t(roomnameAccLabel) }
|
||||
autoCapitalize = 'none'
|
||||
autoComplete = 'off'
|
||||
autoCorrect = { false }
|
||||
autoFocus = { false }
|
||||
onBlur = { this._onFieldBlur }
|
||||
onChangeText = { this._onRoomChange }
|
||||
onFocus = { this._onFieldFocus }
|
||||
onSubmitEditing = { this._onJoin }
|
||||
placeholder = { this.state.roomPlaceholder }
|
||||
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
|
||||
returnKeyType = { 'go' }
|
||||
spellCheck = { false }
|
||||
style = { styles.textInput }
|
||||
underlineColorAndroid = 'transparent'
|
||||
value = { this.state.room } />
|
||||
{
|
||||
this._renderInsecureRoomNameWarning()
|
||||
}
|
||||
{
|
||||
this._renderHintBox()
|
||||
}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
<WelcomePageLists disabled = { this.state._fieldFocused } />
|
||||
</View>
|
||||
<WelcomePageSideBar />
|
||||
{ this._renderWelcomePageModals() }
|
||||
</LocalVideoTrackUnderlay>
|
||||
<>
|
||||
<JitsiStatusBar />
|
||||
<LocalVideoTrackUnderlay style = { styles.welcomePage }>
|
||||
<View style = { _headerStyles.page }>
|
||||
<SafeAreaView style = { styles.roomContainer } >
|
||||
<View style = { styles.joinControls } >
|
||||
<Text style = { styles.enterRoomText }>
|
||||
{ t('welcomepage.roomname') }
|
||||
</Text>
|
||||
{/* // $FlowExpectedError*/}
|
||||
<TextInput
|
||||
accessibilityLabel = { t(roomnameAccLabel) }
|
||||
autoCapitalize = { 'none' }
|
||||
autoComplete = { 'off' }
|
||||
autoCorrect = { false }
|
||||
autoFocus = { false }
|
||||
onBlur = { this._onFieldBlur }
|
||||
onChangeText = { this._onRoomChange }
|
||||
onFocus = { this._onFieldFocus }
|
||||
onSubmitEditing = { this._onJoin }
|
||||
placeholder = { this.state.roomPlaceholder }
|
||||
placeholderTextColor = { PLACEHOLDER_TEXT_COLOR }
|
||||
returnKeyType = { 'go' }
|
||||
spellCheck = { false }
|
||||
style = { styles.textInput }
|
||||
underlineColorAndroid = 'transparent'
|
||||
value = { this.state.room } />
|
||||
{
|
||||
|
||||
// $FlowExpectedError
|
||||
this._renderInsecureRoomNameWarning()
|
||||
}
|
||||
{
|
||||
this._renderHintBox()
|
||||
}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
{/* $FlowExpectedError*/}
|
||||
<WelcomePageLists disabled = { this.state._fieldFocused } />
|
||||
</View>
|
||||
</LocalVideoTrackUnderlay>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -347,19 +405,6 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders JitsiModals that are supposed to be on the welcome page.
|
||||
*
|
||||
* @returns {Array<ReactElement>}
|
||||
*/
|
||||
_renderWelcomePageModals() {
|
||||
return [
|
||||
<HelpView key = 'helpView' />,
|
||||
<DialInSummary key = 'dialInSummary' />,
|
||||
<SettingsView key = 'settings' />
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
// @flow
|
||||
|
||||
import { createDrawerNavigator } from '@react-navigation/drawer';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
helpScreenOptions,
|
||||
settingsScreenOptions,
|
||||
termsAndPrivacyScreenOptions,
|
||||
welcomeScreenOptions
|
||||
} from '../../conference/components/native/ConferenceNavigatorScreenOptions';
|
||||
import { screen } from '../../conference/components/native/routes';
|
||||
import HelpView from '../components/help/components/HelpView';
|
||||
import PrivacyView from '../components/privacy/components/PrivacyView';
|
||||
import SettingsView from '../components/settings/components/SettingsView';
|
||||
import TermsView from '../components/terms/components/TermsView';
|
||||
|
||||
import CustomDrawerContent from './CustomDrawerContent';
|
||||
import WelcomePage from './WelcomePage.native';
|
||||
import { drawerContentOptions } from './constants';
|
||||
import styles from './styles';
|
||||
|
||||
const DrawerStack = createDrawerNavigator();
|
||||
|
||||
|
||||
const WelcomePageNavigationContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<DrawerStack.Navigator
|
||||
/* eslint-disable-next-line react/jsx-no-bind */
|
||||
drawerContent = { props => <CustomDrawerContent { ...props } /> }
|
||||
drawerContentOptions = { drawerContentOptions }
|
||||
drawerStyle = { styles.drawerStyle }>
|
||||
<DrawerStack.Screen
|
||||
component = { WelcomePage }
|
||||
name = { screen.welcome.main }
|
||||
options = { welcomeScreenOptions } />
|
||||
<DrawerStack.Screen
|
||||
component = { SettingsView }
|
||||
name = { screen.welcome.settings }
|
||||
options = {{
|
||||
...settingsScreenOptions,
|
||||
title: t('settingsView.header')
|
||||
}} />
|
||||
<DrawerStack.Screen
|
||||
component = { TermsView }
|
||||
name = { screen.welcome.terms }
|
||||
options = {{
|
||||
...termsAndPrivacyScreenOptions,
|
||||
title: t('termsView.header')
|
||||
}} />
|
||||
<DrawerStack.Screen
|
||||
component = { PrivacyView }
|
||||
name = { screen.welcome.privacy }
|
||||
options = {{
|
||||
...termsAndPrivacyScreenOptions,
|
||||
title: t('privacyView.header')
|
||||
}} />
|
||||
<DrawerStack.Screen
|
||||
component = { HelpView }
|
||||
name = { screen.welcome.help }
|
||||
options = {{
|
||||
...helpScreenOptions,
|
||||
title: t('helpView.header')
|
||||
}} />
|
||||
</DrawerStack.Navigator>
|
||||
);
|
||||
};
|
||||
|
||||
export default WelcomePageNavigationContainer;
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { SafeAreaView, ScrollView, Text } from 'react-native';
|
||||
|
||||
import { Avatar } from '../../base/avatar';
|
||||
import { IconInfo, IconSettings, IconHelp } from '../../base/icons';
|
||||
import { setActiveModalId } from '../../base/modal';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantDisplayName
|
||||
} from '../../base/participants';
|
||||
import {
|
||||
Header,
|
||||
SlidingView
|
||||
} from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { HELP_VIEW_MODAL_ID } from '../../help';
|
||||
import { SETTINGS_VIEW_ID } from '../../settings/constants';
|
||||
import { setSideBarVisible } from '../actions';
|
||||
|
||||
import SideBarItem from './SideBarItem';
|
||||
import styles, { SIDEBAR_AVATAR_SIZE } from './styles';
|
||||
|
||||
/**
|
||||
* The URL at which the privacy policy is available to the user.
|
||||
*/
|
||||
const PRIVACY_URL = 'https://jitsi.org/meet/privacy';
|
||||
|
||||
/**
|
||||
* The URL at which the terms (of service/use) are available to the user.
|
||||
*/
|
||||
const TERMS_URL = 'https://jitsi.org/meet/terms';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Redux dispatch action.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Display name of the local participant.
|
||||
*/
|
||||
_displayName: ?string,
|
||||
|
||||
/**
|
||||
* ID of the local participant.
|
||||
*/
|
||||
_localParticipantId: ?string,
|
||||
|
||||
/**
|
||||
* Sets the side bar visible or hidden.
|
||||
*/
|
||||
_visible: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* A component rendering a welcome page sidebar.
|
||||
*/
|
||||
class WelcomePageSideBar extends Component<Props> {
|
||||
/**
|
||||
* Constructs a new SideBar instance.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onHideSideBar = this._onHideSideBar.bind(this);
|
||||
this._onOpenHelpPage = this._onOpenHelpPage.bind(this);
|
||||
this._onOpenSettings = this._onOpenSettings.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the sidebar.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<SlidingView
|
||||
onHide = { this._onHideSideBar }
|
||||
show = { this.props._visible }
|
||||
style = { styles.sideBar } >
|
||||
<Header style = { styles.sideBarHeader }>
|
||||
<Avatar
|
||||
participantId = { this.props._localParticipantId }
|
||||
size = { SIDEBAR_AVATAR_SIZE } />
|
||||
<Text style = { styles.displayName }>
|
||||
{ this.props._displayName }
|
||||
</Text>
|
||||
</Header>
|
||||
<SafeAreaView style = { styles.sideBarBody }>
|
||||
<ScrollView
|
||||
style = { styles.itemContainer }>
|
||||
<SideBarItem
|
||||
icon = { IconSettings }
|
||||
label = 'settings.title'
|
||||
onPress = { this._onOpenSettings } />
|
||||
<SideBarItem
|
||||
icon = { IconInfo }
|
||||
label = 'welcomepage.terms'
|
||||
url = { TERMS_URL } />
|
||||
<SideBarItem
|
||||
icon = { IconInfo }
|
||||
label = 'welcomepage.privacy'
|
||||
url = { PRIVACY_URL } />
|
||||
<SideBarItem
|
||||
icon = { IconHelp }
|
||||
label = 'welcomepage.getHelp'
|
||||
onPress = { this._onOpenHelpPage } />
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
</SlidingView>
|
||||
);
|
||||
}
|
||||
|
||||
_onHideSideBar: () => void;
|
||||
|
||||
/**
|
||||
* Invoked when the sidebar has closed itself (e.g. Overlay pressed).
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onHideSideBar() {
|
||||
this.props.dispatch(setSideBarVisible(false));
|
||||
}
|
||||
|
||||
_onOpenHelpPage: () => void;
|
||||
|
||||
/**
|
||||
* Shows the {@link HelpView}.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOpenHelpPage() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(setSideBarVisible(false));
|
||||
dispatch(setActiveModalId(HELP_VIEW_MODAL_ID));
|
||||
}
|
||||
|
||||
_onOpenSettings: () => void;
|
||||
|
||||
/**
|
||||
* Shows the {@link SettingsView}.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onOpenSettings() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(setSideBarVisible(false));
|
||||
dispatch(setActiveModalId(SETTINGS_VIEW_ID));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the React {@code Component} props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
const _localParticipant = getLocalParticipant(state);
|
||||
const _localParticipantId = _localParticipant?.id;
|
||||
const _displayName = _localParticipant && getParticipantDisplayName(state, _localParticipantId);
|
||||
|
||||
return {
|
||||
_displayName,
|
||||
_localParticipantId,
|
||||
_visible: state['features/welcome'].sideBarVisible
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(WelcomePageSideBar);
|
|
@ -0,0 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import BaseTheme from '../../base/ui/components/BaseTheme';
|
||||
|
||||
|
||||
export const drawerContentOptions = {
|
||||
activeBackgroundColor: BaseTheme.palette.ui12,
|
||||
activeTintColor: BaseTheme.palette.screen01Header,
|
||||
labelStyle: {
|
||||
marginLeft: BaseTheme.spacing[2]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import JitsiScreenWebView from '../../../../base/modal/components/JitsiScreenWebView';
|
||||
import JitsiStatusBar from '../../../../base/modal/components/JitsiStatusBar';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { screen } from '../../../../conference/components/native/routes';
|
||||
import { renderArrowBackButton } from '../../../../welcome/functions.native';
|
||||
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
const DEFAULT_HELP_CENTRE_URL = 'https://web-cdn.jitsi.net/faq/meet-faq.html';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to display in the Help Centre.
|
||||
*/
|
||||
_url: string,
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a page that renders the help content for the app.
|
||||
*/
|
||||
class HelpView extends PureComponent<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}. Invoked
|
||||
* immediately after mounting occurs.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
navigation.setOptions({
|
||||
headerLeft: () =>
|
||||
renderArrowBackButton(() =>
|
||||
navigation.jumpTo(screen.welcome.main))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements {@code PureComponent#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<JitsiStatusBar />
|
||||
<JitsiScreenWebView
|
||||
source = { this.props._url }
|
||||
style = { styles.helpViewContainer } />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux state to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_url: state['features/base/config'].helpCentreURL || DEFAULT_HELP_CENTRE_URL
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(HelpView);
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* The styles of the native components of the feature {@code settings}.
|
||||
*/
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
helpViewContainer: {
|
||||
flex: 1
|
||||
}
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
// @flow
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import JitsiScreenWebView from '../../../../base/modal/components/JitsiScreenWebView';
|
||||
import JitsiStatusBar from '../../../../base/modal/components/JitsiStatusBar';
|
||||
import { screen } from '../../../../conference/components/native/routes';
|
||||
import { renderArrowBackButton } from '../../../../welcome/functions.native';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL at which the privacy policy is available to the user.
|
||||
*/
|
||||
const PRIVACY_URL = 'https://jitsi.org/meet/privacy';
|
||||
|
||||
const PrivacyView = ({ navigation }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () =>
|
||||
renderArrowBackButton(() =>
|
||||
navigation.jumpTo(screen.welcome.main))
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<JitsiStatusBar />
|
||||
<JitsiScreenWebView
|
||||
source = { PRIVACY_URL }
|
||||
style = { styles.privacyViewContainer } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PrivacyView;
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* The styles of the native components of the feature {@code privacy}.
|
||||
*/
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
privacyViewContainer: {
|
||||
flex: 1
|
||||
}
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
|
||||
import styles, { ANDROID_UNDERLINE_COLOR, PLACEHOLDER_COLOR } from './styles';
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { List } from 'react-native-paper';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconArrowDown, IconArrowUp } from '../../../base/icons';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { Icon, IconArrowDown, IconArrowUp } from '../../../../base/icons';
|
||||
|
||||
import styles from './styles';
|
||||
|
|
@ -1,19 +1,25 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Alert, NativeModules, ScrollView, Text } from 'react-native';
|
||||
import {
|
||||
Alert,
|
||||
NativeModules,
|
||||
ScrollView,
|
||||
Text
|
||||
} from 'react-native';
|
||||
import { Divider, Switch, TextInput, withTheme } from 'react-native-paper';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiModal } from '../../../base/modal';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { SETTINGS_VIEW_ID } from '../../constants';
|
||||
import { normalizeUserInputURL, isServerURLChangeEnabled } from '../../functions';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import JitsiScreen from '../../../../base/modal/components/JitsiScreen';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { screen } from '../../../../conference/components/native/routes';
|
||||
import {
|
||||
AbstractSettingsView,
|
||||
_mapStateToProps as _abstractMapStateToProps,
|
||||
type Props as AbstractProps
|
||||
} from '../AbstractSettingsView';
|
||||
} from '../../../../settings/components/AbstractSettingsView';
|
||||
import { normalizeUserInputURL, isServerURLChangeEnabled } from '../../../../settings/functions';
|
||||
import { renderArrowBackButton } from '../../../../welcome/functions.native';
|
||||
|
||||
import FormRow from './FormRow';
|
||||
import FormSectionAccordion from './FormSectionAccordion';
|
||||
|
@ -80,6 +86,11 @@ type Props = AbstractProps & {
|
|||
*/
|
||||
_serverURLChangeEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object,
|
||||
|
||||
/**
|
||||
* Theme used for styles.
|
||||
*/
|
||||
|
@ -133,6 +144,25 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
this._showURLAlert = this._showURLAlert.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount()}. Invoked
|
||||
* immediately after mounting occurs.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {void}
|
||||
*/
|
||||
componentDidMount() {
|
||||
const {
|
||||
navigation
|
||||
} = this.props;
|
||||
|
||||
navigation.setOptions({
|
||||
headerLeft: () =>
|
||||
renderArrowBackButton(() =>
|
||||
navigation.jumpTo(screen.welcome.main))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the settings page.
|
||||
*
|
||||
|
@ -153,12 +183,8 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
const { palette } = this.props.theme;
|
||||
|
||||
return (
|
||||
<JitsiModal
|
||||
headerProps = {{
|
||||
headerLabelKey: 'settingsView.header'
|
||||
}}
|
||||
modalId = { SETTINGS_VIEW_ID }
|
||||
onClose = { this._onClose }>
|
||||
<JitsiScreen
|
||||
style = { styles.settingsViewContainer }>
|
||||
<ScrollView>
|
||||
<FormSectionAccordion
|
||||
accordion = { false }
|
||||
|
@ -175,7 +201,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
textContentType = { 'name' } // iOS only
|
||||
theme = {{
|
||||
colors: {
|
||||
primary: palette.action01Active,
|
||||
primary: palette.screen01Header,
|
||||
underlineColor: 'transparent'
|
||||
}
|
||||
}}
|
||||
|
@ -194,7 +220,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
textContentType = { 'emailAddress' } // iOS only
|
||||
theme = {{
|
||||
colors: {
|
||||
primary: palette.action01Active,
|
||||
primary: palette.screen01Header,
|
||||
underlineColor: 'transparent'
|
||||
}
|
||||
}}
|
||||
|
@ -219,7 +245,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
textContentType = { 'URL' } // iOS only
|
||||
theme = {{
|
||||
colors: {
|
||||
primary: palette.action01Active,
|
||||
primary: palette.screen01Header,
|
||||
underlineColor: 'transparent'
|
||||
}
|
||||
}}
|
||||
|
@ -230,7 +256,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
<Switch
|
||||
onValueChange = { this._onStartAudioMutedChange }
|
||||
thumbColor = { THUMB_COLOR }
|
||||
trackColor = {{ true: palette.action01Active }}
|
||||
trackColor = {{ true: palette.screen01Header }}
|
||||
value = { startWithAudioMuted } />
|
||||
</FormRow>
|
||||
<Divider style = { styles.fieldSeparator } />
|
||||
|
@ -238,7 +264,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
<Switch
|
||||
onValueChange = { this._onStartVideoMutedChange }
|
||||
thumbColor = { THUMB_COLOR }
|
||||
trackColor = {{ true: palette.action01Active }}
|
||||
trackColor = {{ true: palette.screen01Header }}
|
||||
value = { startWithVideoMuted } />
|
||||
</FormRow>
|
||||
</FormSectionAccordion>
|
||||
|
@ -262,7 +288,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
<Switch
|
||||
onValueChange = { this._onDisableCallIntegration }
|
||||
thumbColor = { THUMB_COLOR }
|
||||
trackColor = {{ true: palette.action01Active }}
|
||||
trackColor = {{ true: palette.screen01Header }}
|
||||
value = { disableCallIntegration } />
|
||||
</FormRow>
|
||||
<Divider style = { styles.fieldSeparator } />
|
||||
|
@ -271,7 +297,7 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
<Switch
|
||||
onValueChange = { this._onDisableP2P }
|
||||
thumbColor = { THUMB_COLOR }
|
||||
trackColor = {{ true: palette.action01Active }}
|
||||
trackColor = {{ true: palette.screen01Header }}
|
||||
value = { disableP2P } />
|
||||
</FormRow>
|
||||
<Divider style = { styles.fieldSeparator } />
|
||||
|
@ -282,13 +308,13 @@ class SettingsView extends AbstractSettingsView<Props, State> {
|
|||
<Switch
|
||||
onValueChange = { this._onDisableCrashReporting }
|
||||
thumbColor = { THUMB_COLOR }
|
||||
trackColor = {{ true: palette.action01Active }}
|
||||
trackColor = {{ true: palette.screen01Header }}
|
||||
value = { disableCrashReporting } />
|
||||
</FormRow>
|
||||
)}
|
||||
</FormSectionAccordion>
|
||||
</ScrollView>
|
||||
</JitsiModal>
|
||||
</JitsiScreen>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import BaseTheme from '../../../base/ui/components/BaseTheme.native';
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
|
||||
export const ANDROID_UNDERLINE_COLOR = 'transparent';
|
||||
export const PLACEHOLDER_COLOR = BaseTheme.palette.action02Focus;
|
||||
export const THUMB_COLOR = BaseTheme.palette.field02;
|
||||
|
@ -9,6 +9,14 @@ const TEXT_SIZE = 14;
|
|||
* The styles of the native components of the feature {@code settings}.
|
||||
*/
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
settingsViewContainer: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* Standardized style for a field container {@code View}.
|
||||
*/
|
||||
|
@ -80,7 +88,7 @@ export default {
|
|||
},
|
||||
|
||||
formSectionTitleActive: {
|
||||
color: BaseTheme.palette.section01Active
|
||||
color: BaseTheme.palette.screen01Header
|
||||
},
|
||||
|
||||
formSectionTitleInActive: {
|
||||
|
@ -93,7 +101,7 @@ export default {
|
|||
},
|
||||
|
||||
sectionOpen: {
|
||||
color: BaseTheme.palette.section01Active,
|
||||
color: BaseTheme.palette.screen01Header,
|
||||
fontSize: 14
|
||||
},
|
||||
|
|
@ -1,23 +1,25 @@
|
|||
// @flow
|
||||
|
||||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { BoxModel, ColorPalette } from '../../base/styles';
|
||||
import { BoxModel } from '../../base/styles';
|
||||
import BaseTheme from '../../base/ui/components/BaseTheme.native';
|
||||
|
||||
export const PLACEHOLDER_TEXT_COLOR = 'rgba(255, 255, 255, 0.5)';
|
||||
|
||||
export const SIDEBAR_AVATAR_SIZE = 100;
|
||||
export const PLACEHOLDER_TEXT_COLOR = BaseTheme.palette.text01;
|
||||
|
||||
const SIDEBAR_HEADER_HEIGHT = 150;
|
||||
export const DRAWER_AVATAR_SIZE = 104;
|
||||
|
||||
export const SWITCH_THUMB_COLOR = ColorPalette.blueHighlight;
|
||||
const DRAWER_HEADER_HEIGHT = 220;
|
||||
|
||||
export const SWITCH_THUMB_COLOR = BaseTheme.palette.action04;
|
||||
|
||||
export const SWITCH_UNDER_COLOR = 'rgba(0, 0, 0, 0.4)';
|
||||
|
||||
/**
|
||||
* The default color of text on the WelcomePage.
|
||||
*/
|
||||
const TEXT_COLOR = ColorPalette.white;
|
||||
const TEXT_COLOR = BaseTheme.palette.text01;
|
||||
|
||||
/**
|
||||
* The styles of the React {@code Components} of the feature welcome including
|
||||
|
@ -37,7 +39,8 @@ export default {
|
|||
*/
|
||||
audioVideoSwitchContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
flexDirection: 'row',
|
||||
marginRight: BaseTheme.spacing[2]
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -55,8 +58,8 @@ export default {
|
|||
* Join button style.
|
||||
*/
|
||||
button: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
borderColor: ColorPalette.blue,
|
||||
backgroundColor: BaseTheme.palette.screen01Header,
|
||||
borderColor: BaseTheme.palette.screen01Header,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
height: 30,
|
||||
|
@ -69,15 +72,23 @@ export default {
|
|||
*/
|
||||
buttonText: {
|
||||
alignSelf: 'center',
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.text01,
|
||||
fontSize: 14
|
||||
},
|
||||
|
||||
/**
|
||||
* Drawer style.
|
||||
*/
|
||||
drawerStyle: {
|
||||
backgroundColor: BaseTheme.palette.ui12,
|
||||
width: '54%'
|
||||
},
|
||||
|
||||
/**
|
||||
* The style of the display name label in the side bar.
|
||||
*/
|
||||
displayName: {
|
||||
color: ColorPalette.white,
|
||||
color: BaseTheme.palette.text01,
|
||||
fontSize: 16,
|
||||
marginTop: BoxModel.margin,
|
||||
textAlign: 'center'
|
||||
|
@ -89,13 +100,6 @@ export default {
|
|||
marginBottom: BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* The welcome screen header style.
|
||||
*/
|
||||
header: {
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
|
||||
/**
|
||||
* Container for the button on the hint box.
|
||||
*/
|
||||
|
@ -142,8 +146,8 @@ export default {
|
|||
},
|
||||
|
||||
messageContainer: {
|
||||
backgroundColor: ColorPalette.white,
|
||||
borderColor: ColorPalette.white,
|
||||
backgroundColor: BaseTheme.palette.ui12,
|
||||
borderColor: BaseTheme.palette.field02,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
marginVertical: 5,
|
||||
|
@ -174,7 +178,7 @@ export default {
|
|||
*/
|
||||
reducedUIContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: ColorPalette.blue,
|
||||
backgroundColor: BaseTheme.palette.screen01Header,
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
@ -192,64 +196,22 @@ export default {
|
|||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
/**
|
||||
* Container of the side bar.
|
||||
*/
|
||||
sideBar: {
|
||||
width: 250,
|
||||
height: Dimensions.get('window').height
|
||||
},
|
||||
|
||||
/**
|
||||
* The body of the side bar where the items are.
|
||||
*/
|
||||
sideBarBody: {
|
||||
backgroundColor: ColorPalette.white,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
/**
|
||||
* The style of the side bar header.
|
||||
*/
|
||||
sideBarHeader: {
|
||||
drawerHeader: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: BaseTheme.palette.screen01Header,
|
||||
flexDirection: 'column',
|
||||
height: SIDEBAR_HEADER_HEIGHT,
|
||||
justifyContent: 'center',
|
||||
padding: BoxModel.padding
|
||||
height: DRAWER_HEADER_HEIGHT,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
/**
|
||||
* Style of the menu items in the side bar.
|
||||
*/
|
||||
sideBarItem: {
|
||||
padding: 13
|
||||
},
|
||||
|
||||
/**
|
||||
* The View inside the side bar buttons (icon + text).
|
||||
*/
|
||||
sideBarItemButtonContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start'
|
||||
},
|
||||
|
||||
/**
|
||||
* The icon in the side bar item touchables.
|
||||
*/
|
||||
sideBarItemIcon: {
|
||||
color: ColorPalette.blueHighlight,
|
||||
fontSize: 20,
|
||||
marginRight: 15
|
||||
},
|
||||
|
||||
/**
|
||||
* The label of the side bar item touchables.
|
||||
*/
|
||||
sideBarItemText: {
|
||||
color: ColorPalette.black,
|
||||
fontWeight: 'bold'
|
||||
drawerNavigationIcon: {
|
||||
height: BaseTheme.spacing[6],
|
||||
marginLeft: BaseTheme.spacing[1],
|
||||
marginTop: BaseTheme.spacing[1],
|
||||
width: BaseTheme.spacing[6]
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -264,7 +226,7 @@ export default {
|
|||
*/
|
||||
textInput: {
|
||||
backgroundColor: 'transparent',
|
||||
borderColor: ColorPalette.white,
|
||||
borderColor: BaseTheme.palette.field02,
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
color: TEXT_COLOR,
|
||||
|
@ -291,13 +253,13 @@ export default {
|
|||
},
|
||||
|
||||
insecureRoomNameWarningIcon: {
|
||||
color: ColorPalette.warning,
|
||||
color: BaseTheme.palette.warning03,
|
||||
fontSize: 24,
|
||||
marginRight: 10
|
||||
},
|
||||
|
||||
insecureRoomNameWarningText: {
|
||||
color: ColorPalette.warning,
|
||||
color: BaseTheme.palette.warning03,
|
||||
flex: 1
|
||||
},
|
||||
|
||||
|
@ -305,7 +267,7 @@ export default {
|
|||
* The style of the top-level container of {@code WelcomePage}.
|
||||
*/
|
||||
welcomePage: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
backgroundColor: BaseTheme.palette.screen01Header,
|
||||
overflow: 'hidden'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// @flow
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import JitsiScreenWebView from '../../../../base/modal/components/JitsiScreenWebView';
|
||||
import JitsiStatusBar from '../../../../base/modal/components/JitsiStatusBar';
|
||||
import { screen } from '../../../../conference/components/native/routes';
|
||||
import { renderArrowBackButton } from '../../../../welcome/functions.native';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Default prop for navigating between screen components(React Navigation).
|
||||
*/
|
||||
navigation: Object
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL at which the terms (of service/use) are available to the user.
|
||||
*/
|
||||
const TERMS_URL = 'https://jitsi.org/meet/terms';
|
||||
|
||||
const TermsView = ({ navigation }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () =>
|
||||
renderArrowBackButton(() =>
|
||||
navigation.jumpTo(screen.welcome.main))
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<JitsiStatusBar />
|
||||
<JitsiScreenWebView
|
||||
source = { TERMS_URL }
|
||||
style = { styles.termsViewContainer } />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TermsView;
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* The styles of the native components of the feature {@code terms}.
|
||||
*/
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Style for screen container.
|
||||
*/
|
||||
termsViewContainer: {
|
||||
flex: 1
|
||||
}
|
||||
};
|
|
@ -1,35 +1,9 @@
|
|||
// @flow
|
||||
|
||||
import { WELCOME_PAGE_ENABLED, getFeatureFlag } from '../base/flags';
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Determines whether the {@code WelcomePage} is enabled by the app itself
|
||||
* (e.g. Programmatically via the Jitsi Meet SDK for Android and iOS). Not to be
|
||||
* confused with {@link isWelcomePageUserEnabled}.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux state or {@link getState}
|
||||
* function.
|
||||
* @returns {boolean} If the {@code WelcomePage} is enabled by the app, then
|
||||
* {@code true}; otherwise, {@code false}.
|
||||
*/
|
||||
export function isWelcomePageAppEnabled(stateful: Function | Object) {
|
||||
if (navigator.product === 'ReactNative') {
|
||||
// We introduced the welcomePageEnabled prop on App in Jitsi Meet SDK
|
||||
// for Android and iOS. There isn't a strong reason not to introduce it
|
||||
// on Web but there're a few considerations to be taken before I go
|
||||
// there among which:
|
||||
// - Enabling/disabling the Welcome page on Web historically
|
||||
// automatically redirects to a random room and that does not make sense
|
||||
// on mobile (right now).
|
||||
return Boolean(getFeatureFlag(stateful, WELCOME_PAGE_ENABLED));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the {@code WelcomePage} is enabled by the user either
|
||||
* herself or through her deployment config(uration). Not to be confused with
|
||||
|
@ -46,3 +20,5 @@ export function isWelcomePageUserEnabled(stateful: Function | Object) {
|
|||
? true
|
||||
: toState(stateful)['features/base/config'].enableWelcomePage);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { getFeatureFlag, WELCOME_PAGE_ENABLED } from '../base/flags';
|
||||
import { IconArrowBack } from '../base/icons';
|
||||
import HeaderNavigationButton
|
||||
from '../conference/components/native/HeaderNavigationButton';
|
||||
|
||||
/**
|
||||
* Determines whether the {@code WelcomePage} is enabled by the app itself
|
||||
* (e.g. Programmatically via the Jitsi Meet SDK for Android and iOS). Not to be
|
||||
* confused with {@link isWelcomePageUserEnabled}.
|
||||
*
|
||||
* @param {Function|Object} stateful - The redux state or {@link getState}
|
||||
* function.
|
||||
* @returns {boolean} If the {@code WelcomePage} is enabled by the app, then
|
||||
* {@code true}; otherwise, {@code false}.
|
||||
*/
|
||||
export function isWelcomePageAppEnabled(stateful: Function | Object) {
|
||||
return Boolean(getFeatureFlag(stateful, WELCOME_PAGE_ENABLED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render header arrow back button for navigation.
|
||||
*
|
||||
* @param {Function} onPress - Callback for when the button is pressed
|
||||
* function.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
export function renderArrowBackButton(onPress: Function) {
|
||||
return (
|
||||
<HeaderNavigationButton
|
||||
onPress = { onPress }
|
||||
src = { IconArrowBack } />
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue