diff --git a/ios/Podfile b/ios/Podfile index 6584c987cd..f8f133caa7 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -43,6 +43,8 @@ target 'JitsiMeet' do :path => '../node_modules/react-native-fast-image' pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake' + pod 'BVLinearGradient', + :path => '../node_modules/react-native-linear-gradient' pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc' pod 'RNGoogleSignin', :path => '../node_modules/react-native-google-signin' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 11165864f5..bcc1050c87 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - boost-for-react-native (1.63.0) + - BVLinearGradient (2.5.3): + - React - Crashlytics (3.12.0): - Fabric (~> 1.9.0) - DoubleConversion (1.1.6) @@ -152,6 +154,7 @@ PODS: - yoga (0.57.8.React) DEPENDENCIES: + - BVLinearGradient (from `../node_modules/react-native-linear-gradient`) - Crashlytics - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - Fabric @@ -202,6 +205,8 @@ SPEC REPOS: - SDWebImage EXTERNAL SOURCES: + BVLinearGradient: + :path: "../node_modules/react-native-linear-gradient" DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" Folly: @@ -231,6 +236,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + BVLinearGradient: b0b70acf63ee888829b7c2ebbf6b50e227396e55 Crashlytics: 07fb167b1694128c1c9a5a5cc319b0e9c3ca0933 DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd Fabric: f988e33c97f08930a413e08123064d2e5f68d655 @@ -262,6 +268,6 @@ SPEC CHECKSUMS: SDWebImage: c5594f1a19c48d526d321e548902b56b479cd508 yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85 -PODFILE CHECKSUM: b5218184626a027e8b1ca12361d46100e2fa2f1f +PODFILE CHECKSUM: 7d1909450626f31f9ea2de80122a66a50af2e1ea COCOAPODS: 1.5.3 diff --git a/react/features/base/conference/functions.js b/react/features/base/conference/functions.js index d7d32487e4..739c5c4fed 100644 --- a/react/features/base/conference/functions.js +++ b/react/features/base/conference/functions.js @@ -145,6 +145,22 @@ export function forEachConference( return true; } +/** + * Returns the display name of the conference. + * + * @param {Function | Object} stateful - Reference that can be resolved to Redux + * state with the {@code toState} function. + * @returns {string} + */ +export function getConferenceName(stateful: Function | Object): string { + const state = toState(stateful); + const { callee } = state['features/base/jwt']; + + return state['features/base/config'].callDisplayName + || (callee && callee.name) + || state['features/base/conference'].room; +} + /** * Returns the current {@code JitsiConference} which is joining or joined and is * not leaving. Please note the contrast with merely reading the diff --git a/react/features/conference/components/native/Conference.js b/react/features/conference/components/native/Conference.js index eb8846efc0..1e49593ce3 100644 --- a/react/features/conference/components/native/Conference.js +++ b/react/features/conference/components/native/Conference.js @@ -29,6 +29,7 @@ import { shouldDisplayTileView } from '../../../video-layout'; import DisplayNameLabel from './DisplayNameLabel'; import Labels from './Labels'; +import NavigationBar from './NavigationBar'; import styles from './styles'; /** @@ -306,6 +307,8 @@ class Conference extends Component { } + + { diff --git a/react/features/conference/components/native/Labels.js b/react/features/conference/components/native/Labels.js index 62af73ef8a..2b39cec8eb 100644 --- a/react/features/conference/components/native/Labels.js +++ b/react/features/conference/components/native/Labels.js @@ -12,7 +12,9 @@ import { import { RecordingExpandedLabel } from '../../../recording'; +import { isToolboxVisible } from '../../../toolbox'; import { TranscribingExpandedLabel } from '../../../transcribing'; +import { shouldDisplayTileView } from '../../../video-layout'; import { VideoQualityExpandedLabel } from '../../../video-quality'; import AbstractLabels, { @@ -37,7 +39,12 @@ type Props = AbstractLabelsProps & { * * @private */ - _reducedUI: boolean + _reducedUI: boolean, + + /** + * True if the labels should be visible, false otherwise. + */ + _visible: boolean }; type State = { @@ -148,6 +155,10 @@ class Labels extends AbstractLabels { * @inheritdoc */ render() { + if (!this.props._visible) { + return null; + } + const wide = !isNarrowAspectRatio(this); const { _filmstripVisible, _reducedUI } = this.props; @@ -344,13 +355,15 @@ class Labels extends AbstractLabels { * @private * @returns {{ * _filmstripVisible: boolean, - * _reducedUI: boolean + * _reducedUI: boolean, + * _visible: boolean * }} */ function _mapStateToProps(state) { return { ..._abstractMapStateToProps(state), - _reducedUI: state['features/base/responsive-ui'].reducedUI + _reducedUI: state['features/base/responsive-ui'].reducedUI, + _visible: !isToolboxVisible(state) && !shouldDisplayTileView(state) }; } diff --git a/react/features/conference/components/native/NavigationBar.js b/react/features/conference/components/native/NavigationBar.js new file mode 100644 index 0000000000..4fd54328c3 --- /dev/null +++ b/react/features/conference/components/native/NavigationBar.js @@ -0,0 +1,96 @@ +// @flow + +import _ from 'lodash'; +import React, { Component } from 'react'; +import { SafeAreaView, Text, View } from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { connect } from 'react-redux'; + +import { getConferenceName } from '../../../base/conference'; +import { PictureInPictureButton } from '../../../mobile/picture-in-picture'; +import { isToolboxVisible } from '../../../toolbox'; + +import styles, { NAVBAR_GRADIENT_COLORS } from './styles'; + +type Props = { + + /** + * Name of the meeting we're currently in. + */ + _meetingName: string, + + /** + * True if the navigation bar should be visible. + */ + _visible: boolean +}; + +/** + * Implements a navigation bar component that is rendered on top of the + * conference screen. + */ +class NavigationBar extends Component { + /** + * Implements {@Component#render}. + * + * @inheritdoc + */ + render() { + if (!this.props._visible) { + return null; + } + + return ( + + + + + + + + + + + + { this.props._meetingName } + + + + + + ); + } + +} + +/** + * Maps part of the Redux store to the props of this component. + * + * @param {Object} state - The Redux state. + * @returns {{ + * _meetingName: string, + * _visible: boolean + * }} + */ +function _mapStateToProps(state) { + return { + _meetingName: _.startCase(getConferenceName(state)), + _visible: isToolboxVisible(state) + }; +} + +export default connect(_mapStateToProps)(NavigationBar); diff --git a/react/features/conference/components/native/styles.js b/react/features/conference/components/native/styles.js index c055c79498..c454e7b7ce 100644 --- a/react/features/conference/components/native/styles.js +++ b/react/features/conference/components/native/styles.js @@ -7,6 +7,8 @@ import { import { FILMSTRIP_SIZE } from '../../../filmstrip'; +export const NAVBAR_GRADIENT_COLORS = [ 'black', '#00000000' ]; + /** * The styles of the feature conference. */ @@ -34,6 +36,14 @@ export default createStyleSheet({ fontSize: 14 }, + gradient: { + flex: 1 + }, + + gradientStretch: { + height: 116 + }, + /** * View that contains the indicators. */ @@ -58,6 +68,54 @@ export default createStyleSheet({ top: 0 }, + navBarButton: { + iconStyle: { + color: ColorPalette.white, + fontSize: 24 + }, + + underlayColor: 'transparent' + }, + + navBarContainer: { + flexDirection: 'column', + left: 0, + position: 'absolute', + right: 0, + top: 0 + }, + + navBarSafeView: { + left: 0, + position: 'absolute', + right: 0, + top: 0 + }, + + navBarWrapper: { + alignItems: 'center', + flex: 1, + flexDirection: 'row', + height: 44, + justifyContent: 'space-between', + paddingHorizontal: 14 + }, + + roomName: { + color: ColorPalette.white, + fontSize: 17, + fontWeight: '400' + }, + + roomNameWrapper: { + flexDirection: 'row', + justifyContent: 'center', + left: 0, + paddingHorizontal: 48, + position: 'absolute', + right: 0 + }, + /** * The style of the {@link View} which expands over the whole * {@link Conference} area and splits it between the {@link Filmstrip} and diff --git a/react/features/mobile/call-integration/middleware.js b/react/features/mobile/call-integration/middleware.js index 72933a1e31..d26ecd50a4 100644 --- a/react/features/mobile/call-integration/middleware.js +++ b/react/features/mobile/call-integration/middleware.js @@ -12,6 +12,7 @@ import { CONFERENCE_WILL_JOIN, CONFERENCE_JOINED, SET_AUDIO_ONLY, + getConferenceName, getCurrentConference } from '../../base/conference'; import { getInviteURL } from '../../base/connection'; @@ -226,12 +227,7 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) { CallIntegration.startCall(conference.callUUID, handle, hasVideo) .then(() => { - const { callee } = state['features/base/jwt']; - const displayName - = state['features/base/config'].callDisplayName - || (callee && callee.name) - || state['features/base/conference'].room; - + const displayName = getConferenceName(state); const muted = isLocalTrackMuted( state['features/base/tracks'], diff --git a/react/features/toolbox/components/native/OverflowMenu.js b/react/features/toolbox/components/native/OverflowMenu.js index c7c9a686eb..3b4e809abb 100644 --- a/react/features/toolbox/components/native/OverflowMenu.js +++ b/react/features/toolbox/components/native/OverflowMenu.js @@ -11,7 +11,6 @@ import { } from '../../../base/dialog'; import { InviteButton } from '../../../invite'; import { AudioRouteButton } from '../../../mobile/audio-mode'; -import { PictureInPictureButton } from '../../../mobile/picture-in-picture'; import { LiveStreamButton, RecordButton } from '../../../recording'; import { RoomLockButton } from '../../../room-lock'; import { ClosedCaptionButton } from '../../../subtitles'; @@ -90,7 +89,6 @@ class OverflowMenu extends Component { - ); }