diff --git a/lang/main.json b/lang/main.json index 9cdf33dc14..6229d5d95d 100644 --- a/lang/main.json +++ b/lang/main.json @@ -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" }, diff --git a/package-lock.json b/package-lock.json index 5c3096ea06..5f0e648842 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ea60ae592f..bb0beda526 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/react/features/app/getRouteToRender.native.js b/react/features/app/getRouteToRender.native.js index a1bf1c7fcc..9d6c4cc92b 100644 --- a/react/features/app/getRouteToRender.native.js +++ b/react/features/app/getRouteToRender.native.js @@ -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 - }; -} diff --git a/react/features/app/getRouteToRender.web.js b/react/features/app/getRouteToRender.web.js index 76d7ca0af2..0a390db19a 100644 --- a/react/features/app/getRouteToRender.web.js +++ b/react/features/app/getRouteToRender.web.js @@ -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 diff --git a/react/features/base/icons/svg/home.svg b/react/features/base/icons/svg/home.svg new file mode 100644 index 0000000000..d56b771f9d --- /dev/null +++ b/react/features/base/icons/svg/home.svg @@ -0,0 +1,4 @@ + + + + diff --git a/react/features/base/icons/svg/icons8-home.svg b/react/features/base/icons/svg/icons8-home.svg new file mode 100644 index 0000000000..5e45f1ee76 --- /dev/null +++ b/react/features/base/icons/svg/icons8-home.svg @@ -0,0 +1,4 @@ + + + + diff --git a/react/features/base/icons/svg/index.js b/react/features/base/icons/svg/index.js index 6e3ff727cf..68b251abdd 100644 --- a/react/features/base/icons/svg/index.js +++ b/react/features/base/icons/svg/index.js @@ -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'; diff --git a/react/features/base/modal/components/JitsiModal.js b/react/features/base/modal/components/JitsiModal.js deleted file mode 100644 index d945ec8fc3..0000000000 --- a/react/features/base/modal/components/JitsiModal.js +++ /dev/null @@ -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 { - 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 ( - - - - - { children } - - { footerComponent && footerComponent() } - - - ); - } - - _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 { - return { - _headerStyles: ColorSchemeRegistry.get(state, 'Header'), - _show: state['features/base/modal'].activeModalId === ownProps.modalId, - _styles: ColorSchemeRegistry.get(state, 'Modal') - }; -} - -export default connect(_mapStateToProps)(JitsiModal); diff --git a/react/features/base/modal/components/JitsiScreen.js b/react/features/base/modal/components/JitsiScreen.js index 08a6dc4d51..8a53a5ffd6 100644 --- a/react/features/base/modal/components/JitsiScreen.js +++ b/react/features/base/modal/components/JitsiScreen.js @@ -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) => ( ( + + + +); + +export default JitsiScreenWebView; diff --git a/react/features/base/modal/components/JitsiStatusBar.js b/react/features/base/modal/components/JitsiStatusBar.js new file mode 100644 index 0000000000..ee38828f20 --- /dev/null +++ b/react/features/base/modal/components/JitsiStatusBar.js @@ -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 ( + + ); +}; + +/** + * 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); diff --git a/react/features/base/modal/components/functions.native.js b/react/features/base/modal/components/functions.native.js index 602b3db325..965f21823f 100644 --- a/react/features/base/modal/components/functions.native.js +++ b/react/features/base/modal/components/functions.native.js @@ -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; } diff --git a/react/features/base/modal/components/index.native.js b/react/features/base/modal/components/index.native.js deleted file mode 100644 index 524eacbdd1..0000000000 --- a/react/features/base/modal/components/index.native.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export { default as JitsiModal } from './JitsiModal'; diff --git a/react/features/base/modal/components/index.web.js b/react/features/base/modal/components/index.web.js deleted file mode 100644 index 125099d638..0000000000 --- a/react/features/base/modal/components/index.web.js +++ /dev/null @@ -1,6 +0,0 @@ -// @flow - -import { Component } from 'react'; - -export const JitsiModal = Component; - diff --git a/react/features/base/modal/index.js b/react/features/base/modal/index.js index 7fdd75c293..3d915f1aad 100644 --- a/react/features/base/modal/index.js +++ b/react/features/base/modal/index.js @@ -2,4 +2,3 @@ export * from './actions'; export * from './actionTypes'; -export * from './components'; diff --git a/react/features/base/react/components/native/Header.js b/react/features/base/react/components/native/Header.js deleted file mode 100644 index 6e6455228f..0000000000 --- a/react/features/base/react/components/native/Header.js +++ /dev/null @@ -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 { - /** - * Implements React's {@link Component#render()}. - * - * @inheritdoc - */ - render() { - const { _styles } = this.props; - - return ( - - - - - { - this.props.children - } - - - - ); - } - - /** - * 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); diff --git a/react/features/base/react/components/native/HeaderWithNavigation.js b/react/features/base/react/components/native/HeaderWithNavigation.js deleted file mode 100644 index 271277749a..0000000000 --- a/react/features/base/react/components/native/HeaderWithNavigation.js +++ /dev/null @@ -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 { - /** - * Implements {@code Component#render}. - * - * @inheritdoc - */ - render() { - const { hideHeaderWithNavigation, onPressBack, onPressForward } = this.props; - - return ( - !hideHeaderWithNavigation - &&
- { onPressBack && } - - { onPressForward && } -
- ); - } -} - -export default translate(HeaderWithNavigation); diff --git a/react/features/base/react/components/native/index.js b/react/features/base/react/components/native/index.js index 10de6d84ed..3437d26b71 100644 --- a/react/features/base/react/components/native/index.js +++ b/react/features/base/react/components/native/index.js @@ -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'; diff --git a/react/features/base/ui/Tokens.js b/react/features/base/ui/Tokens.js index f25702f5e1..d623a8f11a 100644 --- a/react/features/base/ui/Tokens.js +++ b/react/features/base/ui/Tokens.js @@ -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' }; diff --git a/react/features/chat/components/native/Chat.js b/react/features/chat/components/native/Chat.js index 6a22c9a02d..7acc18cdaf 100644 --- a/react/features/chat/components/native/Chat.js +++ b/react/features/chat/components/native/Chat.js @@ -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()); diff --git a/react/features/conference/components/native/ConferenceNavigationContainer.js b/react/features/conference/components/native/ConferenceNavigationContainer.js index ffb4e10fe6..4116c81790 100644 --- a/react/features/conference/components/native/ConferenceNavigationContainer.js +++ b/react/features/conference/components/native/ConferenceNavigationContainer.js @@ -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 ( + theme = { navigationContainerTheme }> + options = { conferenceScreenOptions } /> + options = { lobbyScreenOptions } /> diff --git a/react/features/conference/components/native/ConferenceNavigatorScreenOptions.js b/react/features/conference/components/native/ConferenceNavigatorScreenOptions.js index ff0a7f0007..ee21f105cf 100644 --- a/react/features/conference/components/native/ConferenceNavigatorScreenOptions.js +++ b/react/features/conference/components/native/ConferenceNavigatorScreenOptions.js @@ -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 }) => ( + + ), + headerTitleStyle: { + color: BaseTheme.palette.screen01Header + } +}; + +/** + * Screen options for settings screen. + */ +export const settingsScreenOptions = { + ...drawerScreenOptions, + drawerIcon: ({ focused }) => ( + + ), + headerTitleStyle: { + color: BaseTheme.palette.text01 + } +}; + +/** + * Screen options for terms/privacy screens. + */ +export const termsAndPrivacyScreenOptions = { + ...drawerScreenOptions, + drawerIcon: ({ focused }) => ( + + ), + headerTitleStyle: { + color: BaseTheme.palette.text01 + } +}; + +/** + * Screen options for help screen. + */ +export const helpScreenOptions = { + ...drawerScreenOptions, + drawerIcon: ({ focused }) => ( + + ), + headerTitleStyle: { + color: BaseTheme.palette.text01 + } +}; + /** * Screen options for conference. */ export const conferenceScreenOptions = { - ...screenOptions + ...fullScreenOptions }; /** * Screen options for lobby modal. */ export const lobbyScreenOptions = { - ...screenOptions + ...fullScreenOptions }; /** diff --git a/react/features/conference/components/native/HeaderNavigationButton.js b/react/features/conference/components/native/HeaderNavigationButton.js index 1c26ba1795..ec9108143e 100644 --- a/react/features/conference/components/native/HeaderNavigationButton.js +++ b/react/features/conference/components/native/HeaderNavigationButton.js @@ -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) => ( - + - + style = { [ styles.headerNavigationIcon, style ] } /> + ); diff --git a/react/features/conference/components/native/routes.js b/react/features/conference/components/native/routes.js index 89bda30355..b4d52cfac4 100644 --- a/react/features/conference/components/native/routes.js +++ b/react/features/conference/components/native/routes.js @@ -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', diff --git a/react/features/conference/components/native/styles.js b/react/features/conference/components/native/styles.js index 46dfd0d0a7..c965c57187 100644 --- a/react/features/conference/components/native/styles.js +++ b/react/features/conference/components/native/styles.js @@ -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' }, diff --git a/react/features/conference/functions.native.js b/react/features/conference/functions.native.js index 5e19c6bd8b..c49aebfc58 100644 --- a/react/features/conference/functions.native.js +++ b/react/features/conference/functions.native.js @@ -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; } + diff --git a/react/features/etherpad/components/native/SharedDocument.js b/react/features/etherpad/components/native/SharedDocument.js index 8a64e23dd6..cc9a76f2da 100644 --- a/react/features/etherpad/components/native/SharedDocument.js +++ b/react/features/etherpad/components/native/SharedDocument.js @@ -89,7 +89,6 @@ class SharedDocument extends PureComponent { return ( { - /** - * Implements {@code PureComponent#render()}. - * - * @inheritdoc - * @returns {ReactElement} - */ - render() { - return ( - - - - ); - } -} - -/** - * 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); diff --git a/react/features/help/components/index.js b/react/features/help/components/index.js deleted file mode 100644 index 22e3c111ec..0000000000 --- a/react/features/help/components/index.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export { default as HelpView } from './HelpView'; diff --git a/react/features/help/constants.js b/react/features/help/constants.js deleted file mode 100644 index 701adae6c3..0000000000 --- a/react/features/help/constants.js +++ /dev/null @@ -1,3 +0,0 @@ -// @flow - -export const HELP_VIEW_MODAL_ID = 'helpView'; diff --git a/react/features/help/index.js b/react/features/help/index.js deleted file mode 100644 index 4c3ff3b68e..0000000000 --- a/react/features/help/index.js +++ /dev/null @@ -1,4 +0,0 @@ -// @flow - -export * from './components'; -export * from './constants'; diff --git a/react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js b/react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js index a05842d5bb..76b89f692d 100644 --- a/react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js +++ b/react/features/invite/components/add-people-dialog/native/AddPeopleDialog.js @@ -210,7 +210,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog { return ( , - dispatch: Dispatch + /** + * 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 { 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 ( - - + ); } @@ -78,7 +101,6 @@ class DialInSummary extends Component { * @returns {void} */ _onError() { - this.props.dispatch(setActiveModalId()); this.props.dispatch(openDialog(DialInSummaryErrorDialog)); } @@ -94,14 +116,14 @@ class DialInSummary extends Component { */ _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; @@ -122,18 +144,4 @@ class DialInSummary extends Component { } } -/** - * 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)); diff --git a/react/features/invite/components/dial-in-summary/native/styles.js b/react/features/invite/components/dial-in-summary/native/styles.js index 9bfcaa56b5..8fcd6aec6f 100644 --- a/react/features/invite/components/dial-in-summary/native/styles.js +++ b/react/features/invite/components/dial-in-summary/native/styles.js @@ -9,7 +9,8 @@ const WV_BACKGROUND = 'rgb(71, 71, 71)'; export default { backDrop: { - backgroundColor: WV_BACKGROUND + backgroundColor: WV_BACKGROUND, + flex: 1 }, indicatorWrapper: { diff --git a/react/features/lobby/components/native/LobbyScreen.js b/react/features/lobby/components/native/LobbyScreen.js index 2aa03dd160..2beeafdeb2 100644 --- a/react/features/lobby/components/native/LobbyScreen.js +++ b/react/features/lobby/components/native/LobbyScreen.js @@ -28,7 +28,6 @@ class LobbyScreen extends AbstractLobbyScreen { return ( diff --git a/react/features/participants-pane/components/native/ParticipantsPane.js b/react/features/participants-pane/components/native/ParticipantsPane.js index 6958988b90..dc8d558d8a 100644 --- a/react/features/participants-pane/components/native/ParticipantsPane.js +++ b/react/features/participants-pane/components/native/ParticipantsPane.js @@ -35,7 +35,6 @@ const ParticipantsPane = () => { return ( diff --git a/react/features/polls/components/native/PollsPane.js b/react/features/polls/components/native/PollsPane.js index 5558799c2d..acfb8a9148 100644 --- a/react/features/polls/components/native/PollsPane.js +++ b/react/features/polls/components/native/PollsPane.js @@ -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 ]); diff --git a/react/features/recent-list/components/ShowDialInInfoButton.native.js b/react/features/recent-list/components/ShowDialInInfoButton.native.js index ebd1fb46cc..40b48b63fa 100644 --- a/react/features/recent-list/components/ShowDialInInfoButton.native.js +++ b/react/features/recent-list/components/ShowDialInInfoButton.native.js @@ -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 { * @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 + }); } } diff --git a/react/features/settings/components/_.native.js b/react/features/settings/components/_.native.js deleted file mode 100644 index 738c4d2b8a..0000000000 --- a/react/features/settings/components/_.native.js +++ /dev/null @@ -1 +0,0 @@ -export * from './native'; diff --git a/react/features/welcome/components/AbstractWelcomePage.js b/react/features/welcome/components/AbstractWelcomePage.js index 48372d7b2c..906a04e902 100644 --- a/react/features/welcome/components/AbstractWelcomePage.js +++ b/react/features/welcome/components/AbstractWelcomePage.js @@ -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 { +export class AbstractWelcomePage extends Component { _mounted: ?boolean; /** @@ -64,7 +64,7 @@ export class AbstractWelcomePage extends Component { * * @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 { * @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. diff --git a/react/features/welcome/components/CustomDrawerContent.js b/react/features/welcome/components/CustomDrawerContent.js new file mode 100644 index 0000000000..7f637b67e5 --- /dev/null +++ b/react/features/welcome/components/CustomDrawerContent.js @@ -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) => ( + + + + + { props.displayName } + + + + + + +); + +/** + * 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); diff --git a/react/features/welcome/components/RootNavigationContainer.js b/react/features/welcome/components/RootNavigationContainer.js new file mode 100644 index 0000000000..22f56442d2 --- /dev/null +++ b/react/features/welcome/components/RootNavigationContainer.js @@ -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) => ( + + + + { + isWelcomePageAvailable + ? + : + } + + + + +); + +/** + * 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); + diff --git a/react/features/welcome/components/RootNavigationContainerRef.js b/react/features/welcome/components/RootNavigationContainerRef.js new file mode 100644 index 0000000000..f2623af8a4 --- /dev/null +++ b/react/features/welcome/components/RootNavigationContainerRef.js @@ -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); +} + diff --git a/react/features/welcome/components/SideBarItem.js b/react/features/welcome/components/SideBarItem.js deleted file mode 100644 index f693aeeb2b..0000000000 --- a/react/features/welcome/components/SideBarItem.js +++ /dev/null @@ -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 { - - /** - * 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 ( - - - - - { t(label) } - - - - ); - } - - _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); diff --git a/react/features/welcome/components/WelcomePage.native.js b/react/features/welcome/components/WelcomePage.native.js index cb2924cceb..887ca7fb36 100644 --- a/react/features/welcome/components/WelcomePage.native.js +++ b/react/features/welcome/components/WelcomePage.native.js @@ -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: () => ( + + navigation.dispatch(DrawerActions.openDrawer()) } + style = { styles.drawerNavigationIcon }> + + + ), + // eslint-disable-next-line react/no-multi-comp + headerRight: () => + + }); 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; /** * 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 ( @@ -283,51 +344,48 @@ class WelcomePage extends AbstractWelcomePage { const { _headerStyles, t } = this.props; return ( - - -
- - - - -
- - - - { t('welcomepage.roomname') } - - - { - this._renderInsecureRoomNameWarning() - } - { - this._renderHintBox() - } - - - -
- - { this._renderWelcomePageModals() } -
+ <> + + + + + + + { t('welcomepage.roomname') } + + {/* // $FlowExpectedError*/} + + { + + // $FlowExpectedError + this._renderInsecureRoomNameWarning() + } + { + this._renderHintBox() + } + + + {/* $FlowExpectedError*/} + + + + ); } @@ -347,19 +405,6 @@ class WelcomePage extends AbstractWelcomePage {
); } - - /** - * Renders JitsiModals that are supposed to be on the welcome page. - * - * @returns {Array} - */ - _renderWelcomePageModals() { - return [ - , - , - - ]; - } } /** diff --git a/react/features/welcome/components/WelcomePageNavigationContainer.js b/react/features/welcome/components/WelcomePageNavigationContainer.js new file mode 100644 index 0000000000..ba8b516f1e --- /dev/null +++ b/react/features/welcome/components/WelcomePageNavigationContainer.js @@ -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 ( + } + drawerContentOptions = { drawerContentOptions } + drawerStyle = { styles.drawerStyle }> + + + + + + + ); +}; + +export default WelcomePageNavigationContainer; + diff --git a/react/features/welcome/components/WelcomePageSideBar.native.js b/react/features/welcome/components/WelcomePageSideBar.native.js deleted file mode 100644 index 26f5ed348b..0000000000 --- a/react/features/welcome/components/WelcomePageSideBar.native.js +++ /dev/null @@ -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 { - /** - * 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 ( - -
- - - { this.props._displayName } - -
- - - - - - - - -
- ); - } - - _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); diff --git a/react/features/welcome/components/constants.js b/react/features/welcome/components/constants.js new file mode 100644 index 0000000000..6b9bf353a2 --- /dev/null +++ b/react/features/welcome/components/constants.js @@ -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] + } +}; diff --git a/react/features/welcome/components/help/components/HelpView.js b/react/features/welcome/components/help/components/HelpView.js new file mode 100644 index 0000000000..4c30116cfe --- /dev/null +++ b/react/features/welcome/components/help/components/HelpView.js @@ -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 { + /** + * 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 ( + <> + + + + ); + } +} + +/** + * 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); diff --git a/react/features/welcome/components/help/components/styles.js b/react/features/welcome/components/help/components/styles.js new file mode 100644 index 0000000000..b5e764d3e6 --- /dev/null +++ b/react/features/welcome/components/help/components/styles.js @@ -0,0 +1,12 @@ +/** + * The styles of the native components of the feature {@code settings}. + */ +export default { + + /** + * Style for screen container. + */ + helpViewContainer: { + flex: 1 + } +}; diff --git a/react/features/welcome/components/privacy/components/PrivacyView.js b/react/features/welcome/components/privacy/components/PrivacyView.js new file mode 100644 index 0000000000..44c300cae7 --- /dev/null +++ b/react/features/welcome/components/privacy/components/PrivacyView.js @@ -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 ( + <> + + + + ); +}; + +export default PrivacyView; diff --git a/react/features/welcome/components/privacy/components/styles.js b/react/features/welcome/components/privacy/components/styles.js new file mode 100644 index 0000000000..87cfee4547 --- /dev/null +++ b/react/features/welcome/components/privacy/components/styles.js @@ -0,0 +1,12 @@ +/** + * The styles of the native components of the feature {@code privacy}. + */ +export default { + + /** + * Style for screen container. + */ + privacyViewContainer: { + flex: 1 + } +}; diff --git a/react/features/settings/components/native/FormRow.js b/react/features/welcome/components/settings/components/FormRow.js similarity index 98% rename from react/features/settings/components/native/FormRow.js rename to react/features/welcome/components/settings/components/FormRow.js index a91c4d5d5f..5a2c89c050 100644 --- a/react/features/settings/components/native/FormRow.js +++ b/react/features/welcome/components/settings/components/FormRow.js @@ -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'; diff --git a/react/features/settings/components/native/FormSectionAccordion.js b/react/features/welcome/components/settings/components/FormSectionAccordion.js similarity index 93% rename from react/features/settings/components/native/FormSectionAccordion.js rename to react/features/welcome/components/settings/components/FormSectionAccordion.js index 7997196c5a..d0b23c5bef 100644 --- a/react/features/settings/components/native/FormSectionAccordion.js +++ b/react/features/welcome/components/settings/components/FormSectionAccordion.js @@ -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'; diff --git a/react/features/settings/components/native/SettingsView.js b/react/features/welcome/components/settings/components/SettingsView.js similarity index 90% rename from react/features/settings/components/native/SettingsView.js rename to react/features/welcome/components/settings/components/SettingsView.js index 2fa7b39531..f6e6619cbb 100644 --- a/react/features/settings/components/native/SettingsView.js +++ b/react/features/welcome/components/settings/components/SettingsView.js @@ -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 { 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 { const { palette } = this.props.theme; return ( - + { textContentType = { 'name' } // iOS only theme = {{ colors: { - primary: palette.action01Active, + primary: palette.screen01Header, underlineColor: 'transparent' } }} @@ -194,7 +220,7 @@ class SettingsView extends AbstractSettingsView { textContentType = { 'emailAddress' } // iOS only theme = {{ colors: { - primary: palette.action01Active, + primary: palette.screen01Header, underlineColor: 'transparent' } }} @@ -219,7 +245,7 @@ class SettingsView extends AbstractSettingsView { textContentType = { 'URL' } // iOS only theme = {{ colors: { - primary: palette.action01Active, + primary: palette.screen01Header, underlineColor: 'transparent' } }} @@ -230,7 +256,7 @@ class SettingsView extends AbstractSettingsView { @@ -238,7 +264,7 @@ class SettingsView extends AbstractSettingsView { @@ -262,7 +288,7 @@ class SettingsView extends AbstractSettingsView { @@ -271,7 +297,7 @@ class SettingsView extends AbstractSettingsView { @@ -282,13 +308,13 @@ class SettingsView extends AbstractSettingsView { )} - +
); } diff --git a/react/features/settings/components/native/index.js b/react/features/welcome/components/settings/components/index.js similarity index 100% rename from react/features/settings/components/native/index.js rename to react/features/welcome/components/settings/components/index.js diff --git a/react/features/settings/components/native/styles.js b/react/features/welcome/components/settings/components/styles.js similarity index 91% rename from react/features/settings/components/native/styles.js rename to react/features/welcome/components/settings/components/styles.js index 3086aa0efe..93591fda67 100644 --- a/react/features/settings/components/native/styles.js +++ b/react/features/welcome/components/settings/components/styles.js @@ -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 }, diff --git a/react/features/welcome/components/styles.js b/react/features/welcome/components/styles.js index 28efc5cd5c..fa47fd65a6 100644 --- a/react/features/welcome/components/styles.js +++ b/react/features/welcome/components/styles.js @@ -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' } }; diff --git a/react/features/welcome/components/terms/components/TermsView.js b/react/features/welcome/components/terms/components/TermsView.js new file mode 100644 index 0000000000..d6eb235192 --- /dev/null +++ b/react/features/welcome/components/terms/components/TermsView.js @@ -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 ( + <> + + + + ); +}; + +export default TermsView; diff --git a/react/features/welcome/components/terms/components/styles.js b/react/features/welcome/components/terms/components/styles.js new file mode 100644 index 0000000000..82faf2411a --- /dev/null +++ b/react/features/welcome/components/terms/components/styles.js @@ -0,0 +1,12 @@ +/** + * The styles of the native components of the feature {@code terms}. + */ +export default { + + /** + * Style for screen container. + */ + termsViewContainer: { + flex: 1 + } +}; diff --git a/react/features/welcome/functions.js b/react/features/welcome/functions.js index be35135a35..b7ae5ef17b 100644 --- a/react/features/welcome/functions.js +++ b/react/features/welcome/functions.js @@ -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); } + + diff --git a/react/features/welcome/functions.native.js b/react/features/welcome/functions.native.js new file mode 100644 index 0000000000..4ae8712867 --- /dev/null +++ b/react/features/welcome/functions.native.js @@ -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 ( + + ); +}