diff --git a/package-lock.json b/package-lock.json index 7a83258ad..1a2557189 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/bottom-tabs": "5.11.15", "@react-navigation/drawer": "5.12.9", "@react-navigation/material-top-tabs": "5.3.19", "@react-navigation/native": "5.9.8", @@ -4206,6 +4207,22 @@ "react-native": ">=0.57" } }, + "node_modules/@react-navigation/bottom-tabs": { + "version": "5.11.15", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.15.tgz", + "integrity": "sha512-TBY419W6aN/HZg98xbVp5Bx1HEF5sXuHR5f55W6KMI4k2AvxlwelKD1wbfvEcX2iuQT0YUiiXsACRFUSECYhkw==", + "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-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/core": { "version": "5.16.1", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz", @@ -23360,6 +23377,15 @@ "resolved": "https://registry.npmjs.org/@react-native-masked-view/masked-view/-/masked-view-0.2.6.tgz", "integrity": "sha512-303CxmetUmgiX9NSUxatZkNh9qTYYdiM8xkGf9I3Uj20U3eGY3M78ljeNQ4UVCJA+FNGS5nC1dtS9GjIqvB4dg==" }, + "@react-navigation/bottom-tabs": { + "version": "5.11.15", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-5.11.15.tgz", + "integrity": "sha512-TBY419W6aN/HZg98xbVp5Bx1HEF5sXuHR5f55W6KMI4k2AvxlwelKD1wbfvEcX2iuQT0YUiiXsACRFUSECYhkw==", + "requires": { + "color": "^3.1.3", + "react-native-iphone-x-helper": "^1.3.0" + } + }, "@react-navigation/core": { "version": "5.16.1", "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.16.1.tgz", diff --git a/package.json b/package.json index d8b347992..e9dffc10e 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/bottom-tabs": "5.11.15", "@react-navigation/drawer": "5.12.9", "@react-navigation/material-top-tabs": "5.3.19", "@react-navigation/native": "5.9.8", diff --git a/react/features/app/reducers.any.js b/react/features/app/reducers.any.js index 427e64a63..e0018c061 100644 --- a/react/features/app/reducers.any.js +++ b/react/features/app/reducers.any.js @@ -54,4 +54,3 @@ import '../toolbox/reducer'; import '../transcribing/reducer'; import '../video-layout/reducer'; import '../videosipgw/reducer'; -import '../welcome/reducer'; diff --git a/react/features/base/react/components/native/styles.js b/react/features/base/react/components/native/styles.js index 5d3e11cc1..9dbc0ff73 100644 --- a/react/features/base/react/components/native/styles.js +++ b/react/features/base/react/components/native/styles.js @@ -117,7 +117,8 @@ const SECTION_LIST_STYLES = { * The top level container style of the list. */ container: { - flex: 1 + flex: 1, + width: '100%' }, list: { diff --git a/react/features/base/ui/Tokens.js b/react/features/base/ui/Tokens.js index 1fc17a518..7869264c1 100644 --- a/react/features/base/ui/Tokens.js +++ b/react/features/base/ui/Tokens.js @@ -42,6 +42,8 @@ export const colors = { warning06: '#ED9E1B', warning07: '#D77976', + disabled01: '#00000040', + support01: '#FF9B42', support02: '#F96E57', support03: '#DF486F', @@ -245,6 +247,12 @@ export const colorMap = { // Color for insecure room warning03: 'warning07', + // Color for disabled tab + tab01Disabled: 'disabled01', + + // Color for disabled video switch + video01Disabled: 'disabled01', + // Backgrounds for avatars support01: 'support01', support02: 'support02', diff --git a/react/features/calendar-sync/components/CalendarList.native.js b/react/features/calendar-sync/components/CalendarList.native.js index 34d54930a..e60cdd71e 100644 --- a/react/features/calendar-sync/components/CalendarList.native.js +++ b/react/features/calendar-sync/components/CalendarList.native.js @@ -75,10 +75,16 @@ class CalendarList extends AbstractPage { return ( CalendarListContent - ? + ? + + : null ); } diff --git a/react/features/calendar-sync/components/styles.js b/react/features/calendar-sync/components/styles.js index 572baaf1e..d57456014 100644 --- a/react/features/calendar-sync/components/styles.js +++ b/react/features/calendar-sync/components/styles.js @@ -1,4 +1,5 @@ import { ColorPalette, createStyleSheet } from '../../base/styles'; +import BaseTheme from '../../base/ui/components/BaseTheme'; const NOTIFICATION_SIZE = 55; @@ -163,5 +164,18 @@ export default createStyleSheet({ */ touchableView: { flexDirection: 'row' + }, + + calendarSync: { + backgroundColor: BaseTheme.palette.uiBackground, + flex: 1, + overflow: 'hidden' + }, + + calendarSyncDisabled: { + backgroundColor: BaseTheme.palette.uiBackground, + flex: 1, + opacity: 0.8, + overflow: 'hidden' } }); diff --git a/react/features/conference/components/native/routes.js b/react/features/conference/components/native/routes.js index b4d52cfac..f27b22d60 100644 --- a/react/features/conference/components/native/routes.js +++ b/react/features/conference/components/native/routes.js @@ -1,6 +1,10 @@ export const screen = { welcome: { main: 'Home', + tabs: { + recent: 'Recent', + calendar: 'Calendar' + }, settings: 'Settings', terms: 'Terms', privacy: 'Privacy', diff --git a/react/features/recent-list/components/RecentList.native.js b/react/features/recent-list/components/RecentList.native.js index 0f3a0ec78..fa4dbff60 100644 --- a/react/features/recent-list/components/RecentList.native.js +++ b/react/features/recent-list/components/RecentList.native.js @@ -1,6 +1,7 @@ // @flow import React from 'react'; +import { View } from 'react-native'; import type { Dispatch } from 'redux'; import { getDefaultURL } from '../../app/functions'; @@ -8,6 +9,7 @@ import { openDialog } from '../../base/dialog/actions'; import { translate } from '../../base/i18n'; import { NavigateSectionList, type Section } from '../../base/react'; import { connect } from '../../base/redux'; +import styles from '../../welcome/components/styles'; import { isRecentListEnabled, toDisplayableList } from '../functions'; import AbstractRecentList from './AbstractRecentList'; @@ -82,13 +84,15 @@ class RecentList extends AbstractRecentList { const recentList = toDisplayableList(_recentList, t, _defaultServerURL); return ( - + + + ); } diff --git a/react/features/welcome/actionTypes.js b/react/features/welcome/actionTypes.js deleted file mode 100644 index e9b99ff51..000000000 --- a/react/features/welcome/actionTypes.js +++ /dev/null @@ -1,24 +0,0 @@ -// @flow - -/** - * The type of the (redux) action which sets the visibility of - * {@link WelcomePageSideBar}. - * - * { - * type: SET_SIDEBAR_VISIBLE, - * visible: boolean - * } - */ -export const SET_SIDEBAR_VISIBLE = 'SET_SIDEBAR_VISIBLE'; - -/** - * The type of (redux) action to set the default page index of - * {@link WelcomePageLists}. - * - * { - * type: SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE, - * pageIndex: number - * } - */ -export const SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE - = 'SET_WELCOME_PAGE_LIST_DEFAULT_PAGE'; diff --git a/react/features/welcome/actions.js b/react/features/welcome/actions.js deleted file mode 100644 index ee2a2db93..000000000 --- a/react/features/welcome/actions.js +++ /dev/null @@ -1,39 +0,0 @@ -// @flow - -import { - SET_SIDEBAR_VISIBLE, - SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE -} from './actionTypes'; - -/** - * Sets the visibility of {@link WelcomePageSideBar}. - * - * @param {boolean} visible - If the {@code WelcomePageSideBar} is to be made - * visible, {@code true}; otherwise, {@code false}. - * @returns {{ - * type: SET_SIDEBAR_VISIBLE, - * visible: boolean - * }} - */ -export function setSideBarVisible(visible: boolean) { - return { - type: SET_SIDEBAR_VISIBLE, - visible - }; -} - -/** - * Sets the default page index of {@link WelcomePageLists}. - * - * @param {number} pageIndex - The index of the default page. - * @returns {{ - * type: SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE, - * pageIndex: number - * }} - */ -export function setWelcomePageListsDefaultPage(pageIndex: number) { - return { - type: SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE, - pageIndex - }; -} diff --git a/react/features/welcome/components/TabIcon.js b/react/features/welcome/components/TabIcon.js new file mode 100644 index 000000000..71ff337f1 --- /dev/null +++ b/react/features/welcome/components/TabIcon.js @@ -0,0 +1,36 @@ +// @flow + +import React from 'react'; + +import { Icon } from '../../base/icons'; +import BaseTheme from '../../base/ui/components/BaseTheme'; +import { INACTIVE_TAB_COLOR } from '../constants'; + +type Props = { + + /** + * Is the tab focused? + */ + focused?: boolean, + + /** + * The ImageSource to be rendered as image. + */ + src: Object, + + /** + * The component's external style. + */ + style?: Object +} + +const TabIcon = ({ focused, src, style }: Props) => ( + +); + + +export default TabIcon; diff --git a/react/features/welcome/components/WelcomePage.native.js b/react/features/welcome/components/WelcomePage.native.js index 887ca7fb3..5e39888cf 100644 --- a/react/features/welcome/components/WelcomePage.native.js +++ b/react/features/welcome/components/WelcomePage.native.js @@ -19,21 +19,20 @@ import { MEDIA_TYPE } from '../../base/media'; import JitsiStatusBar from '../../base/modal/components/JitsiStatusBar'; import { LoadingIndicator, Text } from '../../base/react'; import { connect } from '../../base/redux'; -import { ColorPalette } from '../../base/styles'; import { createDesiredLocalTracks, destroyLocalDesktopTrackIfExists, destroyLocalTracks } from '../../base/tracks'; +import BaseTheme from '../../base/ui/components/BaseTheme.native'; import { AbstractWelcomePage, _mapStateToProps as _abstractMapStateToProps, type Props as AbstractProps } from './AbstractWelcomePage'; -import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay'; import VideoSwitch from './VideoSwitch'; -import WelcomePageLists from './WelcomePageLists'; +import WelcomePageTabs from './WelcomePageTabs'; import styles, { PLACEHOLDER_TEXT_COLOR } from './styles'; @@ -328,7 +327,7 @@ class WelcomePage extends AbstractWelcomePage<*> { { t('welcomepage.accessibilityLabel.join') } onPress = { this._onJoin } style = { styles.button } - underlayColor = { ColorPalette.white }> + underlayColor = { BaseTheme.palette.ui12 }> { children } ); @@ -341,50 +340,48 @@ class WelcomePage extends AbstractWelcomePage<*> { */ _renderFullUI() { const roomnameAccLabel = 'welcomepage.accessibilityLabel.roomname'; - const { _headerStyles, t } = this.props; + const { t } = this.props; return ( <> - - - - - - { t('welcomepage.roomname') } - - {/* // $FlowExpectedError*/} - - { + + + + + { t('welcomepage.roomname') } + + {/* // $FlowExpectedError*/} + + { - // $FlowExpectedError - this._renderInsecureRoomNameWarning() - } - { - this._renderHintBox() - } - - - {/* $FlowExpectedError*/} - - - + // $FlowExpectedError + this._renderInsecureRoomNameWarning() + } + { + this._renderHintBox() + } + + + {/* // $FlowExpectedError*/} + + ); } diff --git a/react/features/welcome/components/WelcomePageLists.js b/react/features/welcome/components/WelcomePageLists.js deleted file mode 100644 index afcbd3ccc..000000000 --- a/react/features/welcome/components/WelcomePageLists.js +++ /dev/null @@ -1,139 +0,0 @@ -// @flow - -import React, { Component } from 'react'; - -import { translate } from '../../base/i18n'; -import { IconEventNote, IconRestore } from '../../base/icons'; -import { PagedList } from '../../base/react'; -import { connect } from '../../base/redux'; -import { CalendarList, isCalendarEnabled } from '../../calendar-sync'; -import { RecentList } from '../../recent-list'; -import { setWelcomePageListsDefaultPage } from '../actions'; - -/** - * The type of the React {@code Component} props of {@link WelcomePageLists}. - */ -type Props = { - - /** - * Whether the calendar functionality is enabled or not. - */ - _calendarEnabled: boolean, - - /** - * The stored default page index. - */ - _defaultPage: number, - - /** - * Renders the lists disabled. - */ - disabled: boolean, - - /** - * The Redux dispatch function. - */ - dispatch: Function, - - /** - * The i18n translate function. - */ - t: Function -}; - -/** - * Implements the lists displayed on the mobile welcome screen. - */ -class WelcomePageLists extends Component { - /** - * Initializes a new {@code WelcomePageLists} instance. - * - * @inheritdoc - */ - constructor(props) { - super(props); - - // Bind event handlers so they are only bound once per instance. - this._onSelectPage = this._onSelectPage.bind(this); - } - - /** - * Implements React's {@link Component#render}. - * - * @inheritdoc - */ - render() { - const { _calendarEnabled, _defaultPage, t } = this.props; - - if (typeof _defaultPage === 'undefined') { - return null; - } - - const pages = [ - { - component: RecentList, - icon: IconRestore, - title: t('welcomepage.recentList') - } - ]; - - if (_calendarEnabled) { - pages.push( - { - component: CalendarList, - icon: IconEventNote, - title: t('welcomepage.calendar') - } - ); - } - - return ( - - ); - } - - _onSelectPage: number => void; - - /** - * Callback for the {@code PagedList} page select action. - * - * @private - * @param {number} pageIndex - The index of the selected page. - * @returns {void} - */ - _onSelectPage(pageIndex) { - this.props.dispatch(setWelcomePageListsDefaultPage(pageIndex)); - } -} - -/** - * Maps (parts of) the redux state to the React {@code Component} props of - * {@code WelcomePageLists}. - * - * @param {Object} state - The redux state. - * @protected - * @returns {{ - * _calendarEnabled: boolean, - * _defaultPage: number - * }} - */ -function _mapStateToProps(state: Object) { - let { defaultPage } = state['features/welcome']; - - if (typeof defaultPage === 'undefined') { - const recentList = state['features/recent-list']; - - defaultPage = recentList && recentList.length ? 0 : 1; - } - - return { - _calendarEnabled: isCalendarEnabled(state), - _defaultPage: defaultPage - }; -} - -export default translate(connect(_mapStateToProps)(WelcomePageLists)); diff --git a/react/features/welcome/components/WelcomePageTabs.js b/react/features/welcome/components/WelcomePageTabs.js new file mode 100644 index 000000000..90fc73ad1 --- /dev/null +++ b/react/features/welcome/components/WelcomePageTabs.js @@ -0,0 +1,60 @@ +// @flow + +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import React, { useCallback } from 'react'; +import { useSelector } from 'react-redux'; + +import { CalendarList, isCalendarEnabled } from '../../calendar-sync'; +import { screen } from '../../conference/components/native/routes'; +import { RecentList } from '../../recent-list'; +import { + calendarListTabBarOptions, + recentListTabBarOptions, + tabBarOptions +} from '../constants'; + +const WelcomePage = createBottomTabNavigator(); + +/** + * The type of the React {@code Component} props of {@link WelcomePageTabs}. + */ +type Props = { + + /** + * Renders the lists disabled. + */ + disabled: boolean +}; + +const WelcomePageTabs = ({ disabled }: Props) => { + const RecentListScreen = useCallback(() => + + ); + + const calendarEnabled = useSelector(isCalendarEnabled); + + const CalendarListScreen = useCallback(() => + + ); + + return ( + + + { RecentListScreen } + + { + calendarEnabled + && + { CalendarListScreen } + + } + + ); +}; + +export default WelcomePageTabs; diff --git a/react/features/welcome/components/styles.js b/react/features/welcome/components/styles.js index fa47fd65a..d032a65ac 100644 --- a/react/features/welcome/components/styles.js +++ b/react/features/welcome/components/styles.js @@ -5,7 +5,6 @@ import { StyleSheet } from 'react-native'; import { BoxModel } from '../../base/styles'; import BaseTheme from '../../base/ui/components/BaseTheme.native'; - export const PLACEHOLDER_TEXT_COLOR = BaseTheme.palette.text01; export const DRAWER_AVATAR_SIZE = 104; @@ -14,7 +13,7 @@ const DRAWER_HEADER_HEIGHT = 220; export const SWITCH_THUMB_COLOR = BaseTheme.palette.action04; -export const SWITCH_UNDER_COLOR = 'rgba(0, 0, 0, 0.4)'; +export const SWITCH_UNDER_COLOR = BaseTheme.palette.video01Disabled; /** * The default color of text on the WelcomePage. @@ -267,7 +266,21 @@ export default { * The style of the top-level container of {@code WelcomePage}. */ welcomePage: { - backgroundColor: BaseTheme.palette.screen01Header, + backgroundColor: BaseTheme.palette.uiBackground, + flex: 1, + overflow: 'hidden' + }, + + recentList: { + backgroundColor: BaseTheme.palette.uiBackground, + flex: 1, + overflow: 'hidden' + }, + + recentListDisabled: { + backgroundColor: BaseTheme.palette.uiBackground, + flex: 1, + opacity: 0.8, overflow: 'hidden' } }; diff --git a/react/features/welcome/constants.js b/react/features/welcome/constants.js new file mode 100644 index 000000000..58598e062 --- /dev/null +++ b/react/features/welcome/constants.js @@ -0,0 +1,39 @@ +// @flow + +import React from 'react'; + +import { IconEventNote, IconRestore } from '../base/icons'; +import BaseTheme from '../base/ui/components/BaseTheme'; + +import TabIcon from './components/TabIcon'; + +export const INACTIVE_TAB_COLOR = BaseTheme.palette.tab01Disabled; + +export const tabBarOptions = { + activeTintColor: BaseTheme.palette.icon01, + inactiveTintColor: INACTIVE_TAB_COLOR, + labelStyle: { + fontSize: 12 + }, + style: { + backgroundColor: BaseTheme.palette.screen01Header + } +}; + +export const recentListTabBarOptions = { + // $FlowExpectedError + tabBarIcon: ({ focused }) => ( + + ) +}; + +export const calendarListTabBarOptions = { + // $FlowExpectedError + tabBarIcon: ({ focused }) => ( + + ) +}; diff --git a/react/features/welcome/reducer.js b/react/features/welcome/reducer.js deleted file mode 100644 index 68e2f9888..000000000 --- a/react/features/welcome/reducer.js +++ /dev/null @@ -1,36 +0,0 @@ -// @flow - -import { PersistenceRegistry, ReducerRegistry, set } from '../base/redux'; - -import { - SET_SIDEBAR_VISIBLE, - SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE -} from './actionTypes'; - -/** - * The name of the redux store/state property which is the root of the redux - * state of the feature {@code welcome}. - */ -const STORE_NAME = 'features/welcome'; - -/** - * Sets up the persistence of the feature {@code welcome}. - */ -PersistenceRegistry.register(STORE_NAME, { - defaultPage: true -}); - -/** - * Reduces redux actions for the purposes of the feature {@code welcome}. - */ -ReducerRegistry.register(STORE_NAME, (state = {}, action) => { - switch (action.type) { - case SET_SIDEBAR_VISIBLE: - return set(state, 'sideBarVisible', action.visible); - - case SET_WELCOME_PAGE_LISTS_DEFAULT_PAGE: - return set(state, 'defaultPage', action.pageIndex); - } - - return state; -});